chore: remove connection / session redundancy (#51)
This commit is contained in:
parent
7783400156
commit
c26166203e
|
|
@ -47,15 +47,13 @@ export class Browser extends EventEmitter {
|
||||||
defaultViewport: Viewport | null,
|
defaultViewport: Viewport | null,
|
||||||
process: childProcess.ChildProcess | null,
|
process: childProcess.ChildProcess | null,
|
||||||
closeCallback?: (() => Promise<void>)) {
|
closeCallback?: (() => Promise<void>)) {
|
||||||
const session = await connection.createBrowserSession();
|
const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback);
|
||||||
const browser = new Browser(connection, session, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback);
|
await connection.rootSession.send('Target.setDiscoverTargets', { discover: true });
|
||||||
await session.send('Target.setDiscoverTargets', {discover: true});
|
|
||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
client: CDPSession,
|
|
||||||
contextIds: string[],
|
contextIds: string[],
|
||||||
ignoreHTTPSErrors: boolean,
|
ignoreHTTPSErrors: boolean,
|
||||||
defaultViewport: Viewport | null,
|
defaultViewport: Viewport | null,
|
||||||
|
|
@ -63,7 +61,7 @@ export class Browser extends EventEmitter {
|
||||||
closeCallback?: (() => Promise<void>)) {
|
closeCallback?: (() => Promise<void>)) {
|
||||||
super();
|
super();
|
||||||
this._connection = connection;
|
this._connection = connection;
|
||||||
this._client = client;
|
this._client = connection.rootSession;
|
||||||
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
|
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||||
this._defaultViewport = defaultViewport;
|
this._defaultViewport = defaultViewport;
|
||||||
this._process = process;
|
this._process = process;
|
||||||
|
|
@ -151,13 +149,17 @@ export class Browser extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _createPageInContext(contextId: string | null): Promise<Page> {
|
async _createPageInContext(contextId: string | null): Promise<Page> {
|
||||||
const {targetId} = await this._connection.send('Target.createTarget', {url: 'about:blank', browserContextId: contextId || undefined});
|
const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined });
|
||||||
const target = await this._targets.get(targetId);
|
const target = await this._targets.get(targetId);
|
||||||
assert(await target._initializedPromise, 'Failed to create target for page');
|
assert(await target._initializedPromise, 'Failed to create target for page');
|
||||||
const page = await target.page();
|
const page = await target.page();
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _closeTarget(target: Target) {
|
||||||
|
await this._client.send('Target.closeTarget', { targetId: target._targetId });
|
||||||
|
}
|
||||||
|
|
||||||
targets(): Target[] {
|
targets(): Target[] {
|
||||||
return Array.from(this._targets.values()).filter(target => target._isInitialized);
|
return Array.from(this._targets.values()).filter(target => target._isInitialized);
|
||||||
}
|
}
|
||||||
|
|
@ -222,6 +224,6 @@ export class Browser extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
_getVersion(): Promise<any> {
|
_getVersion(): Promise<any> {
|
||||||
return this._connection.send('Browser.getVersion');
|
return this._client.send('Browser.getVersion');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,16 +30,11 @@ export const ConnectionEvents = {
|
||||||
export class Connection extends EventEmitter {
|
export class Connection extends EventEmitter {
|
||||||
private _url: string;
|
private _url: string;
|
||||||
private _lastId = 0;
|
private _lastId = 0;
|
||||||
private _callbacks = new Map<number, {resolve:(o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
|
||||||
private _delay: number;
|
private _delay: number;
|
||||||
private _transport: ConnectionTransport;
|
private _transport: ConnectionTransport;
|
||||||
private _sessions = new Map<string, CDPSession>();
|
private _sessions = new Map<string, CDPSession>();
|
||||||
|
readonly rootSession: CDPSession;
|
||||||
_closed = false;
|
_closed = false;
|
||||||
on: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
|
||||||
addListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
|
||||||
off: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
|
||||||
removeListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
|
||||||
once: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
|
|
||||||
|
|
||||||
constructor(url: string, transport: ConnectionTransport, delay: number | undefined = 0) {
|
constructor(url: string, transport: ConnectionTransport, delay: number | undefined = 0) {
|
||||||
super();
|
super();
|
||||||
|
|
@ -49,6 +44,8 @@ export class Connection extends EventEmitter {
|
||||||
this._transport = transport;
|
this._transport = transport;
|
||||||
this._transport.onmessage = this._onMessage.bind(this);
|
this._transport.onmessage = this._onMessage.bind(this);
|
||||||
this._transport.onclose = this._onClose.bind(this);
|
this._transport.onclose = this._onClose.bind(this);
|
||||||
|
this.rootSession = new CDPSession(this, 'browser', '');
|
||||||
|
this._sessions.set('', this.rootSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromSession(session: CDPSession): Connection {
|
static fromSession(session: CDPSession): Connection {
|
||||||
|
|
@ -63,21 +60,14 @@ export class Connection extends EventEmitter {
|
||||||
return this._url;
|
return this._url;
|
||||||
}
|
}
|
||||||
|
|
||||||
send<T extends keyof Protocol.CommandParameters>(
|
_rawSend(sessionId: string, message: any): number {
|
||||||
method: T,
|
|
||||||
params?: Protocol.CommandParameters[T]
|
|
||||||
): Promise<Protocol.CommandReturnValues[T]> {
|
|
||||||
const id = this._rawSend({method, params});
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this._callbacks.set(id, {resolve, reject, error: new Error(), method});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_rawSend(message: any): number {
|
|
||||||
const id = ++this._lastId;
|
const id = ++this._lastId;
|
||||||
message = JSON.stringify(Object.assign({}, message, {id}));
|
message.id = id;
|
||||||
debugProtocol('SEND ► ' + message);
|
if (sessionId)
|
||||||
this._transport.send(message);
|
message.sessionId = sessionId;
|
||||||
|
const data = JSON.stringify(message);
|
||||||
|
debugProtocol('SEND ► ' + data);
|
||||||
|
this._transport.send(data);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,23 +87,9 @@ export class Connection extends EventEmitter {
|
||||||
this._sessions.delete(object.params.sessionId);
|
this._sessions.delete(object.params.sessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (object.sessionId) {
|
const session = this._sessions.get(object.sessionId || '');
|
||||||
const session = this._sessions.get(object.sessionId);
|
if (session)
|
||||||
if (session)
|
session._onMessage(object);
|
||||||
session._onMessage(object);
|
|
||||||
} else if (object.id) {
|
|
||||||
const callback = this._callbacks.get(object.id);
|
|
||||||
// Callbacks could be all rejected if someone has called `.dispose()`.
|
|
||||||
if (callback) {
|
|
||||||
this._callbacks.delete(object.id);
|
|
||||||
if (object.error)
|
|
||||||
callback.reject(createProtocolError(callback.error, callback.method, object));
|
|
||||||
else
|
|
||||||
callback.resolve(object.result);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.emit(object.method, object.params);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onClose() {
|
_onClose() {
|
||||||
|
|
@ -122,9 +98,6 @@ export class Connection extends EventEmitter {
|
||||||
this._closed = true;
|
this._closed = true;
|
||||||
this._transport.onmessage = null;
|
this._transport.onmessage = null;
|
||||||
this._transport.onclose = null;
|
this._transport.onclose = null;
|
||||||
for (const callback of this._callbacks.values())
|
|
||||||
callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): Target closed.`));
|
|
||||||
this._callbacks.clear();
|
|
||||||
for (const session of this._sessions.values())
|
for (const session of this._sessions.values())
|
||||||
session._onClosed();
|
session._onClosed();
|
||||||
this._sessions.clear();
|
this._sessions.clear();
|
||||||
|
|
@ -137,15 +110,13 @@ export class Connection extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSession(targetInfo: Protocol.Target.TargetInfo): Promise<CDPSession> {
|
async createSession(targetInfo: Protocol.Target.TargetInfo): Promise<CDPSession> {
|
||||||
const {sessionId} = await this.send('Target.attachToTarget', {targetId: targetInfo.targetId, flatten: true});
|
const { sessionId } = await this.rootSession.send('Target.attachToTarget', { targetId: targetInfo.targetId, flatten: true });
|
||||||
return this._sessions.get(sessionId);
|
return this._sessions.get(sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createBrowserSession(): Promise<CDPSession> {
|
async createBrowserSession(): Promise<CDPSession> {
|
||||||
const { sessionId } = await this.send('Target.attachToBrowserTarget');
|
const { sessionId } = await this.rootSession.send('Target.attachToBrowserTarget');
|
||||||
const session = new CDPSession(this, 'browser', sessionId);
|
return this._sessions.get(sessionId);
|
||||||
this._sessions.set(sessionId, session);
|
|
||||||
return session;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,7 +148,7 @@ export class CDPSession extends EventEmitter {
|
||||||
): Promise<Protocol.CommandReturnValues[T]> {
|
): Promise<Protocol.CommandReturnValues[T]> {
|
||||||
if (!this._connection)
|
if (!this._connection)
|
||||||
return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`));
|
return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the ${this._targetType} has been closed.`));
|
||||||
const id = this._connection._rawSend({sessionId: this._sessionId, method, params});
|
const id = this._connection._rawSend(this._sessionId, { method, params });
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this._callbacks.set(id, {resolve, reject, error: new Error(), method});
|
this._callbacks.set(id, {resolve, reject, error: new Error(), method});
|
||||||
});
|
});
|
||||||
|
|
@ -200,7 +171,7 @@ export class CDPSession extends EventEmitter {
|
||||||
async detach() {
|
async detach() {
|
||||||
if (!this._connection)
|
if (!this._connection)
|
||||||
throw new Error(`Session already detached. Most likely the ${this._targetType} has been closed.`);
|
throw new Error(`Session already detached. Most likely the ${this._targetType} has been closed.`);
|
||||||
await this._connection.send('Target.detachFromTarget', {sessionId: this._sessionId});
|
await this._connection.rootSession.send('Target.detachFromTarget', { sessionId: this._sessionId });
|
||||||
}
|
}
|
||||||
|
|
||||||
_onClosed() {
|
_onClosed() {
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ export class Launcher {
|
||||||
killChrome();
|
killChrome();
|
||||||
} else if (connection) {
|
} else if (connection) {
|
||||||
// Attempt to close chrome gracefully
|
// Attempt to close chrome gracefully
|
||||||
connection.send('Browser.close').catch(error => {
|
connection.rootSession.send('Browser.close').catch(error => {
|
||||||
debugError(error);
|
debugError(error);
|
||||||
killChrome();
|
killChrome();
|
||||||
});
|
});
|
||||||
|
|
@ -273,7 +273,7 @@ export class Launcher {
|
||||||
connection = new Connection(connectionURL, connectionTransport, slowMo);
|
connection = new Connection(connectionURL, connectionTransport, slowMo);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {browserContextIds} = await connection.send('Target.getBrowserContexts');
|
const { browserContextIds } = await connection.rootSession.send('Target.getBrowserContexts');
|
||||||
return Browser.create(connection, browserContextIds, ignoreHTTPSErrors, defaultViewport, null, () => connection.send('Browser.close').catch(debugError));
|
return Browser.create(connection, browserContextIds, ignoreHTTPSErrors, defaultViewport, null, () => connection.send('Browser.close').catch(debugError));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -678,7 +678,7 @@ export class Page extends EventEmitter {
|
||||||
if (runBeforeUnload) {
|
if (runBeforeUnload) {
|
||||||
await this._client.send('Page.close');
|
await this._client.send('Page.close');
|
||||||
} else {
|
} else {
|
||||||
await this._client._connection.send('Target.closeTarget', { targetId: this._target._targetId });
|
await this.browser()._closeTarget(this._target);
|
||||||
await this._target._isClosedPromise;
|
await this._target._isClosedPromise;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue