Skip to content

Commit

Permalink
fix: add cdp.resolveRealm command (#1882)
Browse files Browse the repository at this point in the history
`cdp.resolveRealm` resolves a realm ID to an executionContextId.

This is needed for Puppeteer to adopt nodes from ARIA queries.

This is a workaround for https://crbug.com/326258571
  • Loading branch information
jrandolf-2 authored Feb 23, 2024
1 parent d05c54d commit 08d3e45
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 5 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,25 @@ CdpGetSessionResult = {

The command returns the default CDP session for the selected browsing context.

### Command `cdp.resolveRealm`

```cddl
CdpResolveRealmCommand = {
method: "cdp.resolveRealm",
params: ScriptEvaluateParameters,
}
CdpResolveRealmParameters = {
realm: Script.Realm,
}
CdpResolveRealmResult = {
executionContextId: text,
}
```

The command returns resolves a BiDi realm to its CDP execution context ID.

### Events `cdp`

```cddl
Expand Down
3 changes: 3 additions & 0 deletions src/bidiMapper/BidiNoOpParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ export class BidiNoOpParser implements BidiCommandParameterParser {
parseGetSessionParams(params: unknown): Cdp.GetSessionParameters {
return params as Cdp.GetSessionParameters;
}
parseResolveRealmParams(params: unknown): Cdp.ResolveRealmParameters {
return params as Cdp.ResolveRealmParameters;
}
parseSendCommandParams(params: unknown): Cdp.SendCommandParameters {
return params as Cdp.SendCommandParameters;
}
Expand Down
1 change: 1 addition & 0 deletions src/bidiMapper/BidiParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface BidiCommandParameterParser {
// CDP domain
// keep-sorted start block=yes
parseGetSessionParams(params: unknown): Cdp.GetSessionParameters;
parseResolveRealmParams(params: unknown): Cdp.ResolveRealmParameters;
parseSendCommandParams(params: unknown): Cdp.SendCommandParameters;
// keep-sorted end

Expand Down
5 changes: 5 additions & 0 deletions src/bidiMapper/CommandProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export class CommandProcessor extends EventEmitter<CommandProcessorEventsMap> {
);
this.#cdpProcessor = new CdpProcessor(
browsingContextStorage,
realmStorage,
cdpConnection,
browserCdpClient
);
Expand Down Expand Up @@ -217,6 +218,10 @@ export class CommandProcessor extends EventEmitter<CommandProcessorEventsMap> {
return this.#cdpProcessor.getSession(
this.#parser.parseGetSessionParams(command.params)
);
case 'cdp.resolveRealm':
return this.#cdpProcessor.resolveRealm(
this.#parser.parseResolveRealmParams(command.params)
);
case 'cdp.sendCommand':
return await this.#cdpProcessor.sendCommand(
this.#parser.parseSendCommandParams(command.params)
Expand Down
15 changes: 14 additions & 1 deletion src/bidiMapper/domains/cdp/CdpProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,25 @@
* limitations under the License.
*/

import type {Cdp} from '../../../protocol/protocol.js';
import {UnknownErrorException, type Cdp} from '../../../protocol/protocol.js';
import type {CdpClient, CdpConnection} from '../../BidiMapper.js';
import type {BrowsingContextStorage} from '../context/BrowsingContextStorage.js';
import type {RealmStorage} from '../script/RealmStorage.js';

export class CdpProcessor {
readonly #browsingContextStorage: BrowsingContextStorage;
readonly #realmStorage: RealmStorage;
readonly #cdpConnection: CdpConnection;
readonly #browserCdpClient: CdpClient;

constructor(
browsingContextStorage: BrowsingContextStorage,
realmStorage: RealmStorage,
cdpConnection: CdpConnection,
browserCdpClient: CdpClient
) {
this.#browsingContextStorage = browsingContextStorage;
this.#realmStorage = realmStorage;
this.#cdpConnection = cdpConnection;
this.#browserCdpClient = browserCdpClient;
}
Expand All @@ -44,6 +48,15 @@ export class CdpProcessor {
return {session: sessionId};
}

resolveRealm(params: Cdp.ResolveRealmParameters): Cdp.ResolveRealmResult {
const context = params.realm;
const realm = this.#realmStorage.getRealm({realmId: context});
if (realm === undefined) {
throw new UnknownErrorException(`Could not find realm ${params.realm}`);
}
return {executionContextId: realm.executionContextId};
}

async sendCommand(
params: Cdp.SendCommandParameters
): Promise<Cdp.SendCommandResult> {
Expand Down
3 changes: 3 additions & 0 deletions src/bidiTab/BidiParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ export class BidiParser implements BidiCommandParameterParser {
parseGetSessionParams(params: unknown): Cdp.GetSessionParameters {
return Parser.Cdp.parseGetSessionRequest(params);
}
parseResolveRealmParams(params: unknown): Cdp.ResolveRealmParameters {
return Parser.Cdp.parseResolveRealmRequest(params);
}
parseSendCommandParams(params: unknown): Cdp.SendCommandParameters {
return Parser.Cdp.parseSendCommandRequest(params);
}
Expand Down
10 changes: 10 additions & 0 deletions src/protocol-parser/protocol-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,10 @@ export namespace Cdp {
context: WebDriverBidi.BrowsingContext.BrowsingContextSchema,
});

const ResolveRealmRequestSchema = z.object({
realm: WebDriverBidi.Script.RealmSchema,
});

export function parseSendCommandRequest(
params: unknown
): Protocol.Cdp.SendCommandParameters {
Expand All @@ -344,6 +348,12 @@ export namespace Cdp {
): Protocol.Cdp.GetSessionParameters {
return parseObject(params, GetSessionRequestSchema);
}

export function parseResolveRealmRequest(
params: unknown
): Protocol.Cdp.ResolveRealmParameters {
return parseObject(params, ResolveRealmRequestSchema);
}
}

export namespace Permissions {
Expand Down
29 changes: 26 additions & 3 deletions src/protocol/cdp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
import type {Protocol} from 'devtools-protocol';
import type {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';

import type {BrowsingContext, JsUint} from './generated/webdriver-bidi.js';
import type {
BrowsingContext,
JsUint,
Script,
} from './generated/webdriver-bidi.js';

export type EventNames = Event['method'];

Expand All @@ -26,14 +30,20 @@ export type Message = CommandResponse | Event;
export type Command = {
id: JsUint;
} & CommandData;
export type CommandData = SendCommandCommand | GetSessionCommand;
export type CommandData =
| SendCommandCommand
| GetSessionCommand
| ResolveRealmCommand;

export type CommandResponse = {
type: 'success';
id: JsUint;
result: ResultData;
};
export type ResultData = SendCommandResult | GetSessionResult;
export type ResultData =
| SendCommandResult
| GetSessionResult
| ResolveRealmResult;

export type SendCommandCommand = {
method: 'cdp.sendCommand';
Expand Down Expand Up @@ -67,6 +77,19 @@ export type GetSessionResult = {
session?: Protocol.Target.SessionID;
};

export type ResolveRealmCommand = {
method: 'cdp.resolveRealm';
params: ResolveRealmParameters;
};

export type ResolveRealmParameters = {
realm: Script.Realm;
};

export type ResolveRealmResult = {
executionContextId: Protocol.Runtime.ExecutionContextId;
};

export type Event = {
type: 'event';
} & EventData;
Expand Down
32 changes: 31 additions & 1 deletion tests/script/test_realm.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# limitations under the License.

import pytest
from anys import ANY_STR
from anys import ANY_NUMBER, ANY_STR
from test_helpers import (execute_command, read_JSON_message,
send_JSON_command, subscribe, wait_for_event,
wait_for_filtered_event)
Expand Down Expand Up @@ -205,3 +205,33 @@ async def test_realm_dedicated_worker(websocket, context_id, html):
# Wait for confirmation that worker was destroyed
event = await wait_for_event(websocket, 'script.realmDestroyed')
assert event['params'] == {'realm': worker_realm}


@pytest.mark.asyncio
async def test_realm_cdpResolveRealm(websocket, context_id, html):
url = html()

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

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

response = await read_JSON_message(websocket)

realm = response["params"]["realm"]

result = await execute_command(websocket, {
"method": "cdp.resolveRealm",
"params": {
"realm": realm
}
})

assert result == {'executionContextId': ANY_NUMBER}

0 comments on commit 08d3e45

Please sign in to comment.