Skip to content

Commit

Permalink
feat: implement evaluate for dedicated workers (#1625)
Browse files Browse the repository at this point in the history
Implements script evaluation for workers.
  • Loading branch information
jrandolf-2 authored Dec 14, 2023
1 parent 552cece commit ca083df
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 19 deletions.
22 changes: 16 additions & 6 deletions src/bidiMapper/domains/context/BrowsingContextProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,17 @@ export class BrowsingContextProcessor {
this.#acceptInsecureCerts
);

this.#handleWorkerTarget(cdpTarget);
const browsingContext =
parentSessionCdpClient.sessionId &&
this.#browsingContextStorage.findContextBySession(
parentSessionCdpClient.sessionId
);
// If there is no browsing context, this worker is already terminated.
if (!browsingContext) {
break;
}

this.#handleWorkerTarget(cdpTarget, browsingContext.id);
return;
}
}
Expand All @@ -416,14 +426,14 @@ export class BrowsingContextProcessor {
}

#workers = new Map<string, Realm>();
#handleWorkerTarget(cdpTarget: CdpTarget) {
#handleWorkerTarget(cdpTarget: CdpTarget, browsingContextId: string) {
cdpTarget.cdpClient.on('Runtime.executionContextCreated', (params) => {
const {uniqueId, id, origin} = params.context;
const realm = new Realm(
this.#realmStorage,
this.#browsingContextStorage,
uniqueId,
cdpTarget.targetId,
browsingContextId,
id,
serializeOrigin(origin),
'dedicated-worker',
Expand All @@ -439,9 +449,9 @@ export class BrowsingContextProcessor {
#handleDetachedFromTargetEvent(
params: Protocol.Target.DetachedFromTargetEvent
) {
const context = this.#browsingContextStorage
.getAllContexts()
.find((context) => context.cdpTarget.cdpSessionId === params.sessionId);
const context = this.#browsingContextStorage.findContextBySession(
params.sessionId
);
if (context) {
context.dispose();
this.#preloadScriptStorage
Expand Down
9 changes: 9 additions & 0 deletions src/bidiMapper/domains/context/BrowsingContextStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ export class BrowsingContextStorage {
return this.findTopLevelContextId(parentId);
}

findContextBySession(sessionId: string): BrowsingContextImpl | undefined {
for (const context of this.#contexts.values()) {
if (context.cdpTarget.cdpSessionId === sessionId) {
return context;
}
}
return;
}

/** Gets the context with the given ID, if any, otherwise throws. */
getContext(id: BrowsingContext.BrowsingContext): BrowsingContextImpl {
const result = this.findContext(id);
Expand Down
29 changes: 20 additions & 9 deletions src/bidiMapper/domains/script/Realm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,10 @@ export class Realm {

get navigableId(): string {
return (
this.#browsingContextStorage.findContext(this.#browsingContextId)
?.navigableId ?? 'UNKNOWN'
(this.browsingContextId &&
this.#browsingContextStorage.findContext(this.browsingContextId)
?.navigableId) ??
'UNKNOWN'
);
}

Expand All @@ -250,13 +252,22 @@ export class Realm {
}

get realmInfo(): Script.RealmInfo {
return {
realm: this.realmId,
origin: this.origin,
type: this.type,
context: this.browsingContextId,
...(this.sandbox === undefined ? {} : {sandbox: this.sandbox}),
};
switch (this.type) {
case 'window':
return {
realm: this.realmId,
origin: this.origin,
type: this.type,
context: this.browsingContextId,
...(this.sandbox === undefined ? {} : {sandbox: this.sandbox}),
};
default:
return {
realm: this.realmId,
origin: this.origin,
type: this.type,
};
}
}

async evaluate(
Expand Down
10 changes: 10 additions & 0 deletions tests/script/__snapshots__/test_evaluate.ambr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# serializer version: 1
# name: test_scriptEvaluate_dedicated_worker[websocket0]
dict({
'result': dict({
'type': 'string',
'value': 'hello world',
}),
'type': 'success',
})
# ---
64 changes: 63 additions & 1 deletion tests/script/test_evaluate.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

import pytest
from anys import ANY_STR
from test_helpers import execute_command, get_tree
from syrupy.filters import props
from test_helpers import (execute_command, get_tree, send_JSON_command,
subscribe, wait_for_command, wait_for_event)


@pytest.mark.asyncio
Expand Down Expand Up @@ -395,3 +397,63 @@ async def test_scriptEvaluate_realm(websocket, context_id):
"exceptionDetails": ANY,
"realm": realm
} == result


@pytest.mark.asyncio
async def test_scriptEvaluate_dedicated_worker(websocket, context_id, html,
snapshot):
worker_url = 'data:application/javascript,'
url = html(f"<script>window.w = new Worker('{worker_url}');</script>")

await subscribe(websocket, ["script.realmCreated"])

await send_JSON_command(
websocket, {
"method": "browsingContext.navigate",
"params": {
"context": context_id,
"url": url,
"wait": "complete",
}
})

# Wait for worker to be created
while True:
message = await wait_for_event(websocket, "script.realmCreated")
if message["params"] == {
"realm": ANY_STR,
"origin": worker_url,
"type": "dedicated-worker"
}:
realm = message["params"]["realm"]
break

# Set up a listener on the page.
command_id = await send_JSON_command(
websocket, {
"method": "script.evaluate",
"params": {
"target": {
"context": context_id
},
"expression": "new Promise(resolve => window.w.addEventListener('message', ({data}) => resolve(data), {once: true}))",
"awaitPromise": True
}
})

# Post a message from the worker.
await send_JSON_command(
websocket, {
"method": "script.evaluate",
"params": {
"target": {
"realm": realm
},
"expression": "self.postMessage('hello world')",
"awaitPromise": True
}
})

# Check the promise
assert await wait_for_command(
websocket, command_id) == snapshot(exclude=props("realm"))
3 changes: 1 addition & 2 deletions tests/script/test_realm.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ async def test_realm_realmDestroyed_sandbox(websocket, context_id):


@pytest.mark.asyncio
async def test_realm_worker(websocket, context_id, html):
async def test_realm_dedicated_worker(websocket, context_id, html):
worker_url = 'data:application/javascript,while(true){}'
url = html(f"<script>window.w = new Worker('{worker_url}');</script>")

Expand All @@ -175,7 +175,6 @@ async def test_realm_worker(websocket, context_id, html):
if message["params"] == {
"realm": ANY_STR,
"origin": worker_url,
"context": ANY_STR,
"type": "dedicated-worker"
}:
realm = message["params"]["realm"]
Expand Down
6 changes: 5 additions & 1 deletion tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,14 @@ async def execute_command(websocket, command: dict) -> dict:
logger.info(
f"Executing command with method '{command['method']}' and params '{command['params']}'..."
)
return await wait_for_command(websocket, command["id"])


async def wait_for_command(websocket, command_id: int) -> dict:
while True:
# Wait for the command to be finished.
resp = await read_JSON_message(websocket)
if "id" in resp and resp["id"] == command["id"]:
if "id" in resp and resp["id"] == command_id:
if "result" in resp:
return resp["result"]
raise Exception({
Expand Down

0 comments on commit ca083df

Please sign in to comment.