Skip to content

Commit

Permalink
Allow to connect to any target type
Browse files Browse the repository at this point in the history
Resolves:
#251
Resolves:
#252
Resolves:
#253
  • Loading branch information
joffrey-bion committed May 15, 2023
1 parent dc7db67 commit e457939
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 60 deletions.
48 changes: 32 additions & 16 deletions api/chrome-devtools-kotlin.api
Original file line number Diff line number Diff line change
Expand Up @@ -45049,8 +45049,8 @@ public final class org/hildan/chrome/devtools/domains/webauthn/events/WebAuthnEv
}

public final class org/hildan/chrome/devtools/extensions/CrossDomainExtensionsKt {
public static final fun clickOnElement (Lorg/hildan/chrome/devtools/targets/ChromePageSession;Ljava/lang/String;JLorg/hildan/chrome/devtools/domains/input/MouseButton;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun clickOnElement$default (Lorg/hildan/chrome/devtools/targets/ChromePageSession;Ljava/lang/String;JLorg/hildan/chrome/devtools/domains/input/MouseButton;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun clickOnElement (Lorg/hildan/chrome/devtools/sessions/PageSession;Ljava/lang/String;JLorg/hildan/chrome/devtools/domains/input/MouseButton;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun clickOnElement$default (Lorg/hildan/chrome/devtools/sessions/PageSession;Ljava/lang/String;JLorg/hildan/chrome/devtools/domains/input/MouseButton;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public final class org/hildan/chrome/devtools/protocol/ChromeDPClient {
Expand Down Expand Up @@ -45242,7 +45242,26 @@ public final class org/hildan/chrome/devtools/protocol/TargetCrashedException :
public final fun getSessionId ()Ljava/lang/String;
}

public abstract interface class org/hildan/chrome/devtools/targets/AllDomainsTarget : org/hildan/chrome/devtools/targets/BrowserTarget, org/hildan/chrome/devtools/targets/RenderFrameTarget, org/hildan/chrome/devtools/targets/ServiceWorkerTarget, org/hildan/chrome/devtools/targets/SharedWorkerTarget, org/hildan/chrome/devtools/targets/WorkerTarget {
public abstract interface class org/hildan/chrome/devtools/sessions/PageSession : org/hildan/chrome/devtools/targets/ChildSession, org/hildan/chrome/devtools/targets/PageTarget {
}

public abstract interface class org/hildan/chrome/devtools/sessions/ServiceWorkerSession : org/hildan/chrome/devtools/targets/ChildSession, org/hildan/chrome/devtools/targets/ServiceWorkerTarget {
}

public final class org/hildan/chrome/devtools/sessions/SessionAdaptersKt {
public static final fun asPageSession (Lorg/hildan/chrome/devtools/targets/ChildSession;)Lorg/hildan/chrome/devtools/sessions/PageSession;
public static final fun asServiceWorkerSession (Lorg/hildan/chrome/devtools/targets/ChildSession;)Lorg/hildan/chrome/devtools/sessions/ServiceWorkerSession;
public static final fun asSharedWorkerSession (Lorg/hildan/chrome/devtools/targets/ChildSession;)Lorg/hildan/chrome/devtools/sessions/SharedWorkerSession;
public static final fun asWorkerSession (Lorg/hildan/chrome/devtools/targets/ChildSession;)Lorg/hildan/chrome/devtools/sessions/WorkerSession;
}

public abstract interface class org/hildan/chrome/devtools/sessions/SharedWorkerSession : org/hildan/chrome/devtools/targets/ChildSession, org/hildan/chrome/devtools/targets/SharedWorkerTarget {
}

public abstract interface class org/hildan/chrome/devtools/sessions/WorkerSession : org/hildan/chrome/devtools/targets/ChildSession, org/hildan/chrome/devtools/targets/WorkerTarget {
}

public abstract interface class org/hildan/chrome/devtools/targets/AllDomainsTarget : org/hildan/chrome/devtools/targets/BrowserTarget, org/hildan/chrome/devtools/targets/PageTarget, org/hildan/chrome/devtools/targets/ServiceWorkerTarget, org/hildan/chrome/devtools/targets/SharedWorkerTarget, org/hildan/chrome/devtools/targets/WorkerTarget {
public static final field Companion Lorg/hildan/chrome/devtools/targets/AllDomainsTarget$Companion;
public abstract fun getAutofill ()Lorg/hildan/chrome/devtools/domains/autofill/AutofillDomain;
public abstract fun getCast ()Lorg/hildan/chrome/devtools/domains/cast/CastDomain;
Expand Down Expand Up @@ -45287,13 +45306,10 @@ public final class org/hildan/chrome/devtools/targets/ChildSession$DefaultImpls
}

public abstract interface class org/hildan/chrome/devtools/targets/ChromeBrowserSession : org/hildan/chrome/devtools/targets/BrowserTarget, org/hildan/chrome/devtools/targets/ChromeSession {
public abstract fun attachToPage (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun attachToTarget (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract interface class org/hildan/chrome/devtools/targets/ChromePageSession : org/hildan/chrome/devtools/targets/ChildSession, org/hildan/chrome/devtools/targets/RenderFrameTarget {
}

public abstract interface class org/hildan/chrome/devtools/targets/ChromeSession {
public abstract fun closeWebSocket (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun unsafe ()Lorg/hildan/chrome/devtools/targets/AllDomainsTarget;
Expand All @@ -45305,8 +45321,8 @@ public final class org/hildan/chrome/devtools/targets/NavigationFailed : java/la
public final fun getResponse ()Lorg/hildan/chrome/devtools/domains/page/NavigateResponse;
}

public abstract interface class org/hildan/chrome/devtools/targets/RenderFrameTarget {
public static final field Companion Lorg/hildan/chrome/devtools/targets/RenderFrameTarget$Companion;
public abstract interface class org/hildan/chrome/devtools/targets/PageTarget : org/hildan/chrome/devtools/targets/ChromeDevToolsTarget {
public static final field Companion Lorg/hildan/chrome/devtools/targets/PageTarget$Companion;
public abstract fun getAccessibility ()Lorg/hildan/chrome/devtools/domains/accessibility/AccessibilityDomain;
public abstract fun getAnimation ()Lorg/hildan/chrome/devtools/domains/animation/AnimationDomain;
public abstract fun getAudits ()Lorg/hildan/chrome/devtools/domains/audits/AuditsDomain;
Expand Down Expand Up @@ -45348,7 +45364,7 @@ public abstract interface class org/hildan/chrome/devtools/targets/RenderFrameTa
public abstract fun getWebAuthn ()Lorg/hildan/chrome/devtools/domains/webauthn/WebAuthnDomain;
}

public final class org/hildan/chrome/devtools/targets/RenderFrameTarget$Companion {
public final class org/hildan/chrome/devtools/targets/PageTarget$Companion {
}

public abstract interface class org/hildan/chrome/devtools/targets/ServiceWorkerTarget {
Expand Down Expand Up @@ -45389,13 +45405,13 @@ public final class org/hildan/chrome/devtools/targets/TargetExtensionsKt {
public static final fun attachToNewPageAndAwaitPageLoad (Lorg/hildan/chrome/devtools/targets/ChromeBrowserSession;Ljava/lang/String;ZIIZLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun attachToNewPageAndAwaitPageLoad$default (Lorg/hildan/chrome/devtools/targets/ChromeBrowserSession;Ljava/lang/String;ZIIZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun attachToPage (Lorg/hildan/chrome/devtools/targets/ChromeBrowserSession;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun childPages (Lorg/hildan/chrome/devtools/targets/ChromePageSession;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getTargetInfo (Lorg/hildan/chrome/devtools/targets/ChromePageSession;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun navigateAndAwaitPageLoad (Lorg/hildan/chrome/devtools/targets/ChromePageSession;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun navigateAndAwaitPageLoad (Lorg/hildan/chrome/devtools/targets/ChromePageSession;Lorg/hildan/chrome/devtools/domains/page/NavigateRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun navigateAndAwaitPageLoad$default (Lorg/hildan/chrome/devtools/targets/ChromePageSession;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun childPages (Lorg/hildan/chrome/devtools/sessions/PageSession;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun getTargetInfo (Lorg/hildan/chrome/devtools/sessions/PageSession;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun navigateAndAwaitPageLoad (Lorg/hildan/chrome/devtools/sessions/PageSession;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun navigateAndAwaitPageLoad (Lorg/hildan/chrome/devtools/sessions/PageSession;Lorg/hildan/chrome/devtools/domains/page/NavigateRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun navigateAndAwaitPageLoad$default (Lorg/hildan/chrome/devtools/sessions/PageSession;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun use (Lorg/hildan/chrome/devtools/sessions/PageSession;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun use (Lorg/hildan/chrome/devtools/targets/ChromeBrowserSession;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun use (Lorg/hildan/chrome/devtools/targets/ChromePageSession;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun watchTargetsIn (Lorg/hildan/chrome/devtools/targets/ChromeBrowserSession;Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

Expand Down
8 changes: 7 additions & 1 deletion buildSrc/src/main/kotlin/generator/Generator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class Generator(
}
generateAllDomainsTargetInterfaceFile(allTargets = targets, allDomains = domains)
generateAllDomainsTargetImplFile(allTargets = targets, allDomains = domains)
generateChildSessionsFiles(childTargets = targets.filterNot { it.name == "Browser" })
}

private fun loadProtocolDomains(): List<ChromeDPDomain> {
Expand All @@ -43,7 +44,7 @@ class Generator(
}

private fun generateTargetInterfaceFile(target: TargetType, domains: List<ChromeDPDomain>) {
val targetInterface = ExtDeclarations.targetInterface(target.name)
val targetInterface = ExtDeclarations.targetInterface(target)
FileSpec.builder(targetInterface.packageName, targetInterface.simpleName)
.addAnnotation(Annotations.suppressWarnings)
.addType(createTargetInterface(target, domains))
Expand All @@ -69,6 +70,11 @@ class Generator(
.writeTo(generatedSourcesDir)
}

private fun generateChildSessionsFiles(childTargets: List<TargetType>) {
createSessionInterfacesFileSpec(childTargets).writeTo(generatedSourcesDir)
createSessionAdaptersFileSpec(childTargets).writeTo(generatedSourcesDir)
}

private fun generateDomainFiles(domain: ChromeDPDomain) {
if (domain.types.isNotEmpty()) {
domain.createDomainTypesFileSpec().writeTo(generatedSourcesDir)
Expand Down
80 changes: 80 additions & 0 deletions buildSrc/src/main/kotlin/generator/SessionGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package org.hildan.chrome.devtools.build.generator

import com.squareup.kotlinpoet.*
import org.hildan.chrome.devtools.build.json.TargetType
import org.hildan.chrome.devtools.build.names.ExtDeclarations

fun createSessionInterfacesFileSpec(allTargets: List<TargetType>): FileSpec =
FileSpec.builder(ExtDeclarations.sessionsPackage, ExtDeclarations.sessionsFileName).apply {
allTargets.forEach {
addType(createSessionInterface(it))
}
}.build()

private fun createSessionInterface(target: TargetType): TypeSpec =
TypeSpec.interfaceBuilder(ExtDeclarations.sessionInterface(target)).apply {
addKdoc("A session created when attaching to a target of type ${target.name}.")
addSuperinterface(ExtDeclarations.childSessionInterface)
addSuperinterface(ExtDeclarations.targetInterface(target))
}.build()

fun createSessionAdaptersFileSpec(allTargets: List<TargetType>): FileSpec =
FileSpec.builder(ExtDeclarations.sessionsPackage, ExtDeclarations.sessionAdaptersFileName).apply {
allTargets.forEach {
addFunction(createAdapterExtension(it))
addType(createSessionAdapterClass(it))
}
}.build()

private fun createSessionAdapterClass(target: TargetType): TypeSpec =
TypeSpec.classBuilder(ExtDeclarations.sessionAdapter(target)).apply {
addKdoc(
"An adapter from a generic [%T] to a [%T].",
ExtDeclarations.childSessionInterface,
ExtDeclarations.sessionInterface(target)
)
addModifiers(KModifier.PRIVATE)

val sessionArg = "session"
primaryConstructor(
FunSpec.constructorBuilder()
.addParameter(sessionArg, ExtDeclarations.childSessionInterface)
.build()
)

val targetInterface = ExtDeclarations.targetInterface(target)
addSuperinterface(ExtDeclarations.sessionInterface(target))
addSuperinterface(ExtDeclarations.childSessionInterface, delegate = CodeBlock.of("%N", sessionArg))
addSuperinterface(targetInterface, delegate = CodeBlock.of("%N.unsafe()", sessionArg))

addInitializerBlock(
CodeBlock.of(
"""
val targetType = %N.metaData.targetType
require(targetType in %T.supportedCdpTargets) {
%S
}
""".trimIndent(),
sessionArg,
targetInterface,
CodeBlock.of(
"Cannot initiate a ${target.name} session with a target of type ${'$'}targetType (target ID: ${'$'}{%N.metaData.targetId})",
sessionArg
)
)
)
}.build()

private fun createAdapterExtension(target: TargetType): FunSpec {
val sessionInterface = ExtDeclarations.sessionInterface(target)
return FunSpec.builder("as${sessionInterface.simpleName}").apply {
receiver(ExtDeclarations.childSessionInterface)
addKdoc(
"Adapts this [%T] to a [%T]. If the attached target is not of a compatible type, this function throws an exception.",
ExtDeclarations.childSessionInterface,
sessionInterface,
)
addCode("return %T(this)", ExtDeclarations.sessionAdapter(target))
returns(sessionInterface)
}.build()
}
6 changes: 3 additions & 3 deletions buildSrc/src/main/kotlin/generator/TargetGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import org.hildan.chrome.devtools.build.names.ExtDeclarations
private const val SESSION_ARG = "session"

fun createTargetInterface(target: TargetType, domains: List<ChromeDPDomain>): TypeSpec =
TypeSpec.interfaceBuilder(ExtDeclarations.targetInterface(target.name)).apply {
TypeSpec.interfaceBuilder(ExtDeclarations.targetInterface(target)).apply {
addKdoc("Represents the available domain APIs in ${target.name} targets")
domains.forEach {
addProperty(it.toPropertySpec())
Expand All @@ -31,7 +31,7 @@ fun createAllDomainsTargetInterface(allTargets: List<TargetType>, allDomains: Li
"and runtime errors could occur."
)
allTargets.forEach { target ->
addSuperinterface(ExtDeclarations.targetInterface(target.name))
addSuperinterface(ExtDeclarations.targetInterface(target))
}

// we want to add properties for all domains, including those who are technically not supported by any target
Expand Down Expand Up @@ -67,7 +67,7 @@ fun createAllDomainsTargetImpl(targetTypes: List<TargetType>, domains: List<Chro
addModifiers(KModifier.INTERNAL)
addSuperinterface(ExtDeclarations.allDomainsTargetInterface)
targetTypes.forEach {
addSuperinterface(ExtDeclarations.targetInterface(it.name))
addSuperinterface(ExtDeclarations.targetInterface(it))
}

primaryConstructor(
Expand Down
10 changes: 9 additions & 1 deletion buildSrc/src/main/kotlin/names/ExternalDeclarations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.MemberName
import kotlinx.serialization.Serializable
import org.hildan.chrome.devtools.build.json.*

const val ROOT_PACKAGE_NAME = "org.hildan.chrome.devtools"

object ExtDeclarations {

const val sessionsPackage = "$ROOT_PACKAGE_NAME.sessions"
private const val targetsPackage = "$ROOT_PACKAGE_NAME.targets"
private const val protocolPackage = "$ROOT_PACKAGE_NAME.protocol"

Expand All @@ -21,7 +23,13 @@ object ExtDeclarations {
val allDomainsTargetInterface = ClassName(targetsPackage, "AllDomainsTarget")
val allDomainsTargetImplementation = ClassName(targetsPackage, "UberTarget")

fun targetInterface(targetName: String): ClassName = ClassName(targetsPackage, "${targetName}Target")
val sessionsFileName = "ChildSessions"
val sessionAdaptersFileName = "ChildSessionAdapters"
val childSessionInterface = ClassName(targetsPackage, "ChildSession") // TODO change package

fun targetInterface(target: TargetType): ClassName = ClassName(targetsPackage, "${target.name}Target")
fun sessionInterface(target: TargetType): ClassName = ClassName(sessionsPackage, "${target.name}Session")
fun sessionAdapter(target: TargetType): ClassName = ClassName(sessionsPackage, "${target.name}SessionAdapter")
}

object Annotations {
Expand Down
2 changes: 1 addition & 1 deletion protocol/target_types.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
]
},
{
"name": "RenderFrame",
"name": "Page",
"supportedCdpTargets": [
"page",
"iframe",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import org.hildan.chrome.devtools.domains.dom.getBoxModel
import org.hildan.chrome.devtools.domains.input.MouseButton
import org.hildan.chrome.devtools.domains.input.dispatchMouseClick
import org.hildan.chrome.devtools.domains.utils.center
import org.hildan.chrome.devtools.targets.ChromePageSession
import org.hildan.chrome.devtools.sessions.PageSession

/**
* Finds a DOM element via the given [selector], and simulates a click event on it based on its padding box.
* The current tab doesn't need to be focused.
*
* If this click opens a new tab, that new tab may become focused, but this session still targets the old tab.
*/
suspend fun ChromePageSession.clickOnElement(
suspend fun PageSession.clickOnElement(
selector: CssSelector,
clickDurationMillis: Long = 100,
mouseButton: MouseButton = MouseButton.left
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.hildan.chrome.devtools.targets.*
import org.hildan.chrome.devtools.sessions.*

private val DEFAULT_HTTP_CLIENT by lazy { createHttpClient(overrideHostHeader = false) }

Expand Down Expand Up @@ -137,7 +138,7 @@ class ChromeDPClient(
*
* Note that the caller of this method is responsible for closing the web socket after use by calling
* [ChromeBrowserSession.close], or indirectly by calling `use()` on the browser session.
* Calling [close()][ChromePageSession.close] or `use()` on a derived [ChromePageSession] doesn't close the
* Calling [close()][PageSession.close] or `use()` on a derived [PageSession] doesn't close the
* underlying web socket connection, to avoid undesirable interactions between nested sessions.
*/
suspend fun webSocket(): ChromeBrowserSession {
Expand Down Expand Up @@ -191,7 +192,7 @@ data class ChromeVersion(
*
* When a client wants to interact with a target using CDP, it has to first attach to the target.
* One way to do it is to connect to Chrome via web socket using [ChromeDPClient.webSocket] and then
* using [ChromeBrowserSession.attachToPage] or other attach- methods.
* using [BrowserSession.attachToPage] or other attach- methods.
* The client can then interact with the target using the [ChromePageSession].
*/
@Serializable
Expand Down
20 changes: 20 additions & 0 deletions src/commonMain/kotlin/org/hildan/chrome/devtools/targets/Compat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@

package org.hildan.chrome.devtools.targets

import org.hildan.chrome.devtools.sessions.*

@Deprecated(
message = "Renamed PageSession",
replaceWith = ReplaceWith(
expression = "PageSession",
imports = ["org.hildan.chrome.devtools.sessions.PageSession"],
),
)
typealias ChromePageSession = PageSession

@Deprecated(
message = "Renamed PageTarget",
replaceWith = ReplaceWith(
expression = "PageTarget",
imports = ["org.hildan.chrome.devtools.targets.PageTarget"],
),
)
typealias RenderFrameTarget = PageTarget

@Deprecated(
message = "Renamed ChromeSessionMetaData",
replaceWith = ReplaceWith(
Expand Down
Loading

0 comments on commit e457939

Please sign in to comment.