Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: respect unhandledPromptBehavior capability #2351

Merged
merged 5 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/bidiMapper/BidiServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import type {CdpClient} from '../cdp/CdpClient';
import type {CdpConnection} from '../cdp/CdpConnection.js';
import type {Browser, ChromiumBidi} from '../protocol/protocol.js';
import type {Browser, ChromiumBidi, Session} from '../protocol/protocol.js';
import {EventEmitter} from '../utils/EventEmitter.js';
import {type LoggerFn, LogType} from '../utils/log.js';
import {ProcessingQueue} from '../utils/ProcessingQueue.js';
Expand All @@ -43,6 +43,7 @@ type BidiServerEvent = {

export type MapperOptions = {
acceptInsecureCerts: boolean;
unhandledPromptBehavior?: Session.UserPromptHandler;
};

export class BidiServer extends EventEmitter<BidiServerEvent> {
Expand Down Expand Up @@ -109,6 +110,7 @@ export class BidiServer extends EventEmitter<BidiServerEvent> {
this.#preloadScriptStorage,
options?.acceptInsecureCerts ?? false,
defaultUserContextId,
options?.unhandledPromptBehavior,
logger
);
this.#commandProcessor = new CommandProcessor(
Expand Down
8 changes: 7 additions & 1 deletion src/bidiMapper/modules/cdp/CdpTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import type {Protocol} from 'devtools-protocol';

import type {CdpClient} from '../../../cdp/CdpClient.js';
import {BiDiModule} from '../../../protocol/chromium-bidi.js';
import type {ChromiumBidi} from '../../../protocol/protocol.js';
import type {ChromiumBidi, Session} from '../../../protocol/protocol.js';
import {Deferred} from '../../../utils/Deferred.js';
import type {LoggerFn} from '../../../utils/log.js';
import {LogType} from '../../../utils/log.js';
Expand All @@ -45,6 +45,7 @@ export class CdpTarget {
readonly #networkStorage: NetworkStorage;

readonly #unblocked = new Deferred<Result<void>>();
readonly #unhandledPromptBehavior?: Session.UserPromptHandler;
readonly #acceptInsecureCerts: boolean;
readonly #logger: LoggerFn | undefined;

Expand All @@ -65,6 +66,7 @@ export class CdpTarget {
browsingContextStorage: BrowsingContextStorage,
networkStorage: NetworkStorage,
acceptInsecureCerts: boolean,
unhandledPromptBehavior?: Session.UserPromptHandler,
logger?: LoggerFn
): CdpTarget {
const cdpTarget = new CdpTarget(
Expand All @@ -77,6 +79,7 @@ export class CdpTarget {
browsingContextStorage,
networkStorage,
acceptInsecureCerts,
unhandledPromptBehavior,
logger
);

Expand All @@ -101,6 +104,7 @@ export class CdpTarget {
browsingContextStorage: BrowsingContextStorage,
networkStorage: NetworkStorage,
acceptInsecureCerts: boolean,
unhandledPromptBehavior?: Session.UserPromptHandler,
logger?: LoggerFn
) {
this.#id = targetId;
Expand All @@ -112,6 +116,7 @@ export class CdpTarget {
this.#networkStorage = networkStorage;
this.#browsingContextStorage = browsingContextStorage;
this.#acceptInsecureCerts = acceptInsecureCerts;
this.#unhandledPromptBehavior = unhandledPromptBehavior;
this.#logger = logger;
}

Expand Down Expand Up @@ -214,6 +219,7 @@ export class CdpTarget {
this.#realmStorage,
frame.url,
undefined,
this.#unhandledPromptBehavior,
this.#logger
);
}
Expand Down
8 changes: 7 additions & 1 deletion src/bidiMapper/modules/cdp/CdpTargetManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type Protocol from 'devtools-protocol';

import type {CdpClient} from '../../../cdp/CdpClient.js';
import type {CdpConnection} from '../../../cdp/CdpConnection.js';
import type {Browser} from '../../../protocol/protocol.js';
import type {Browser, Session} from '../../../protocol/protocol.js';
import {LogType, type LoggerFn} from '../../../utils/log.js';
import {
BrowsingContextImpl,
Expand Down Expand Up @@ -54,6 +54,7 @@ export class CdpTargetManager {

readonly #defaultUserContextId: Browser.UserContext;
readonly #logger?: LoggerFn;
readonly #unhandledPromptBehavior?: Session.UserPromptHandler;

constructor(
cdpConnection: CdpConnection,
Expand All @@ -66,6 +67,7 @@ export class CdpTargetManager {
preloadScriptStorage: PreloadScriptStorage,
acceptInsecureCerts: boolean,
defaultUserContextId: Browser.UserContext,
unhandledPromptBehavior?: Session.UserPromptHandler,
logger?: LoggerFn
) {
this.#acceptInsecureCerts = acceptInsecureCerts;
Expand All @@ -78,6 +80,7 @@ export class CdpTargetManager {
this.#networkStorage = networkStorage;
this.#realmStorage = realmStorage;
this.#defaultUserContextId = defaultUserContextId;
this.#unhandledPromptBehavior = unhandledPromptBehavior;
this.#logger = logger;

this.#setEventListeners(browserCdpClient);
Expand Down Expand Up @@ -130,6 +133,7 @@ export class CdpTargetManager {
// later.
'about:blank',
undefined,
this.#unhandledPromptBehavior,
this.#logger
);
}
Expand Down Expand Up @@ -189,6 +193,7 @@ export class CdpTargetManager {
// TODO: check who to deal with non-null creator and its `creatorOrigin`.
targetInfo.url === '' ? 'about:blank' : targetInfo.url,
targetInfo.openerFrameId ?? targetInfo.openerId,
this.#unhandledPromptBehavior,
this.#logger
);
}
Expand Down Expand Up @@ -252,6 +257,7 @@ export class CdpTargetManager {
this.#browsingContextStorage,
this.#networkStorage,
this.#acceptInsecureCerts,
this.#unhandledPromptBehavior,
this.#logger
);

Expand Down
63 changes: 54 additions & 9 deletions src/bidiMapper/modules/context/BrowsingContextImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
NoSuchElementException,
NoSuchHistoryEntryException,
Script,
type Session,
UnableToCaptureScreenException,
UnknownErrorException,
UnsupportedOperationException,
Expand Down Expand Up @@ -90,6 +91,7 @@ export class BrowsingContextImpl {

// Set when the user prompt is opened. Required to provide the type in closing event.
#lastUserPromptType?: BrowsingContext.UserPromptType;
readonly #unhandledPromptBehavior?: Session.UserPromptHandler;

private constructor(
id: BrowsingContext.BrowsingContext,
Expand All @@ -101,6 +103,7 @@ export class BrowsingContextImpl {
realmStorage: RealmStorage,
url: string,
originalOpener?: string,
unhandledPromptBehavior?: Session.UserPromptHandler,
logger?: LoggerFn
) {
this.#cdpTarget = cdpTarget;
Expand All @@ -110,6 +113,7 @@ export class BrowsingContextImpl {
this.#eventManager = eventManager;
this.#browsingContextStorage = browsingContextStorage;
this.#realmStorage = realmStorage;
this.#unhandledPromptBehavior = unhandledPromptBehavior;
this.#logger = logger;
this.#url = url;

Expand All @@ -126,6 +130,7 @@ export class BrowsingContextImpl {
realmStorage: RealmStorage,
url: string,
originalOpener?: string,
unhandledPromptBehavior?: Session.UserPromptHandler,
logger?: LoggerFn
): BrowsingContextImpl {
const context = new BrowsingContextImpl(
Expand All @@ -138,6 +143,7 @@ export class BrowsingContextImpl {
realmStorage,
url,
originalOpener,
unhandledPromptBehavior,
logger
);

Expand Down Expand Up @@ -611,13 +617,14 @@ export class BrowsingContextImpl {
const promptType = BrowsingContextImpl.#getPromptType(params.type);
// Set the last prompt type to provide it in closing event.
this.#lastUserPromptType = promptType;
const promptHandler = this.#getPromptHandler(promptType);
this.#eventManager.registerEvent(
{
type: 'event',
method: ChromiumBidi.BrowsingContext.EventNames.UserPromptOpened,
params: {
context: this.id,
handler: this.#getPromptHandler(),
handler: promptHandler,
type: promptType,
message: params.message,
...(params.type === 'prompt'
Expand All @@ -627,6 +634,19 @@ export class BrowsingContextImpl {
},
this.id
);

switch (promptHandler) {
// Based on `unhandledPromptBehavior`, check if the prompt should be handled
// automatically (`accept`, `dismiss`) or wait for the user to do it.
case 'accept':
void this.handleUserPrompt(true);
break;
case 'dismiss':
void this.handleUserPrompt(false);
break;
case 'ignore':
break;
}
});
}

Expand All @@ -645,9 +665,36 @@ export class BrowsingContextImpl {
}
}

#getPromptHandler(): 'accept' | 'dismiss' | 'ignore' {
// TODO: implement.
return 'ignore';
#getPromptHandler(
promptType: BrowsingContext.UserPromptType
): 'accept' | 'dismiss' | 'ignore' {
const defaultPromptHandler = 'dismiss';
switch (promptType) {
case BrowsingContext.UserPromptType.Alert:
return (
this.#unhandledPromptBehavior?.alert ??
this.#unhandledPromptBehavior?.default ??
defaultPromptHandler
);
case BrowsingContext.UserPromptType.Beforeunload:
return (
this.#unhandledPromptBehavior?.beforeUnload ??
this.#unhandledPromptBehavior?.default ??
defaultPromptHandler
);
case BrowsingContext.UserPromptType.Confirm:
return (
this.#unhandledPromptBehavior?.confirm ??
this.#unhandledPromptBehavior?.default ??
defaultPromptHandler
);
case BrowsingContext.UserPromptType.Prompt:
return (
this.#unhandledPromptBehavior?.prompt ??
this.#unhandledPromptBehavior?.default ??
defaultPromptHandler
);
}
}

#documentChanged(loaderId?: Protocol.Network.LoaderId) {
Expand Down Expand Up @@ -857,12 +904,10 @@ export class BrowsingContextImpl {
}
}

async handleUserPrompt(
params: BrowsingContext.HandleUserPromptParameters
): Promise<void> {
async handleUserPrompt(accept?: boolean, userText?: string): Promise<void> {
await this.#cdpTarget.cdpClient.sendCommand('Page.handleJavaScriptDialog', {
accept: params.accept ?? true,
promptText: params.userText,
accept: accept ?? true,
promptText: userText,
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/bidiMapper/modules/context/BrowsingContextProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export class BrowsingContextProcessor {
): Promise<EmptyResult> {
const context = this.#browsingContextStorage.getContext(params.context);
try {
await context.handleUserPrompt(params);
await context.handleUserPrompt(params.accept, params.userText);
} catch (error: any) {
// Heuristically determine the error
// https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/protocol/page_handler.cc;l=1085?q=%22No%20dialog%20is%20showing%22&ss=chromium
Expand Down
4 changes: 3 additions & 1 deletion src/bidiServer/WebSocketServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,9 @@ export class WebSocketServer {
#getMapperOptions(capabilities: any): MapperOptions {
const acceptInsecureCerts =
capabilities?.alwaysMatch?.acceptInsecureCerts ?? false;
return {acceptInsecureCerts};
const unhandledPromptBehavior =
capabilities?.alwaysMatch?.unhandledPromptBehavior ?? undefined;
return {acceptInsecureCerts, unhandledPromptBehavior};
}

#getChromeOptions(capabilities: any): ChromeOptions {
Expand Down
14 changes: 0 additions & 14 deletions tests/browsing_context/__snapshots__/test_close.ambr

This file was deleted.

Loading
Loading