class Browser extends EventEmitter {
/**
* @param {!Puppeteer.Connection} connection
* @param {!Array<string>} contextIds
* @param {boolean} ignoreHTTPSErrors
* @param {?Puppeteer.Viewport} defaultViewport
* @param {?Puppeteer.ChildProcess} process
* @param {function()=} closeCallback
*/
static async create(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) {
const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback);
await connection.send('Target.setDiscoverTargets', {discover: true});
return browser;
}
/**
* @param {!Puppeteer.Connection} connection
* @param {!Array<string>} contextIds
* @param {boolean} ignoreHTTPSErrors
* @param {?Puppeteer.Viewport} defaultViewport
* @param {?Puppeteer.ChildProcess} process
* @param {(function():Promise)=} closeCallback
*/
constructor(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) {
super();
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
this._defaultViewport = defaultViewport;
this._process = process;
this._screenshotTaskQueue = new TaskQueue();
this._connection = connection;
this._closeCallback = closeCallback || new Function();
this._defaultContext = new BrowserContext(this._connection, this, null);
/** @type {Map<string, BrowserContext>} */
this._contexts = new Map();
for (const contextId of contextIds)
this._contexts.set(contextId, new BrowserContext(this._connection, this, contextId));
/** @type {Map<string, Target>} */
this._targets = new Map();
this._connection.on(Events.Connection.Disconnected, () => this.emit(Events.Browser.Disconnected));
this._connection.on('Target.targetCreated', this._targetCreated.bind(this));
this._connection.on('Target.targetDestroyed', this._targetDestroyed.bind(this));
this._connection.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this));
}
/**
* @return {?Puppeteer.ChildProcess}
*/
process() {
return this._process;
}
/**
* @return {!Promise<!BrowserContext>}
*/
async createIncognitoBrowserContext() {
const {browserContextId} = await this._connection.send('Target.createBrowserContext');
const context = new BrowserContext(this._connection, this, browserContextId);
this._contexts.set(browserContextId, context);
return context;
}
/**
* @return {!Array<!BrowserContext>}
*/
browserContexts() {
return [this._defaultContext, ...Array.from(this._contexts.values())];
}
/**
* @return {!BrowserContext}
*/
defaultBrowserContext() {
return this._defaultContext;
}
/**
* @param {?string} contextId
*/
async _disposeContext(contextId) {
await this._connection.send('Target.disposeBrowserContext', {browserContextId: contextId || undefined});
this._contexts.delete(contextId);
}
/**
* @param {!Protocol.Target.targetCreatedPayload} event
*/
async _targetCreated(event) {
const targetInfo = event.targetInfo;
const {browserContextId} = targetInfo;
const context = (browserContextId && this._contexts.has(browserContextId)) ? this._contexts.get(browserContextId) : this._defaultContext
;
const target = new Target(targetInfo, context, () => this._connection.createSession(targetInfo), this._ignoreHTTPSErrors, this
._defaultViewport, this._screenshotTaskQueue);
assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
this._targets.set(event.targetInfo.targetId, target);
if (await target._initializedPromise) {
this.emit(Events.Browser.TargetCreated, target);
context.emit(Events.BrowserContext.TargetCreated, target);
}
}
/**
* @param {{targetId: string}} event
*/
async _targetDestroyed(event) {
const target = this._targets.get(e...
n/a
class Connection extends EventEmitter {
/**
* @param {string} url
* @param {!Puppeteer.ConnectionTransport} transport
* @param {number=} delay
*/
constructor(url, transport, delay = 0) {
super();
this._url = url;
this._lastId = 0;
/** @type {!Map<number, {resolve: function, reject: function, error: !Error, method: string}>}*/
this._callbacks = new Map();
this._delay = delay;
this._transport = transport;
this._transport.onmessage = this._onMessage.bind(this);
this._transport.onclose = this._onClose.bind(this);
/** @type {!Map<string, !CDPSession>}*/
this._sessions = new Map();
this._closed = false;
}
/**
* @param {!CDPSession} session
* @return {!Connection}
*/
static fromSession(session) {
return session._connection;
}
/**
* @param {string} sessionId
* @return {?CDPSession}
*/
session(sessionId) {
return this._sessions.get(sessionId) || null;
}
/**
* @return {string}
*/
url() {
return this._url;
}
/**
* @param {string} method
* @param {!Object=} params
* @return {!Promise<?Object>}
*/
send(method, params = {}) {
const id = this._rawSend({method, params});
return new Promise((resolve, reject) => {
this._callbacks.set(id, {resolve, reject, error: new Error(), method});
});
}
/**
* @param {*} message
* @return {number}
*/
_rawSend(message) {
const id = ++this._lastId;
message = JSON.stringify(Object.assign({}, message, {id}));
debugProtocol('SEND ► ' + message);
this._transport.send(message);
return id;
}
/**
* @param {string} message
*/
async _onMessage(message) {
if (this._delay)
await new Promise(f => setTimeout(f, this._delay));
debugProtocol('◀ RECV ' + message);
const object = JSON.parse(message);
if (object.method === 'Target.attachedToTarget') {
const sessionId = object.params.sessionId;
const session = new CDPSession(this, object.params.targetInfo.type, sessionId);
this._sessions.set(sessionId, session);
} else if (object.method === 'Target.detachedFromTarget') {
const session = this._sessions.get(object.params.sessionId);
if (session) {
session._onClosed();
this._sessions.delete(object.params.sessionId);
}
}
if (object.sessionId) {
const session = this._sessions.get(object.sessionId);
if (session)
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() {
if (this._closed)
return;
this._closed = true;
this._transport.onmessage = 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())
session._onClosed();
this._sessions.clear();
this.emit(Events.Connection.Disconnected);
}
dispose() {
this._onClose();
this._transport.close();
}
/**
* @param {Protocol.Target.TargetInfo} targetInfo
* @return {!Promise<!CDPSession>}
*/
async createSession(targetInfo) {
const {sessionId} = await this.send('Target.attachToTarget', {targetId: targetInfo.targetId, flatten: true});
return this._sessions.get(sessionId);
}
}
n/a
class WebSocketTransport {
/**
* @param {string} url
* @return {!Promise<!WebSocketTransport>}
*/
static create(url) {
return new Promise((resolve, reject) => {
const ws = new WebSocket(url, [], {
perMessageDeflate: false,
maxPayload: 256 * 1024 * 1024, // 256Mb
});
ws.addEventListener('open', () => resolve(new WebSocketTransport(ws)));
ws.addEventListener('error', reject);
});
}
/**
* @param {!WebSocket} ws
*/
constructor(ws) {
this._ws = ws;
this._ws.addEventListener('message', event => {
if (this.onmessage)
this.onmessage.call(null, event.data);
});
this._ws.addEventListener('close', event => {
if (this.onclose)
this.onclose.call(null);
});
// Silently ignore all errors - we don't know what to do with them.
this._ws.addEventListener('error', () => {});
this.onmessage = null;
this.onclose = null;
}
/**
* @param {string} message
*/
send(message) {
this._ws.send(message);
}
close() {
this._ws.close();
}
}
n/a
assertThrow = function (passed, message) {
/*
* this function will throw err.<message> if <passed> is falsy
*/
var err;
if (passed) {
return;
}
err = (
(
message
&& typeof message.message === "string"
&& typeof message.stack === "string"
)
// if message is errObj, then leave as is
? message
: new Error(
typeof message === "string"
// if message is a string, then leave as is
? message
// else JSON.stringify message
: JSON.stringify(message, null, 4)
)
);
throw err;
}
n/a
functionOrNop = function (fnc) {
/*
* this function will if <fnc> exists,
* them return <fnc>,
* else return <nop>
*/
return fnc || local.nop;
}
n/a
identity = function (value) {
/*
* this function will return <value>
*/
return value;
}
n/a
nop = function () {
/*
* this function will do nothing
*/
return;
}
n/a
objectAssignDefault = function (target, source) {
/*
* this function will if items from <target> are
* null, undefined, or empty-string,
* then overwrite them with items from <source>
*/
target = target || {};
Object.keys(source || {}).forEach(function (key) {
if (
target[key] === null
|| target[key] === undefined
|| target[key] === ""
) {
target[key] = target[key] || source[key];
}
});
return target;
}
...
}).join(" ").replace((
/\u001b\[\d*m/g
), "") + "\n";
// scroll textarea to bottom
elem.scrollTop = elem.scrollHeight;
};
});
local.objectAssignDefault(local, globalThis.domOnEventDelegateDict);
globalThis.domOnEventDelegateDict = local;
local.onEventDomDb = local.db && local.db.onEventDomDb;
local.testRunBrowser = function (evt) {
/*
* this function will run browser-tests
*/
switch (
...