Add Brave CDP automation, replace Oracle browser mode
Connects to user's running Brave via Chrome DevTools Protocol to automate ChatGPT interaction. Uses puppeteer-core to open a tab, send the prompt, wait for response, and extract the result. No cookies, no separate profiles, no copy/paste. Just connects to the browser where the user is already logged in. One-time setup: relaunch Brave with --remote-debugging-port=9222 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
91
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextImpl.d.ts
generated
vendored
Normal file
91
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextImpl.d.ts
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright 2022 Google LLC.
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import type { Protocol } from 'devtools-protocol';
|
||||
import { BrowsingContext, type Emulation, type UAClientHints } from '../../../protocol/protocol.js';
|
||||
import { type LoggerFn } from '../../../utils/log.js';
|
||||
import type { ContextConfigStorage } from '../browser/ContextConfigStorage.js';
|
||||
import type { CdpTarget } from '../cdp/CdpTarget.js';
|
||||
import type { Realm } from '../script/Realm.js';
|
||||
import type { RealmStorage } from '../script/RealmStorage.js';
|
||||
import type { EventManager } from '../session/EventManager.js';
|
||||
import type { BrowsingContextStorage } from './BrowsingContextStorage.js';
|
||||
export declare class BrowsingContextImpl {
|
||||
#private;
|
||||
static readonly LOGGER_PREFIX: "debug:browsingContext";
|
||||
readonly userContext: string;
|
||||
private constructor();
|
||||
static create(id: BrowsingContext.BrowsingContext, parentId: BrowsingContext.BrowsingContext | null, userContext: string, cdpTarget: CdpTarget, eventManager: EventManager, browsingContextStorage: BrowsingContextStorage, realmStorage: RealmStorage, configStorage: ContextConfigStorage, url: string, originalOpener?: string, logger?: LoggerFn): BrowsingContextImpl;
|
||||
/**
|
||||
* @see https://html.spec.whatwg.org/multipage/document-sequences.html#navigable
|
||||
*/
|
||||
get navigableId(): string | undefined;
|
||||
get navigationId(): string;
|
||||
dispose(emitContextDestroyed: boolean): void;
|
||||
/** Returns the ID of this context. */
|
||||
get id(): BrowsingContext.BrowsingContext;
|
||||
/** Returns the parent context ID. */
|
||||
get parentId(): BrowsingContext.BrowsingContext | null;
|
||||
/** Sets the parent context ID and updates parent's children. */
|
||||
set parentId(parentId: BrowsingContext.BrowsingContext | null);
|
||||
/** Returns the parent context. */
|
||||
get parent(): BrowsingContextImpl | null;
|
||||
/** Returns all direct children contexts. */
|
||||
get directChildren(): BrowsingContextImpl[];
|
||||
/** Returns all children contexts, flattened. */
|
||||
get allChildren(): BrowsingContextImpl[];
|
||||
/**
|
||||
* Returns true if this is a top-level context.
|
||||
* This is the case whenever the parent context ID is null.
|
||||
*/
|
||||
isTopLevelContext(): boolean;
|
||||
get top(): BrowsingContextImpl;
|
||||
addChild(childId: BrowsingContext.BrowsingContext): void;
|
||||
get cdpTarget(): CdpTarget;
|
||||
updateCdpTarget(cdpTarget: CdpTarget): void;
|
||||
get url(): string;
|
||||
lifecycleLoaded(): Promise<void>;
|
||||
targetUnblockedOrThrow(): Promise<void>;
|
||||
/** Returns a sandbox for internal helper scripts which is not exposed to the user.*/
|
||||
getOrCreateHiddenSandbox(): Promise<Realm>;
|
||||
/** Returns a sandbox which is exposed to user. */
|
||||
getOrCreateUserSandbox(sandbox: string | undefined): Promise<Realm>;
|
||||
/**
|
||||
* Implements https://w3c.github.io/webdriver-bidi/#get-the-navigable-info.
|
||||
*/
|
||||
serializeToBidiValue(maxDepth?: number | null, addParentField?: boolean): BrowsingContext.Info;
|
||||
onTargetInfoChanged(params: Protocol.Target.TargetInfoChangedEvent): void;
|
||||
navigate(url: string, wait: BrowsingContext.ReadinessState): Promise<BrowsingContext.NavigateResult>;
|
||||
reload(ignoreCache: boolean, wait: BrowsingContext.ReadinessState): Promise<BrowsingContext.NavigateResult>;
|
||||
setViewport(viewport: BrowsingContext.Viewport | null, devicePixelRatio: number | null, screenOrientation: Emulation.ScreenOrientation | null): Promise<void>;
|
||||
handleUserPrompt(accept?: boolean, userText?: string): Promise<void>;
|
||||
activate(): Promise<void>;
|
||||
captureScreenshot(params: BrowsingContext.CaptureScreenshotParameters): Promise<BrowsingContext.CaptureScreenshotResult>;
|
||||
print(params: BrowsingContext.PrintParameters): Promise<BrowsingContext.PrintResult>;
|
||||
close(): Promise<void>;
|
||||
traverseHistory(delta: number): Promise<void>;
|
||||
toggleModulesIfNeeded(): Promise<void>;
|
||||
locateNodes(params: BrowsingContext.LocateNodesParameters): Promise<BrowsingContext.LocateNodesResult>;
|
||||
setTimezoneOverride(timezone: string | null): Promise<void>;
|
||||
setLocaleOverride(locale: string | null): Promise<void>;
|
||||
setGeolocationOverride(geolocation: Emulation.GeolocationCoordinates | Emulation.GeolocationPositionError | null): Promise<void>;
|
||||
setScriptingEnabled(scriptingEnabled: false | null): Promise<void>;
|
||||
setUserAgentAndAcceptLanguage(userAgent: string | null | undefined, acceptLanguage: string | null | undefined, clientHints: UAClientHints.Emulation.ClientHintsMetadata | null | undefined): Promise<void>;
|
||||
setEmulatedNetworkConditions(networkConditions: Emulation.NetworkConditions | null): Promise<void>;
|
||||
setTouchOverride(maxTouchPoints: number | null): Promise<void>;
|
||||
setExtraHeaders(cdpExtraHeaders: Protocol.Network.Headers): Promise<Promise<any>>;
|
||||
}
|
||||
export declare function serializeOrigin(origin: string): string;
|
||||
1463
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextImpl.js
generated
vendored
Normal file
1463
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextImpl.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextImpl.js.map
generated
vendored
Normal file
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextImpl.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
22
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextProcessor.d.ts
generated
vendored
Normal file
22
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextProcessor.d.ts
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { CdpClient } from '../../../cdp/CdpClient.js';
|
||||
import { BrowsingContext, type EmptyResult } from '../../../protocol/protocol.js';
|
||||
import type { ContextConfigStorage } from '../browser/ContextConfigStorage.js';
|
||||
import type { UserContextStorage } from '../browser/UserContextStorage.js';
|
||||
import type { EventManager } from '../session/EventManager.js';
|
||||
import type { BrowsingContextStorage } from './BrowsingContextStorage.js';
|
||||
export declare class BrowsingContextProcessor {
|
||||
#private;
|
||||
constructor(browserCdpClient: CdpClient, browsingContextStorage: BrowsingContextStorage, userContextStorage: UserContextStorage, contextConfigStorage: ContextConfigStorage, eventManager: EventManager);
|
||||
getTree(params: BrowsingContext.GetTreeParameters): BrowsingContext.GetTreeResult;
|
||||
create(params: BrowsingContext.CreateParameters): Promise<BrowsingContext.CreateResult>;
|
||||
navigate(params: BrowsingContext.NavigateParameters): Promise<BrowsingContext.NavigateResult>;
|
||||
reload(params: BrowsingContext.ReloadParameters): Promise<EmptyResult>;
|
||||
activate(params: BrowsingContext.ActivateParameters): Promise<EmptyResult>;
|
||||
captureScreenshot(params: BrowsingContext.CaptureScreenshotParameters): Promise<BrowsingContext.CaptureScreenshotResult>;
|
||||
print(params: BrowsingContext.PrintParameters): Promise<BrowsingContext.PrintResult>;
|
||||
setViewport(params: BrowsingContext.SetViewportParameters): Promise<EmptyResult>;
|
||||
traverseHistory(params: BrowsingContext.TraverseHistoryParameters): Promise<BrowsingContext.TraverseHistoryResult>;
|
||||
handleUserPrompt(params: BrowsingContext.HandleUserPromptParameters): Promise<EmptyResult>;
|
||||
close(params: BrowsingContext.CloseParameters): Promise<EmptyResult>;
|
||||
locateNodes(params: BrowsingContext.LocateNodesParameters): Promise<BrowsingContext.LocateNodesResult>;
|
||||
}
|
||||
267
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextProcessor.js
generated
vendored
Normal file
267
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextProcessor.js
generated
vendored
Normal file
@@ -0,0 +1,267 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BrowsingContextProcessor = void 0;
|
||||
const protocol_js_1 = require("../../../protocol/protocol.js");
|
||||
class BrowsingContextProcessor {
|
||||
#browserCdpClient;
|
||||
#browsingContextStorage;
|
||||
#contextConfigStorage;
|
||||
#eventManager;
|
||||
#userContextStorage;
|
||||
constructor(browserCdpClient, browsingContextStorage, userContextStorage, contextConfigStorage, eventManager) {
|
||||
this.#contextConfigStorage = contextConfigStorage;
|
||||
this.#userContextStorage = userContextStorage;
|
||||
this.#browserCdpClient = browserCdpClient;
|
||||
this.#browsingContextStorage = browsingContextStorage;
|
||||
this.#eventManager = eventManager;
|
||||
this.#eventManager.addSubscribeHook(protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.ContextCreated, this.#onContextCreatedSubscribeHook.bind(this));
|
||||
}
|
||||
getTree(params) {
|
||||
const resultContexts = params.root === undefined
|
||||
? this.#browsingContextStorage.getTopLevelContexts()
|
||||
: [this.#browsingContextStorage.getContext(params.root)];
|
||||
return {
|
||||
contexts: resultContexts.map((c) => c.serializeToBidiValue(params.maxDepth ?? Number.MAX_VALUE)),
|
||||
};
|
||||
}
|
||||
async create(params) {
|
||||
let referenceContext;
|
||||
let userContext = 'default';
|
||||
if (params.referenceContext !== undefined) {
|
||||
referenceContext = this.#browsingContextStorage.getContext(params.referenceContext);
|
||||
if (!referenceContext.isTopLevelContext()) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`referenceContext should be a top-level context`);
|
||||
}
|
||||
userContext = referenceContext.userContext;
|
||||
}
|
||||
if (params.userContext !== undefined) {
|
||||
userContext = params.userContext;
|
||||
}
|
||||
const existingContexts = this.#browsingContextStorage
|
||||
.getAllContexts()
|
||||
.filter((context) => context.userContext === userContext);
|
||||
let newWindow = false;
|
||||
switch (params.type) {
|
||||
case "tab" /* BrowsingContext.CreateType.Tab */:
|
||||
newWindow = false;
|
||||
break;
|
||||
case "window" /* BrowsingContext.CreateType.Window */:
|
||||
newWindow = true;
|
||||
break;
|
||||
}
|
||||
if (!existingContexts.length) {
|
||||
// If there are no contexts in the given user context, we need to set
|
||||
// newWindow to true as newWindow=false will be rejected.
|
||||
newWindow = true;
|
||||
}
|
||||
let result;
|
||||
try {
|
||||
result = await this.#browserCdpClient.sendCommand('Target.createTarget', {
|
||||
url: 'about:blank',
|
||||
newWindow,
|
||||
browserContextId: userContext === 'default' ? undefined : userContext,
|
||||
background: params.background === true,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
if (
|
||||
// See https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/devtools/protocol/target_handler.cc;l=90;drc=e80392ac11e48a691f4309964cab83a3a59e01c8
|
||||
err.message.startsWith('Failed to find browser context with id') ||
|
||||
// See https://source.chromium.org/chromium/chromium/src/+/main:headless/lib/browser/protocol/target_handler.cc;l=49;drc=e80392ac11e48a691f4309964cab83a3a59e01c8
|
||||
err.message === 'browserContextId') {
|
||||
throw new protocol_js_1.NoSuchUserContextException(`The context ${userContext} was not found`);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
// Wait for the new target to be attached and to be added to the browsing context
|
||||
// storage.
|
||||
const context = await this.#browsingContextStorage.waitForContext(result.targetId);
|
||||
// Wait for the new tab to be loaded to avoid race conditions in the
|
||||
// `browsingContext` events, when the `browsingContext.domContentLoaded` and
|
||||
// `browsingContext.load` events from the initial `about:blank` navigation
|
||||
// are emitted after the next navigation is started.
|
||||
// Details: https://github.com/web-platform-tests/wpt/issues/35846
|
||||
await context.lifecycleLoaded();
|
||||
return { context: context.id };
|
||||
}
|
||||
navigate(params) {
|
||||
const context = this.#browsingContextStorage.getContext(params.context);
|
||||
return context.navigate(params.url, params.wait ?? "none" /* BrowsingContext.ReadinessState.None */);
|
||||
}
|
||||
reload(params) {
|
||||
const context = this.#browsingContextStorage.getContext(params.context);
|
||||
return context.reload(params.ignoreCache ?? false, params.wait ?? "none" /* BrowsingContext.ReadinessState.None */);
|
||||
}
|
||||
async activate(params) {
|
||||
const context = this.#browsingContextStorage.getContext(params.context);
|
||||
if (!context.isTopLevelContext()) {
|
||||
throw new protocol_js_1.InvalidArgumentException('Activation is only supported on the top-level context');
|
||||
}
|
||||
await context.activate();
|
||||
return {};
|
||||
}
|
||||
async captureScreenshot(params) {
|
||||
const context = this.#browsingContextStorage.getContext(params.context);
|
||||
return await context.captureScreenshot(params);
|
||||
}
|
||||
async print(params) {
|
||||
const context = this.#browsingContextStorage.getContext(params.context);
|
||||
return await context.print(params);
|
||||
}
|
||||
async setViewport(params) {
|
||||
// Check the The viewport size limits is not checked by protocol parser, so we need to validate
|
||||
// it manually:
|
||||
// https://crsrc.org/c/content/browser/devtools/protocol/emulation_handler.cc;drc=f49e23d8e2bd190b42ec62284b8be10dcccd0446;l=660
|
||||
const maxDimensionSize = 10_000_000;
|
||||
if ((params.viewport?.height ?? 0) > maxDimensionSize ||
|
||||
(params.viewport?.width ?? 0) > maxDimensionSize) {
|
||||
throw new protocol_js_1.UnsupportedOperationException(`Viewport dimension over ${maxDimensionSize} are not supported`);
|
||||
}
|
||||
const config = {};
|
||||
// `undefined` means no changes should be done to the config.
|
||||
if (params.devicePixelRatio !== undefined) {
|
||||
config.devicePixelRatio = params.devicePixelRatio;
|
||||
}
|
||||
if (params.viewport !== undefined) {
|
||||
config.viewport = params.viewport;
|
||||
}
|
||||
const impactedTopLevelContexts = await this.#getRelatedTopLevelBrowsingContexts(params.context, params.userContexts);
|
||||
for (const userContextId of params.userContexts ?? []) {
|
||||
this.#contextConfigStorage.updateUserContextConfig(userContextId, config);
|
||||
}
|
||||
if (params.context !== undefined) {
|
||||
this.#contextConfigStorage.updateBrowsingContextConfig(params.context, config);
|
||||
}
|
||||
await Promise.all(impactedTopLevelContexts.map(async (context) => {
|
||||
const config = this.#contextConfigStorage.getActiveConfig(context.id, context.userContext);
|
||||
await context.setViewport(config.viewport ?? null, config.devicePixelRatio ?? null, config.screenOrientation ?? null);
|
||||
}));
|
||||
return {};
|
||||
}
|
||||
/**
|
||||
* Returns a list of top-level browsing context ids.
|
||||
*/
|
||||
async #getRelatedTopLevelBrowsingContexts(browsingContextId, userContextIds) {
|
||||
if (browsingContextId === undefined && userContextIds === undefined) {
|
||||
throw new protocol_js_1.InvalidArgumentException('Either userContexts or context must be provided');
|
||||
}
|
||||
if (browsingContextId !== undefined && userContextIds !== undefined) {
|
||||
throw new protocol_js_1.InvalidArgumentException('userContexts and context are mutually exclusive');
|
||||
}
|
||||
if (browsingContextId !== undefined) {
|
||||
const context = this.#browsingContextStorage.getContext(browsingContextId);
|
||||
if (!context.isTopLevelContext()) {
|
||||
throw new protocol_js_1.InvalidArgumentException('Emulating viewport is only supported on the top-level context');
|
||||
}
|
||||
return [context];
|
||||
}
|
||||
// Verify that all user contexts exist.
|
||||
await this.#userContextStorage.verifyUserContextIdList(userContextIds);
|
||||
const result = [];
|
||||
for (const userContextId of userContextIds) {
|
||||
const topLevelBrowsingContexts = this.#browsingContextStorage
|
||||
.getTopLevelContexts()
|
||||
.filter((browsingContext) => browsingContext.userContext === userContextId);
|
||||
result.push(...topLevelBrowsingContexts);
|
||||
}
|
||||
// Remove duplicates. Compare `BrowsingContextImpl` by reference is correct here, as
|
||||
// `browsingContextStorage` returns the same instance for the same id.
|
||||
return [...new Set(result).values()];
|
||||
}
|
||||
async traverseHistory(params) {
|
||||
const context = this.#browsingContextStorage.getContext(params.context);
|
||||
if (!context) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`No browsing context with id ${params.context}`);
|
||||
}
|
||||
if (!context.isTopLevelContext()) {
|
||||
throw new protocol_js_1.InvalidArgumentException('Traversing history is only supported on the top-level context');
|
||||
}
|
||||
await context.traverseHistory(params.delta);
|
||||
return {};
|
||||
}
|
||||
async handleUserPrompt(params) {
|
||||
const context = this.#browsingContextStorage.getContext(params.context);
|
||||
try {
|
||||
await context.handleUserPrompt(params.accept, params.userText);
|
||||
}
|
||||
catch (error) {
|
||||
// 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
|
||||
if (error.message?.includes('No dialog is showing')) {
|
||||
throw new protocol_js_1.NoSuchAlertException('No dialog is showing');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
async close(params) {
|
||||
const context = this.#browsingContextStorage.getContext(params.context);
|
||||
if (!context.isTopLevelContext()) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`Non top-level browsing context ${context.id} cannot be closed.`);
|
||||
}
|
||||
// Parent session of a page target session can be a `browser` or a `tab` session.
|
||||
const parentCdpClient = context.cdpTarget.parentCdpClient;
|
||||
try {
|
||||
const detachedFromTargetPromise = new Promise((resolve) => {
|
||||
const onContextDestroyed = (event) => {
|
||||
if (event.targetId === params.context) {
|
||||
parentCdpClient.off('Target.detachedFromTarget', onContextDestroyed);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
parentCdpClient.on('Target.detachedFromTarget', onContextDestroyed);
|
||||
});
|
||||
try {
|
||||
if (params.promptUnload) {
|
||||
await context.close();
|
||||
}
|
||||
else {
|
||||
await parentCdpClient.sendCommand('Target.closeTarget', {
|
||||
targetId: params.context,
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// Swallow error that arise from the session being destroyed. Rely on the
|
||||
// `detachedFromTargetPromise` event to be resolved.
|
||||
if (!parentCdpClient.isCloseError(error)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// Sometimes CDP command finishes before `detachedFromTarget` event,
|
||||
// sometimes after. Wait for the CDP command to be finished, and then wait
|
||||
// for `detachedFromTarget` if it hasn't emitted.
|
||||
await detachedFromTargetPromise;
|
||||
}
|
||||
catch (error) {
|
||||
// Swallow error that arise from the page being destroyed
|
||||
// Example is navigating to faulty SSL certificate
|
||||
if (!(error.code === -32000 /* CdpErrorConstants.GENERIC_ERROR */ &&
|
||||
error.message === 'Not attached to an active page')) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
async locateNodes(params) {
|
||||
const context = this.#browsingContextStorage.getContext(params.context);
|
||||
return await context.locateNodes(params);
|
||||
}
|
||||
#onContextCreatedSubscribeHook(contextId) {
|
||||
const context = this.#browsingContextStorage.getContext(contextId);
|
||||
const contextsToReport = [
|
||||
context,
|
||||
...this.#browsingContextStorage.getContext(contextId).allChildren,
|
||||
];
|
||||
contextsToReport.forEach((context) => {
|
||||
this.#eventManager.registerEvent({
|
||||
type: 'event',
|
||||
method: protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.ContextCreated,
|
||||
params: context.serializeToBidiValue(),
|
||||
}, context.id);
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
exports.BrowsingContextProcessor = BrowsingContextProcessor;
|
||||
//# sourceMappingURL=BrowsingContextProcessor.js.map
|
||||
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextProcessor.js.map
generated
vendored
Normal file
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextProcessor.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
47
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextStorage.d.ts
generated
vendored
Normal file
47
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextStorage.d.ts
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright 2022 Google LLC.
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { type BrowsingContext } from '../../../protocol/protocol.js';
|
||||
import type { BrowsingContextImpl } from './BrowsingContextImpl.js';
|
||||
/** Container class for browsing contexts. */
|
||||
export declare class BrowsingContextStorage {
|
||||
#private;
|
||||
/** Gets all top-level contexts, i.e. those with no parent. */
|
||||
getTopLevelContexts(): BrowsingContextImpl[];
|
||||
/** Gets all contexts. */
|
||||
getAllContexts(): BrowsingContextImpl[];
|
||||
/** Deletes the context with the given ID. */
|
||||
deleteContextById(id: BrowsingContext.BrowsingContext): void;
|
||||
/** Deletes the given context. */
|
||||
deleteContext(context: BrowsingContextImpl): void;
|
||||
/** Tracks the given context. */
|
||||
addContext(context: BrowsingContextImpl): void;
|
||||
/**
|
||||
* Waits for a context with the given ID to be added and returns it.
|
||||
*/
|
||||
waitForContext(browsingContextId: BrowsingContext.BrowsingContext): Promise<BrowsingContextImpl>;
|
||||
/** Returns true whether there is an existing context with the given ID. */
|
||||
hasContext(id: BrowsingContext.BrowsingContext): boolean;
|
||||
/** Gets the context with the given ID, if any. */
|
||||
findContext(id: BrowsingContext.BrowsingContext): BrowsingContextImpl | undefined;
|
||||
/** Returns the top-level context ID of the given context, if any. */
|
||||
findTopLevelContextId(id: BrowsingContext.BrowsingContext | null): BrowsingContext.BrowsingContext | null;
|
||||
findContextBySession(sessionId: string): BrowsingContextImpl | undefined;
|
||||
/** Gets the context with the given ID, if any, otherwise throws. */
|
||||
getContext(id: BrowsingContext.BrowsingContext): BrowsingContextImpl;
|
||||
verifyTopLevelContextsList(contexts: BrowsingContext.BrowsingContext[] | undefined): Set<BrowsingContextImpl>;
|
||||
verifyContextsList(contexts: BrowsingContext.BrowsingContext[]): void;
|
||||
}
|
||||
134
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextStorage.js
generated
vendored
Normal file
134
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextStorage.js
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Copyright 2022 Google LLC.
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BrowsingContextStorage = void 0;
|
||||
const protocol_js_1 = require("../../../protocol/protocol.js");
|
||||
const EventEmitter_js_1 = require("../../../utils/EventEmitter.js");
|
||||
/** Container class for browsing contexts. */
|
||||
class BrowsingContextStorage {
|
||||
/** Map from context ID to context implementation. */
|
||||
#contexts = new Map();
|
||||
/** Event emitter for browsing context storage eventsis not expected to be exposed to
|
||||
* the outside world. */
|
||||
#eventEmitter = new EventEmitter_js_1.EventEmitter();
|
||||
/** Gets all top-level contexts, i.e. those with no parent. */
|
||||
getTopLevelContexts() {
|
||||
return this.getAllContexts().filter((context) => context.isTopLevelContext());
|
||||
}
|
||||
/** Gets all contexts. */
|
||||
getAllContexts() {
|
||||
return Array.from(this.#contexts.values());
|
||||
}
|
||||
/** Deletes the context with the given ID. */
|
||||
deleteContextById(id) {
|
||||
this.#contexts.delete(id);
|
||||
}
|
||||
/** Deletes the given context. */
|
||||
deleteContext(context) {
|
||||
this.#contexts.delete(context.id);
|
||||
}
|
||||
/** Tracks the given context. */
|
||||
addContext(context) {
|
||||
this.#contexts.set(context.id, context);
|
||||
this.#eventEmitter.emit("added" /* BrowsingContextStorageEvents.Added */, {
|
||||
browsingContext: context,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Waits for a context with the given ID to be added and returns it.
|
||||
*/
|
||||
waitForContext(browsingContextId) {
|
||||
if (this.#contexts.has(browsingContextId)) {
|
||||
return Promise.resolve(this.getContext(browsingContextId));
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
const listener = (event) => {
|
||||
if (event.browsingContext.id === browsingContextId) {
|
||||
this.#eventEmitter.off("added" /* BrowsingContextStorageEvents.Added */, listener);
|
||||
resolve(event.browsingContext);
|
||||
}
|
||||
};
|
||||
this.#eventEmitter.on("added" /* BrowsingContextStorageEvents.Added */, listener);
|
||||
});
|
||||
}
|
||||
/** Returns true whether there is an existing context with the given ID. */
|
||||
hasContext(id) {
|
||||
return this.#contexts.has(id);
|
||||
}
|
||||
/** Gets the context with the given ID, if any. */
|
||||
findContext(id) {
|
||||
return this.#contexts.get(id);
|
||||
}
|
||||
/** Returns the top-level context ID of the given context, if any. */
|
||||
findTopLevelContextId(id) {
|
||||
if (id === null) {
|
||||
return null;
|
||||
}
|
||||
const maybeContext = this.findContext(id);
|
||||
if (!maybeContext) {
|
||||
return null;
|
||||
}
|
||||
const parentId = maybeContext.parentId ?? null;
|
||||
if (parentId === null) {
|
||||
return id;
|
||||
}
|
||||
return this.findTopLevelContextId(parentId);
|
||||
}
|
||||
findContextBySession(sessionId) {
|
||||
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) {
|
||||
const result = this.findContext(id);
|
||||
if (result === undefined) {
|
||||
throw new protocol_js_1.NoSuchFrameException(`Context ${id} not found`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
verifyTopLevelContextsList(contexts) {
|
||||
const foundContexts = new Set();
|
||||
if (!contexts) {
|
||||
return foundContexts;
|
||||
}
|
||||
for (const contextId of contexts) {
|
||||
const context = this.getContext(contextId);
|
||||
if (context.isTopLevelContext()) {
|
||||
foundContexts.add(context);
|
||||
}
|
||||
else {
|
||||
throw new protocol_js_1.InvalidArgumentException(`Non top-level context '${contextId}' given.`);
|
||||
}
|
||||
}
|
||||
return foundContexts;
|
||||
}
|
||||
verifyContextsList(contexts) {
|
||||
if (!contexts.length) {
|
||||
return;
|
||||
}
|
||||
for (const contextId of contexts) {
|
||||
this.getContext(contextId);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.BrowsingContextStorage = BrowsingContextStorage;
|
||||
//# sourceMappingURL=BrowsingContextStorage.js.map
|
||||
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextStorage.js.map
generated
vendored
Normal file
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/BrowsingContextStorage.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"BrowsingContextStorage.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/modules/context/BrowsingContextStorage.ts"],"names":[],"mappings":"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"}
|
||||
87
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/NavigationTracker.d.ts
generated
vendored
Normal file
87
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/NavigationTracker.d.ts
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { Protocol } from 'devtools-protocol';
|
||||
import { type BrowsingContext } from '../../../protocol/protocol.js';
|
||||
import { Deferred } from '../../../utils/Deferred.js';
|
||||
import { type LoggerFn } from '../../../utils/log.js';
|
||||
import type { EventManager } from '../session/EventManager.js';
|
||||
export declare const enum NavigationEventName {
|
||||
FragmentNavigated = "browsingContext.fragmentNavigated",
|
||||
NavigationAborted = "browsingContext.navigationAborted",
|
||||
NavigationFailed = "browsingContext.navigationFailed",
|
||||
Load = "browsingContext.load"
|
||||
}
|
||||
export declare class NavigationResult {
|
||||
readonly eventName: NavigationEventName;
|
||||
readonly message?: string;
|
||||
constructor(eventName: NavigationEventName, message?: string);
|
||||
}
|
||||
export declare class NavigationState {
|
||||
#private;
|
||||
readonly navigationId: `${string}-${string}-${string}-${string}-${string}`;
|
||||
url: string;
|
||||
loaderId?: string;
|
||||
committed: Deferred<void>;
|
||||
isFragmentNavigation?: boolean;
|
||||
get finished(): Promise<NavigationResult>;
|
||||
constructor(url: string, browsingContextId: string, isInitial: boolean, eventManager: EventManager);
|
||||
navigationInfo(): BrowsingContext.NavigationInfo;
|
||||
start(): void;
|
||||
frameNavigated(): void;
|
||||
fragmentNavigated(): void;
|
||||
load(): void;
|
||||
fail(message: string): void;
|
||||
}
|
||||
/**
|
||||
* Keeps track of navigations. Details: http://go/webdriver:bidi-navigation
|
||||
*/
|
||||
export declare class NavigationTracker {
|
||||
#private;
|
||||
constructor(url: string, browsingContextId: string, eventManager: EventManager, logger?: LoggerFn);
|
||||
/**
|
||||
* Returns current started ongoing navigation. It can be either a started pending
|
||||
* navigation, or one is already navigated.
|
||||
*/
|
||||
get currentNavigationId(): `${string}-${string}-${string}-${string}-${string}`;
|
||||
/**
|
||||
* Flags if the current navigation relates to the initial to `about:blank` navigation.
|
||||
*/
|
||||
get isInitialNavigation(): boolean;
|
||||
/**
|
||||
* Url of the last navigated navigation.
|
||||
*/
|
||||
get url(): string;
|
||||
/**
|
||||
* Creates a pending navigation e.g. when navigation command is called. Required to
|
||||
* provide navigation id before the actual navigation is started. It will be used when
|
||||
* navigation started. Can be aborted, failed, fragment navigated, or became a current
|
||||
* navigation.
|
||||
*/
|
||||
createPendingNavigation(url: string, canBeInitialNavigation?: boolean): NavigationState;
|
||||
dispose(): void;
|
||||
onTargetInfoChanged(url: string): void;
|
||||
/**
|
||||
* @param {string} unreachableUrl indicated the navigation is actually failed.
|
||||
*/
|
||||
frameNavigated(url: string, loaderId: string, unreachableUrl?: string): void;
|
||||
navigatedWithinDocument(url: string, navigationType: Protocol.Page.NavigatedWithinDocumentEvent['navigationType']): void;
|
||||
/**
|
||||
* Required to mark navigation as fully complete.
|
||||
* TODO: navigation should be complete when it became the current one on
|
||||
* `Page.frameNavigated` or on navigating command finished with a new loader Id.
|
||||
*/
|
||||
loadPageEvent(loaderId: string): void;
|
||||
/**
|
||||
* Fail navigation due to navigation command failed.
|
||||
*/
|
||||
failNavigation(navigation: NavigationState, errorText: string): void;
|
||||
/**
|
||||
* Updates the navigation's `loaderId` and sets it as current one, if it is a
|
||||
* cross-document navigation.
|
||||
*/
|
||||
navigationCommandFinished(navigation: NavigationState, loaderId?: string): void;
|
||||
frameStartedNavigating(url: string, loaderId: string, navigationType: string): void;
|
||||
/**
|
||||
* If there is a navigation with the loaderId equals to the network request id, it means
|
||||
* that the navigation failed.
|
||||
*/
|
||||
networkLoadingFailed(loaderId: string, errorText: string): void;
|
||||
}
|
||||
331
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/NavigationTracker.js
generated
vendored
Normal file
331
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/NavigationTracker.js
generated
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
"use strict";
|
||||
/*
|
||||
* Copyright 2024 Google LLC.
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.NavigationTracker = exports.NavigationState = exports.NavigationResult = void 0;
|
||||
const protocol_js_1 = require("../../../protocol/protocol.js");
|
||||
const Deferred_js_1 = require("../../../utils/Deferred.js");
|
||||
const log_js_1 = require("../../../utils/log.js");
|
||||
const time_js_1 = require("../../../utils/time.js");
|
||||
const urlHelpers_js_1 = require("../../../utils/urlHelpers.js");
|
||||
const uuid_js_1 = require("../../../utils/uuid.js");
|
||||
class NavigationResult {
|
||||
eventName;
|
||||
message;
|
||||
constructor(eventName, message) {
|
||||
this.eventName = eventName;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
exports.NavigationResult = NavigationResult;
|
||||
class NavigationState {
|
||||
navigationId = (0, uuid_js_1.uuidv4)();
|
||||
#browsingContextId;
|
||||
#started = false;
|
||||
#finished = new Deferred_js_1.Deferred();
|
||||
url;
|
||||
loaderId;
|
||||
#isInitial;
|
||||
#eventManager;
|
||||
committed = new Deferred_js_1.Deferred();
|
||||
isFragmentNavigation;
|
||||
get finished() {
|
||||
return this.#finished;
|
||||
}
|
||||
constructor(url, browsingContextId, isInitial, eventManager) {
|
||||
this.#browsingContextId = browsingContextId;
|
||||
this.url = url;
|
||||
this.#isInitial = isInitial;
|
||||
this.#eventManager = eventManager;
|
||||
}
|
||||
navigationInfo() {
|
||||
return {
|
||||
context: this.#browsingContextId,
|
||||
navigation: this.navigationId,
|
||||
timestamp: (0, time_js_1.getTimestamp)(),
|
||||
url: this.url,
|
||||
};
|
||||
}
|
||||
start() {
|
||||
if (
|
||||
// Initial navigation should not be reported.
|
||||
!this.#isInitial &&
|
||||
// No need in reporting started navigation twice.
|
||||
!this.#started &&
|
||||
// No need for reporting fragment navigations. Step 13 vs step 16 of the spec:
|
||||
// https://html.spec.whatwg.org/#beginning-navigation:webdriver-bidi-navigation-started
|
||||
!this.isFragmentNavigation) {
|
||||
this.#eventManager.registerEvent({
|
||||
type: 'event',
|
||||
method: protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.NavigationStarted,
|
||||
params: this.navigationInfo(),
|
||||
}, this.#browsingContextId);
|
||||
}
|
||||
this.#started = true;
|
||||
}
|
||||
#finish(navigationResult) {
|
||||
this.#started = true;
|
||||
if (!this.#isInitial &&
|
||||
!this.#finished.isFinished &&
|
||||
navigationResult.eventName !== "browsingContext.load" /* NavigationEventName.Load */) {
|
||||
this.#eventManager.registerEvent({
|
||||
type: 'event',
|
||||
method: navigationResult.eventName,
|
||||
params: this.navigationInfo(),
|
||||
}, this.#browsingContextId);
|
||||
}
|
||||
this.#finished.resolve(navigationResult);
|
||||
}
|
||||
frameNavigated() {
|
||||
this.committed.resolve();
|
||||
if (!this.#isInitial) {
|
||||
this.#eventManager.registerEvent({
|
||||
type: 'event',
|
||||
method: protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.NavigationCommitted,
|
||||
params: this.navigationInfo(),
|
||||
}, this.#browsingContextId);
|
||||
}
|
||||
}
|
||||
fragmentNavigated() {
|
||||
this.committed.resolve();
|
||||
this.#finish(new NavigationResult("browsingContext.fragmentNavigated" /* NavigationEventName.FragmentNavigated */));
|
||||
}
|
||||
load() {
|
||||
this.#finish(new NavigationResult("browsingContext.load" /* NavigationEventName.Load */));
|
||||
}
|
||||
fail(message) {
|
||||
this.#finish(new NavigationResult(this.committed.isFinished
|
||||
? "browsingContext.navigationAborted" /* NavigationEventName.NavigationAborted */
|
||||
: "browsingContext.navigationFailed" /* NavigationEventName.NavigationFailed */, message));
|
||||
}
|
||||
}
|
||||
exports.NavigationState = NavigationState;
|
||||
/**
|
||||
* Keeps track of navigations. Details: http://go/webdriver:bidi-navigation
|
||||
*/
|
||||
class NavigationTracker {
|
||||
#eventManager;
|
||||
#logger;
|
||||
#loaderIdToNavigationsMap = new Map();
|
||||
#browsingContextId;
|
||||
/**
|
||||
* Last committed navigation is committed, but is not guaranteed to be finished, as it
|
||||
* can still wait for `load` or `DOMContentLoaded` events.
|
||||
*/
|
||||
#lastCommittedNavigation;
|
||||
/**
|
||||
* Pending navigation is a navigation that is started but not yet committed.
|
||||
*/
|
||||
#pendingNavigation;
|
||||
// Flags if the initial navigation to `about:blank` is in progress.
|
||||
#isInitialNavigation = true;
|
||||
constructor(url, browsingContextId, eventManager, logger) {
|
||||
this.#browsingContextId = browsingContextId;
|
||||
this.#eventManager = eventManager;
|
||||
this.#logger = logger;
|
||||
this.#isInitialNavigation = true;
|
||||
// The initial navigation is always committed.
|
||||
this.#lastCommittedNavigation = new NavigationState(url, browsingContextId, (0, urlHelpers_js_1.urlMatchesAboutBlank)(url), this.#eventManager);
|
||||
}
|
||||
/**
|
||||
* Returns current started ongoing navigation. It can be either a started pending
|
||||
* navigation, or one is already navigated.
|
||||
*/
|
||||
get currentNavigationId() {
|
||||
if (this.#pendingNavigation?.isFragmentNavigation === false) {
|
||||
// Use pending navigation if it is started and it is not a fragment navigation.
|
||||
return this.#pendingNavigation.navigationId;
|
||||
}
|
||||
// If the pending navigation is a fragment one, or if it is not exists, the last
|
||||
// committed navigation should be used.
|
||||
return this.#lastCommittedNavigation.navigationId;
|
||||
}
|
||||
/**
|
||||
* Flags if the current navigation relates to the initial to `about:blank` navigation.
|
||||
*/
|
||||
get isInitialNavigation() {
|
||||
return this.#isInitialNavigation;
|
||||
}
|
||||
/**
|
||||
* Url of the last navigated navigation.
|
||||
*/
|
||||
get url() {
|
||||
return this.#lastCommittedNavigation.url;
|
||||
}
|
||||
/**
|
||||
* Creates a pending navigation e.g. when navigation command is called. Required to
|
||||
* provide navigation id before the actual navigation is started. It will be used when
|
||||
* navigation started. Can be aborted, failed, fragment navigated, or became a current
|
||||
* navigation.
|
||||
*/
|
||||
createPendingNavigation(url, canBeInitialNavigation = false) {
|
||||
this.#logger?.(log_js_1.LogType.debug, 'createCommandNavigation');
|
||||
this.#isInitialNavigation =
|
||||
canBeInitialNavigation &&
|
||||
this.#isInitialNavigation &&
|
||||
(0, urlHelpers_js_1.urlMatchesAboutBlank)(url);
|
||||
this.#pendingNavigation?.fail('navigation canceled by concurrent navigation');
|
||||
const navigation = new NavigationState(url, this.#browsingContextId, this.#isInitialNavigation, this.#eventManager);
|
||||
this.#pendingNavigation = navigation;
|
||||
return navigation;
|
||||
}
|
||||
dispose() {
|
||||
this.#pendingNavigation?.fail('navigation canceled by context disposal');
|
||||
this.#lastCommittedNavigation.fail('navigation canceled by context disposal');
|
||||
}
|
||||
// Update the current url.
|
||||
onTargetInfoChanged(url) {
|
||||
this.#logger?.(log_js_1.LogType.debug, `onTargetInfoChanged ${url}`);
|
||||
this.#lastCommittedNavigation.url = url;
|
||||
}
|
||||
#getNavigationForFrameNavigated(url, loaderId) {
|
||||
if (this.#loaderIdToNavigationsMap.has(loaderId)) {
|
||||
return this.#loaderIdToNavigationsMap.get(loaderId);
|
||||
}
|
||||
if (this.#pendingNavigation !== undefined &&
|
||||
this.#pendingNavigation.loaderId === undefined) {
|
||||
// This can be a pending navigation to `about:blank` created by a command. Use the
|
||||
// pending navigation in this case.
|
||||
return this.#pendingNavigation;
|
||||
}
|
||||
// Create a new pending navigation.
|
||||
return this.createPendingNavigation(url, true);
|
||||
}
|
||||
/**
|
||||
* @param {string} unreachableUrl indicated the navigation is actually failed.
|
||||
*/
|
||||
frameNavigated(url, loaderId, unreachableUrl) {
|
||||
this.#logger?.(log_js_1.LogType.debug, `frameNavigated ${url}`);
|
||||
if (unreachableUrl !== undefined) {
|
||||
// The navigation failed.
|
||||
const navigation = this.#loaderIdToNavigationsMap.get(loaderId) ??
|
||||
this.#pendingNavigation ??
|
||||
this.createPendingNavigation(unreachableUrl, true);
|
||||
navigation.url = unreachableUrl;
|
||||
navigation.start();
|
||||
navigation.fail('the requested url is unreachable');
|
||||
return;
|
||||
}
|
||||
const navigation = this.#getNavigationForFrameNavigated(url, loaderId);
|
||||
if (navigation !== this.#lastCommittedNavigation) {
|
||||
// Even though the `lastCommittedNavigation` is navigated, it still can be waiting
|
||||
// for `load` or `DOMContentLoaded` events.
|
||||
this.#lastCommittedNavigation.fail('navigation canceled by concurrent navigation');
|
||||
}
|
||||
navigation.url = url;
|
||||
navigation.loaderId = loaderId;
|
||||
this.#loaderIdToNavigationsMap.set(loaderId, navigation);
|
||||
navigation.start();
|
||||
navigation.frameNavigated();
|
||||
this.#lastCommittedNavigation = navigation;
|
||||
if (this.#pendingNavigation === navigation) {
|
||||
this.#pendingNavigation = undefined;
|
||||
}
|
||||
}
|
||||
navigatedWithinDocument(url, navigationType) {
|
||||
this.#logger?.(log_js_1.LogType.debug, `navigatedWithinDocument ${url}, ${navigationType}`);
|
||||
// Current navigation URL should be updated.
|
||||
this.#lastCommittedNavigation.url = url;
|
||||
if (navigationType !== 'fragment') {
|
||||
// TODO: check for other navigation types, like `javascript`.
|
||||
return;
|
||||
}
|
||||
// There is no way to map `navigatedWithinDocument` to a specific navigation. Consider
|
||||
// it is the pending navigation, if it is a fragment one.
|
||||
const fragmentNavigation = this.#pendingNavigation?.isFragmentNavigation === true
|
||||
? this.#pendingNavigation
|
||||
: new NavigationState(url, this.#browsingContextId, false, this.#eventManager);
|
||||
// Finish ongoing navigation.
|
||||
fragmentNavigation.fragmentNavigated();
|
||||
if (fragmentNavigation === this.#pendingNavigation) {
|
||||
this.#pendingNavigation = undefined;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Required to mark navigation as fully complete.
|
||||
* TODO: navigation should be complete when it became the current one on
|
||||
* `Page.frameNavigated` or on navigating command finished with a new loader Id.
|
||||
*/
|
||||
loadPageEvent(loaderId) {
|
||||
this.#logger?.(log_js_1.LogType.debug, 'loadPageEvent');
|
||||
// Even if it was an initial navigation, it is finished.
|
||||
this.#isInitialNavigation = false;
|
||||
this.#loaderIdToNavigationsMap.get(loaderId)?.load();
|
||||
}
|
||||
/**
|
||||
* Fail navigation due to navigation command failed.
|
||||
*/
|
||||
failNavigation(navigation, errorText) {
|
||||
this.#logger?.(log_js_1.LogType.debug, 'failCommandNavigation');
|
||||
navigation.fail(errorText);
|
||||
}
|
||||
/**
|
||||
* Updates the navigation's `loaderId` and sets it as current one, if it is a
|
||||
* cross-document navigation.
|
||||
*/
|
||||
navigationCommandFinished(navigation, loaderId) {
|
||||
this.#logger?.(log_js_1.LogType.debug, `finishCommandNavigation ${navigation.navigationId}, ${loaderId}`);
|
||||
if (loaderId !== undefined) {
|
||||
navigation.loaderId = loaderId;
|
||||
this.#loaderIdToNavigationsMap.set(loaderId, navigation);
|
||||
}
|
||||
navigation.isFragmentNavigation = loaderId === undefined;
|
||||
}
|
||||
frameStartedNavigating(url, loaderId, navigationType) {
|
||||
this.#logger?.(log_js_1.LogType.debug, `frameStartedNavigating ${url}, ${loaderId}`);
|
||||
if (this.#pendingNavigation &&
|
||||
this.#pendingNavigation?.loaderId !== undefined &&
|
||||
this.#pendingNavigation?.loaderId !== loaderId) {
|
||||
// If there is a pending navigation with loader id set, but not equal to the new
|
||||
// loader id, cancel pending navigation.
|
||||
this.#pendingNavigation?.fail('navigation canceled by concurrent navigation');
|
||||
this.#pendingNavigation = undefined;
|
||||
}
|
||||
if (this.#loaderIdToNavigationsMap.has(loaderId)) {
|
||||
const existingNavigation = this.#loaderIdToNavigationsMap.get(loaderId);
|
||||
// Navigation can be changed from `sameDocument` to `differentDocument`.
|
||||
existingNavigation.isFragmentNavigation =
|
||||
NavigationTracker.#isFragmentNavigation(navigationType);
|
||||
this.#pendingNavigation = existingNavigation;
|
||||
return;
|
||||
}
|
||||
const pendingNavigation = this.#pendingNavigation ?? this.createPendingNavigation(url, true);
|
||||
this.#loaderIdToNavigationsMap.set(loaderId, pendingNavigation);
|
||||
pendingNavigation.isFragmentNavigation =
|
||||
NavigationTracker.#isFragmentNavigation(navigationType);
|
||||
pendingNavigation.url = url;
|
||||
pendingNavigation.loaderId = loaderId;
|
||||
pendingNavigation.start();
|
||||
}
|
||||
static #isFragmentNavigation(navigationType) {
|
||||
// Page.frameStartedNavigating.navigationType can be one of the following values:
|
||||
// reload, reloadBypassingCache, restore, restoreWithPost, historySameDocument,
|
||||
// historyDifferentDocument, sameDocument, differentDocument.
|
||||
// https://chromedevtools.github.io/devtools-protocol/tot/Page/#event-frameStartedNavigating
|
||||
return ['historySameDocument', 'sameDocument'].includes(navigationType);
|
||||
}
|
||||
/**
|
||||
* If there is a navigation with the loaderId equals to the network request id, it means
|
||||
* that the navigation failed.
|
||||
*/
|
||||
networkLoadingFailed(loaderId, errorText) {
|
||||
this.#loaderIdToNavigationsMap.get(loaderId)?.fail(errorText);
|
||||
}
|
||||
}
|
||||
exports.NavigationTracker = NavigationTracker;
|
||||
//# sourceMappingURL=NavigationTracker.js.map
|
||||
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/NavigationTracker.js.map
generated
vendored
Normal file
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/context/NavigationTracker.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user