Skip to content

Commit

Permalink
chore: wip waitForDebugger auto-attach
Browse files Browse the repository at this point in the history
  • Loading branch information
OrKoN committed Jun 15, 2022
1 parent ce0dd25 commit 5eaa531
Show file tree
Hide file tree
Showing 9 changed files with 657 additions and 128 deletions.
149 changes: 89 additions & 60 deletions src/common/Browser.ts
Expand Up @@ -17,13 +17,18 @@
import { ChildProcess } from 'child_process';
import { Protocol } from 'devtools-protocol';
import { assert } from './assert.js';
import { Connection, ConnectionEmittedEvents } from './Connection.js';
import {
CDPSession,
Connection,
ConnectionEmittedEvents,
} from './Connection.js';
import { EventEmitter } from './EventEmitter.js';
import { waitWithTimeout } from './util.js';
import { Page } from './Page.js';
import { Viewport } from './PuppeteerViewport.js';
import { Target } from './Target.js';
import { TaskQueue } from './TaskQueue.js';
import { TargetManager, TargetManagerEmittedEvents } from './TargetManager.js';

/**
* BrowserContext options.
Expand Down Expand Up @@ -237,7 +242,7 @@ export class Browser extends EventEmitter {
targetFilterCallback,
isPageTargetCallback
);
await connection.send('Target.setDiscoverTargets', { discover: true });
await browser._attach();
return browser;
}
#ignoreHTTPSErrors: boolean;
Expand All @@ -250,14 +255,13 @@ export class Browser extends EventEmitter {
#defaultContext: BrowserContext;
#contexts: Map<string, BrowserContext>;
#screenshotTaskQueue: TaskQueue;
#targets: Map<string, Target>;
#ignoredTargets = new Set<string>();
#targetManager: TargetManager;

/**
* @internal
*/
get _targets(): Map<string, Target> {
return this.#targets;
return this.#targetManager.attachedTargets();
}

/**
Expand Down Expand Up @@ -286,6 +290,11 @@ export class Browser extends EventEmitter {
return true;
});
this.#setIsPageTargetCallback(isPageTargetCallback);
this.#targetManager = new TargetManager(
connection,
this.#createTarget,
this.#targetFilterCallback
);

this.#defaultContext = new BrowserContext(this.#connection, this);
this.#contexts = new Map();
Expand All @@ -295,19 +304,48 @@ export class Browser extends EventEmitter {
new BrowserContext(this.#connection, this, contextId)
);
}
}

this.#targets = new Map();
/**
* @internal
*/
async _attach(): Promise<void> {
this.#connection.on(ConnectionEmittedEvents.Disconnected, () => {
return this.emit(BrowserEmittedEvents.Disconnected);
});
this.#connection.on('Target.targetCreated', this.#targetCreated.bind(this));
this.#connection.on(
'Target.targetDestroyed',
this.#targetDestroyed.bind(this)
this.#targetManager.on(
TargetManagerEmittedEvents.AttachedToTarget,
this.#onAttachedToTarget
);
this.#targetManager.on(
TargetManagerEmittedEvents.DetachedFromTarget,
this.#onDetachedFromTarget
);
this.#targetManager.on(
TargetManagerEmittedEvents.TargetChanged,
this.#onTargetChanged
);
await this.#targetManager.initialize();
}

/**
* @internal
*/
_detach(): void {
this.#connection.off(ConnectionEmittedEvents.Disconnected, () => {
return this.emit(BrowserEmittedEvents.Disconnected);
});
this.#targetManager.off(
TargetManagerEmittedEvents.AttachedToTarget,
this.#onAttachedToTarget
);
this.#targetManager.off(
TargetManagerEmittedEvents.DetachedFromTarget,
this.#onDetachedFromTarget
);
this.#connection.on(
'Target.targetInfoChanged',
this.#targetInfoChanged.bind(this)
this.#targetManager.off(
TargetManagerEmittedEvents.TargetChanged,
this.#onTargetChanged
);
}

Expand All @@ -319,6 +357,13 @@ export class Browser extends EventEmitter {
return this.#process ?? null;
}

/**
* @internal
*/
_targetManager(): TargetManager {
return this.#targetManager;
}

#setIsPageTargetCallback(isPageTargetCallback?: IsPageTargetCallback): void {
this.#isPageTargetCallback =
isPageTargetCallback ||
Expand Down Expand Up @@ -404,10 +449,10 @@ export class Browser extends EventEmitter {
this.#contexts.delete(contextId);
}

async #targetCreated(
event: Protocol.Target.TargetCreatedEvent
): Promise<void> {
const targetInfo = event.targetInfo;
#createTarget = (
targetInfo: Protocol.Target.TargetInfo,
session?: CDPSession
) => {
const { browserContextId } = targetInfo;
const context =
browserContextId && this.#contexts.has(browserContextId)
Expand All @@ -418,15 +463,11 @@ export class Browser extends EventEmitter {
throw new Error('Missing browser context');
}

const shouldAttachToTarget = this.#targetFilterCallback(targetInfo);
if (!shouldAttachToTarget) {
this.#ignoredTargets.add(targetInfo.targetId);
return;
}

const target = new Target(
return new Target(
targetInfo,
session,
context,
this.#targetManager,
() => {
return this.#connection.createSession(targetInfo);
},
Expand All @@ -435,59 +476,45 @@ export class Browser extends EventEmitter {
this.#screenshotTaskQueue,
this.#isPageTargetCallback
);
assert(
!this.#targets.has(event.targetInfo.targetId),
'Target should not exist before targetCreated'
);
this.#targets.set(event.targetInfo.targetId, target);
};

#onAttachedToTarget = async (target: Target) => {
if (await target._initializedPromise) {
this.emit(BrowserEmittedEvents.TargetCreated, target);
context.emit(BrowserContextEmittedEvents.TargetCreated, target);
target
.browserContext()
.emit(BrowserContextEmittedEvents.TargetCreated, target);
}
}
};

async #targetDestroyed(event: { targetId: string }): Promise<void> {
if (this.#ignoredTargets.has(event.targetId)) {
return;
}
const target = this.#targets.get(event.targetId);
if (!target) {
throw new Error(
`Missing target in _targetDestroyed (id = ${event.targetId})`
);
}
#onDetachedFromTarget = async (target: Target): Promise<void> => {
target._initializedCallback(false);
this.#targets.delete(event.targetId);
target._closedCallback();
if (await target._initializedPromise) {
this.emit(BrowserEmittedEvents.TargetDestroyed, target);
target
.browserContext()
.emit(BrowserContextEmittedEvents.TargetDestroyed, target);
}
}

#targetInfoChanged(event: Protocol.Target.TargetInfoChangedEvent): void {
if (this.#ignoredTargets.has(event.targetInfo.targetId)) {
return;
}
const target = this.#targets.get(event.targetInfo.targetId);
if (!target) {
throw new Error(
`Missing target in targetInfoChanged (id = ${event.targetInfo.targetId})`
);
}
};

#onTargetChanged = ({
target,
targetInfo,
}: {
target: Target;
targetInfo: Protocol.Target.TargetInfo;
}): void => {
const previousURL = target.url();
const wasInitialized = target._isInitialized;
target._targetInfoChanged(event.targetInfo);
target._targetInfoChanged(targetInfo);
if (wasInitialized && previousURL !== target.url()) {
this.emit(BrowserEmittedEvents.TargetChanged, target);
target
.browserContext()
.emit(BrowserContextEmittedEvents.TargetChanged, target);
}
}
};

/**
* The browser websocket endpoint which can be used as an argument to
Expand Down Expand Up @@ -526,7 +553,7 @@ export class Browser extends EventEmitter {
url: 'about:blank',
browserContextId: contextId || undefined,
});
const target = this.#targets.get(targetId);
const target = this.#targetManager.attachedTargets().get(targetId);
if (!target) {
throw new Error(`Missing target for page (id = ${targetId})`);
}
Expand All @@ -548,9 +575,11 @@ export class Browser extends EventEmitter {
* an array with all the targets in all browser contexts.
*/
targets(): Target[] {
return Array.from(this.#targets.values()).filter((target) => {
return target._isInitialized;
});
return Array.from(this.#targetManager.attachedTargets().values()).filter(
(target) => {
return target._isInitialized;
}
);
}

/**
Expand Down
36 changes: 12 additions & 24 deletions src/common/FrameManager.ts
Expand Up @@ -16,7 +16,7 @@

import { Protocol } from 'devtools-protocol';
import { assert } from './assert.js';
import { CDPSession, Connection } from './Connection.js';
import { CDPSession } from './Connection.js';
import { DOMWorld, WaitForSelectorOptions } from './DOMWorld.js';
import {
EvaluateFn,
Expand All @@ -37,6 +37,7 @@ import {
} from './LifecycleWatcher.js';
import { NetworkManager } from './NetworkManager.js';
import { Page } from './Page.js';
import { Target } from './Target.js';
import { TimeoutSettings } from './TimeoutSettings.js';
import { debugError, isErrorLike, isNumber, isString } from './util.js';

Expand Down Expand Up @@ -140,26 +141,17 @@ export class FrameManager extends EventEmitter {
session.on('Page.lifecycleEvent', (event) => {
this.#onLifecycleEvent(event);
});
session.on('Target.attachedToTarget', async (event) => {
this.#onAttachedToTarget(event);
});
session.on('Target.detachedFromTarget', async (event) => {
this.#onDetachedFromTarget(event);
});
session.on(
'Target.detachedFromTarget',
this.#onDetachedFromTarget.bind(this)
);
}

async initialize(client: CDPSession = this.#client): Promise<void> {
try {
const result = await Promise.all([
client.send('Page.enable'),
client.send('Page.getFrameTree'),
client !== this.#client
? client.send('Target.setAutoAttach', {
autoAttach: true,
waitForDebuggerOnStart: false,
flatten: true,
})
: Promise.resolve(),
]);

const { frameTree } = result[1];
Expand Down Expand Up @@ -275,21 +267,17 @@ export class FrameManager extends EventEmitter {
return await watcher.navigationResponse();
}

async #onAttachedToTarget(event: Protocol.Target.AttachedToTargetEvent) {
if (event.targetInfo.type !== 'iframe') {
async onAttachedToTarget(target: Target): Promise<void> {
if (target._getTargetInfo().type !== 'iframe') {
return;
}

const frame = this.#frames.get(event.targetInfo.targetId);
const connection = Connection.fromSession(this.#client);
assert(connection);
const session = connection.session(event.sessionId);
assert(session);
const frame = this.#frames.get(target._getTargetInfo().targetId);
if (frame) {
frame._updateClient(session);
frame._updateClient(target._session()!);
}
this.setupEventListeners(session);
await this.initialize(session);
this.setupEventListeners(target._session()!);
await this.initialize(target._session());
}

async #onDetachedFromTarget(event: Protocol.Target.DetachedFromTargetEvent) {
Expand Down

0 comments on commit 5eaa531

Please sign in to comment.