add hw2
This commit is contained in:
396
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/cdp/CdpTarget.js
generated
vendored
Normal file
396
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/cdp/CdpTarget.js
generated
vendored
Normal file
@@ -0,0 +1,396 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CdpTarget = void 0;
|
||||
const chromium_bidi_js_1 = require("../../../protocol/chromium-bidi.js");
|
||||
const Deferred_js_1 = require("../../../utils/Deferred.js");
|
||||
const EventEmitter_js_1 = require("../../../utils/EventEmitter.js");
|
||||
const log_js_1 = require("../../../utils/log.js");
|
||||
const BrowsingContextImpl_js_1 = require("../context/BrowsingContextImpl.js");
|
||||
const LogManager_js_1 = require("../log/LogManager.js");
|
||||
class CdpTarget extends EventEmitter_js_1.EventEmitter {
|
||||
#id;
|
||||
#cdpClient;
|
||||
#browserCdpClient;
|
||||
#parentCdpClient;
|
||||
#realmStorage;
|
||||
#eventManager;
|
||||
#preloadScriptStorage;
|
||||
#browsingContextStorage;
|
||||
#prerenderingDisabled;
|
||||
#networkStorage;
|
||||
#unblocked = new Deferred_js_1.Deferred();
|
||||
#unhandledPromptBehavior;
|
||||
#logger;
|
||||
#deviceAccessEnabled = false;
|
||||
#cacheDisableState = false;
|
||||
#fetchDomainStages = {
|
||||
request: false,
|
||||
response: false,
|
||||
auth: false,
|
||||
};
|
||||
static create(targetId, cdpClient, browserCdpClient, parentCdpClient, realmStorage, eventManager, preloadScriptStorage, browsingContextStorage, networkStorage, prerenderingDisabled, unhandledPromptBehavior, logger) {
|
||||
const cdpTarget = new CdpTarget(targetId, cdpClient, browserCdpClient, parentCdpClient, eventManager, realmStorage, preloadScriptStorage, browsingContextStorage, networkStorage, prerenderingDisabled, unhandledPromptBehavior, logger);
|
||||
LogManager_js_1.LogManager.create(cdpTarget, realmStorage, eventManager, logger);
|
||||
cdpTarget.#setEventListeners();
|
||||
// No need to await.
|
||||
// Deferred will be resolved when the target is unblocked.
|
||||
void cdpTarget.#unblock();
|
||||
return cdpTarget;
|
||||
}
|
||||
constructor(targetId, cdpClient, browserCdpClient, parentCdpClient, eventManager, realmStorage, preloadScriptStorage, browsingContextStorage, networkStorage, prerenderingDisabled, unhandledPromptBehavior, logger) {
|
||||
super();
|
||||
this.#id = targetId;
|
||||
this.#cdpClient = cdpClient;
|
||||
this.#browserCdpClient = browserCdpClient;
|
||||
this.#parentCdpClient = parentCdpClient;
|
||||
this.#eventManager = eventManager;
|
||||
this.#realmStorage = realmStorage;
|
||||
this.#preloadScriptStorage = preloadScriptStorage;
|
||||
this.#networkStorage = networkStorage;
|
||||
this.#browsingContextStorage = browsingContextStorage;
|
||||
this.#prerenderingDisabled = prerenderingDisabled;
|
||||
this.#unhandledPromptBehavior = unhandledPromptBehavior;
|
||||
this.#logger = logger;
|
||||
}
|
||||
/** Returns a deferred that resolves when the target is unblocked. */
|
||||
get unblocked() {
|
||||
return this.#unblocked;
|
||||
}
|
||||
get id() {
|
||||
return this.#id;
|
||||
}
|
||||
get cdpClient() {
|
||||
return this.#cdpClient;
|
||||
}
|
||||
get parentCdpClient() {
|
||||
return this.#parentCdpClient;
|
||||
}
|
||||
get browserCdpClient() {
|
||||
return this.#browserCdpClient;
|
||||
}
|
||||
/** Needed for CDP escape path. */
|
||||
get cdpSessionId() {
|
||||
// SAFETY we got the client by it's id for creating
|
||||
return this.#cdpClient.sessionId;
|
||||
}
|
||||
/**
|
||||
* Enables all the required CDP domains and unblocks the target.
|
||||
*/
|
||||
async #unblock() {
|
||||
try {
|
||||
await Promise.all([
|
||||
this.#cdpClient.sendCommand('Page.enable'),
|
||||
// There can be some existing frames in the target, if reconnecting to an
|
||||
// existing browser instance, e.g. via Puppeteer. Need to restore the browsing
|
||||
// contexts for the frames to correctly handle further events, like
|
||||
// `Runtime.executionContextCreated`.
|
||||
// It's important to schedule this task together with enabling domains commands to
|
||||
// prepare the tree before the events (e.g. Runtime.executionContextCreated) start
|
||||
// coming.
|
||||
// https://github.com/GoogleChromeLabs/chromium-bidi/issues/2282
|
||||
this.#cdpClient
|
||||
.sendCommand('Page.getFrameTree')
|
||||
.then((frameTree) => this.#restoreFrameTreeState(frameTree.frameTree)),
|
||||
this.#cdpClient.sendCommand('Runtime.enable'),
|
||||
this.#cdpClient.sendCommand('Page.setLifecycleEventsEnabled', {
|
||||
enabled: true,
|
||||
}),
|
||||
this.#cdpClient
|
||||
.sendCommand('Page.setPrerenderingAllowed', {
|
||||
isAllowed: !this.#prerenderingDisabled,
|
||||
})
|
||||
.catch(() => {
|
||||
// Ignore CDP errors, as the command is not supported by iframe targets or
|
||||
// prerendered pages. Generic catch, as the error can vary between CdpClient
|
||||
// implementations: Tab vs Puppeteer.
|
||||
}),
|
||||
// Enabling CDP Network domain is required for navigation detection:
|
||||
// https://github.com/GoogleChromeLabs/chromium-bidi/issues/2856.
|
||||
this.#cdpClient
|
||||
.sendCommand('Network.enable')
|
||||
.then(() => this.toggleNetworkIfNeeded()),
|
||||
this.#cdpClient.sendCommand('Target.setAutoAttach', {
|
||||
autoAttach: true,
|
||||
waitForDebuggerOnStart: true,
|
||||
flatten: true,
|
||||
}),
|
||||
this.#initAndEvaluatePreloadScripts(),
|
||||
this.#cdpClient.sendCommand('Runtime.runIfWaitingForDebugger'),
|
||||
// Resume tab execution as well if it was paused by the debugger.
|
||||
this.#parentCdpClient.sendCommand('Runtime.runIfWaitingForDebugger'),
|
||||
this.toggleDeviceAccessIfNeeded(),
|
||||
]);
|
||||
}
|
||||
catch (error) {
|
||||
this.#logger?.(log_js_1.LogType.debugError, 'Failed to unblock target', error);
|
||||
// The target might have been closed before the initialization finished.
|
||||
if (!this.#cdpClient.isCloseError(error)) {
|
||||
this.#unblocked.resolve({
|
||||
kind: 'error',
|
||||
error,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.#unblocked.resolve({
|
||||
kind: 'success',
|
||||
value: undefined,
|
||||
});
|
||||
}
|
||||
#restoreFrameTreeState(frameTree) {
|
||||
const frame = frameTree.frame;
|
||||
const maybeContext = this.#browsingContextStorage.findContext(frame.id);
|
||||
if (maybeContext !== undefined) {
|
||||
// Restoring parent of already known browsing context. This means the target is
|
||||
// OOPiF and the BiDi session was connected to already existing browser instance.
|
||||
if (maybeContext.parentId === null &&
|
||||
frame.parentId !== null &&
|
||||
frame.parentId !== undefined) {
|
||||
maybeContext.parentId = frame.parentId;
|
||||
}
|
||||
}
|
||||
if (maybeContext === undefined && frame.parentId !== undefined) {
|
||||
// Restore not yet known nested frames. The top-level frame is created when the
|
||||
// target is attached.
|
||||
const parentBrowsingContext = this.#browsingContextStorage.getContext(frame.parentId);
|
||||
BrowsingContextImpl_js_1.BrowsingContextImpl.create(frame.id, frame.parentId, parentBrowsingContext.userContext, parentBrowsingContext.cdpTarget, this.#eventManager, this.#browsingContextStorage, this.#realmStorage, frame.url, undefined, this.#unhandledPromptBehavior, this.#logger);
|
||||
}
|
||||
frameTree.childFrames?.map((frameTree) => this.#restoreFrameTreeState(frameTree));
|
||||
}
|
||||
async toggleFetchIfNeeded() {
|
||||
const stages = this.#networkStorage.getInterceptionStages(this.topLevelId);
|
||||
if (this.#fetchDomainStages.request === stages.request &&
|
||||
this.#fetchDomainStages.response === stages.response &&
|
||||
this.#fetchDomainStages.auth === stages.auth) {
|
||||
return;
|
||||
}
|
||||
const patterns = [];
|
||||
this.#fetchDomainStages = stages;
|
||||
if (stages.request || stages.auth) {
|
||||
// CDP quirk we need request interception when we intercept auth
|
||||
patterns.push({
|
||||
urlPattern: '*',
|
||||
requestStage: 'Request',
|
||||
});
|
||||
}
|
||||
if (stages.response) {
|
||||
patterns.push({
|
||||
urlPattern: '*',
|
||||
requestStage: 'Response',
|
||||
});
|
||||
}
|
||||
if (patterns.length) {
|
||||
await this.#cdpClient.sendCommand('Fetch.enable', {
|
||||
patterns,
|
||||
handleAuthRequests: stages.auth,
|
||||
});
|
||||
}
|
||||
else {
|
||||
const blockedRequest = this.#networkStorage
|
||||
.getRequestsByTarget(this)
|
||||
.filter((request) => request.interceptPhase);
|
||||
void Promise.allSettled(blockedRequest.map((request) => request.waitNextPhase))
|
||||
.then(async () => {
|
||||
const blockedRequest = this.#networkStorage
|
||||
.getRequestsByTarget(this)
|
||||
.filter((request) => request.interceptPhase);
|
||||
if (blockedRequest.length) {
|
||||
return await this.toggleFetchIfNeeded();
|
||||
}
|
||||
return await this.#cdpClient.sendCommand('Fetch.disable');
|
||||
})
|
||||
.catch((error) => {
|
||||
this.#logger?.(log_js_1.LogType.bidi, 'Disable failed', error);
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Toggles CDP "Fetch" domain and enable/disable network cache.
|
||||
*/
|
||||
async toggleNetworkIfNeeded() {
|
||||
// Although the Network domain remains active, Fetch domain activation and caching
|
||||
// settings should be managed dynamically.
|
||||
try {
|
||||
await Promise.all([
|
||||
this.toggleSetCacheDisabled(),
|
||||
this.toggleFetchIfNeeded(),
|
||||
]);
|
||||
}
|
||||
catch (err) {
|
||||
this.#logger?.(log_js_1.LogType.debugError, err);
|
||||
if (!this.#isExpectedError(err)) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
async toggleSetCacheDisabled(disable) {
|
||||
const defaultCacheDisabled = this.#networkStorage.defaultCacheBehavior === 'bypass';
|
||||
const cacheDisabled = disable ?? defaultCacheDisabled;
|
||||
if (this.#cacheDisableState === cacheDisabled) {
|
||||
return;
|
||||
}
|
||||
this.#cacheDisableState = cacheDisabled;
|
||||
try {
|
||||
await this.#cdpClient.sendCommand('Network.setCacheDisabled', {
|
||||
cacheDisabled,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
this.#logger?.(log_js_1.LogType.debugError, err);
|
||||
this.#cacheDisableState = !cacheDisabled;
|
||||
if (!this.#isExpectedError(err)) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
async toggleDeviceAccessIfNeeded() {
|
||||
const enabled = this.isSubscribedTo(chromium_bidi_js_1.BiDiModule.Bluetooth);
|
||||
if (this.#deviceAccessEnabled === enabled) {
|
||||
return;
|
||||
}
|
||||
this.#deviceAccessEnabled = enabled;
|
||||
try {
|
||||
await this.#cdpClient.sendCommand(enabled ? 'DeviceAccess.enable' : 'DeviceAccess.disable');
|
||||
}
|
||||
catch (err) {
|
||||
this.#logger?.(log_js_1.LogType.debugError, err);
|
||||
this.#deviceAccessEnabled = !enabled;
|
||||
if (!this.#isExpectedError(err)) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Heuristic checking if the error is due to the session being closed. If so, ignore the
|
||||
* error.
|
||||
*/
|
||||
#isExpectedError(err) {
|
||||
const error = err;
|
||||
return ((error.code === -32001 &&
|
||||
error.message === 'Session with given id not found.') ||
|
||||
this.#cdpClient.isCloseError(err));
|
||||
}
|
||||
#setEventListeners() {
|
||||
this.#cdpClient.on('Network.requestWillBeSent', (eventParams) => {
|
||||
if (eventParams.loaderId === eventParams.requestId) {
|
||||
this.emit("frameStartedNavigating" /* TargetEvents.FrameStartedNavigating */, {
|
||||
loaderId: eventParams.loaderId,
|
||||
url: eventParams.request.url,
|
||||
frameId: eventParams.frameId,
|
||||
});
|
||||
}
|
||||
});
|
||||
this.#cdpClient.on('*', (event, params) => {
|
||||
// We may encounter uses for EventEmitter other than CDP events,
|
||||
// which we want to skip.
|
||||
if (typeof event !== 'string') {
|
||||
return;
|
||||
}
|
||||
this.#eventManager.registerEvent({
|
||||
type: 'event',
|
||||
method: `goog:cdp.${event}`,
|
||||
params: {
|
||||
event,
|
||||
params,
|
||||
session: this.cdpSessionId,
|
||||
},
|
||||
}, this.id);
|
||||
// Duplicate the event to the deprecated event name.
|
||||
// https://github.com/GoogleChromeLabs/chromium-bidi/issues/2844
|
||||
this.#eventManager.registerEvent({
|
||||
type: 'event',
|
||||
method: `cdp.${event}`,
|
||||
params: {
|
||||
event,
|
||||
params,
|
||||
session: this.cdpSessionId,
|
||||
},
|
||||
}, this.id);
|
||||
});
|
||||
}
|
||||
async #enableFetch(stages) {
|
||||
const patterns = [];
|
||||
if (stages.request || stages.auth) {
|
||||
// CDP quirk we need request interception when we intercept auth
|
||||
patterns.push({
|
||||
urlPattern: '*',
|
||||
requestStage: 'Request',
|
||||
});
|
||||
}
|
||||
if (stages.response) {
|
||||
patterns.push({
|
||||
urlPattern: '*',
|
||||
requestStage: 'Response',
|
||||
});
|
||||
}
|
||||
if (patterns.length) {
|
||||
const oldStages = this.#fetchDomainStages;
|
||||
this.#fetchDomainStages = stages;
|
||||
try {
|
||||
await this.#cdpClient.sendCommand('Fetch.enable', {
|
||||
patterns,
|
||||
handleAuthRequests: stages.auth,
|
||||
});
|
||||
}
|
||||
catch {
|
||||
this.#fetchDomainStages = oldStages;
|
||||
}
|
||||
}
|
||||
}
|
||||
async #disableFetch() {
|
||||
const blockedRequest = this.#networkStorage
|
||||
.getRequestsByTarget(this)
|
||||
.filter((request) => request.interceptPhase);
|
||||
if (blockedRequest.length === 0) {
|
||||
this.#fetchDomainStages = {
|
||||
request: false,
|
||||
response: false,
|
||||
auth: false,
|
||||
};
|
||||
await this.#cdpClient.sendCommand('Fetch.disable');
|
||||
}
|
||||
}
|
||||
async toggleNetwork() {
|
||||
const stages = this.#networkStorage.getInterceptionStages(this.topLevelId);
|
||||
const fetchEnable = Object.values(stages).some((value) => value);
|
||||
const fetchChanged = this.#fetchDomainStages.request !== stages.request ||
|
||||
this.#fetchDomainStages.response !== stages.response ||
|
||||
this.#fetchDomainStages.auth !== stages.auth;
|
||||
this.#logger?.(log_js_1.LogType.debugInfo, 'Toggle Network', `Fetch (${fetchEnable}) ${fetchChanged}`);
|
||||
if (fetchEnable && fetchChanged) {
|
||||
await this.#enableFetch(stages);
|
||||
}
|
||||
if (!fetchEnable && fetchChanged) {
|
||||
await this.#disableFetch();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* All the ProxyChannels from all the preload scripts of the given
|
||||
* BrowsingContext.
|
||||
*/
|
||||
getChannels() {
|
||||
return this.#preloadScriptStorage
|
||||
.find()
|
||||
.flatMap((script) => script.channels);
|
||||
}
|
||||
/** Loads all top-level preload scripts. */
|
||||
async #initAndEvaluatePreloadScripts() {
|
||||
await Promise.all(this.#preloadScriptStorage
|
||||
.find({
|
||||
// Needed for OOPIF
|
||||
targetId: this.topLevelId,
|
||||
global: true,
|
||||
})
|
||||
.map((script) => {
|
||||
return script.initInTarget(this, true);
|
||||
}));
|
||||
}
|
||||
get topLevelId() {
|
||||
return (this.#browsingContextStorage.findTopLevelContextId(this.id) ?? this.id);
|
||||
}
|
||||
isSubscribedTo(moduleOrEvent) {
|
||||
return this.#eventManager.subscriptionManager.isSubscribedTo(moduleOrEvent, this.topLevelId);
|
||||
}
|
||||
}
|
||||
exports.CdpTarget = CdpTarget;
|
||||
//# sourceMappingURL=CdpTarget.js.map
|
||||
Reference in New Issue
Block a user