45ab3e0332
This adds a proof-of-concept of `puppeteer-firefox`. This consists of two parts: - `//experimental/juggler` - patches to apply to Firefox. - `//experimental/puppeteer-firefox` - front-end code to be merged with Puppeteer. As things become more stable, we'll gradually move it out of the experimental folder.
172 lines
4.3 KiB
JavaScript
172 lines
4.3 KiB
JavaScript
const {helper} = require('./helper');
|
|
const {Page} = require('./Page');
|
|
const EventEmitter = require('events');
|
|
|
|
class Browser extends EventEmitter {
|
|
/**
|
|
* @param {!Puppeteer.Connection} connection
|
|
* @param {?Puppeteer.Viewport} defaultViewport
|
|
* @param {?Puppeteer.ChildProcess} process
|
|
* @param {function():void} closeCallback
|
|
*/
|
|
constructor(connection, defaultViewport, process, closeCallback) {
|
|
super();
|
|
this._connection = connection;
|
|
this._defaultViewport = defaultViewport;
|
|
this._process = process;
|
|
this._closeCallback = closeCallback;
|
|
|
|
/** @type {!Map<string, ?Target>} */
|
|
this._pageTargets = new Map();
|
|
|
|
this._eventListeners = [
|
|
helper.addEventListener(this._connection, 'Browser.tabOpened', this._onTabOpened.bind(this)),
|
|
helper.addEventListener(this._connection, 'Browser.tabClosed', this._onTabClosed.bind(this)),
|
|
helper.addEventListener(this._connection, 'Browser.tabNavigated', this._onTabNavigated.bind(this)),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return {!Promise<string>}
|
|
*/
|
|
async userAgent() {
|
|
const info = await this._connection.send('Browser.getInfo');
|
|
return info.userAgent;
|
|
}
|
|
|
|
/**
|
|
* @return {!Promise<string>}
|
|
*/
|
|
async version() {
|
|
const info = await this._connection.send('Browser.getInfo');
|
|
return info.version;
|
|
}
|
|
|
|
/**
|
|
* @return {?Puppeteer.ChildProcess}
|
|
*/
|
|
process() {
|
|
return this._process;
|
|
}
|
|
|
|
/**
|
|
* @param {function(!Target):boolean} predicate
|
|
* @param {{timeout?: number}=} options
|
|
* @return {!Promise<!Target>}
|
|
*/
|
|
async waitForTarget(predicate, options = {}) {
|
|
const {
|
|
timeout = 30000
|
|
} = options;
|
|
const existingTarget = this.targets().find(predicate);
|
|
if (existingTarget)
|
|
return existingTarget;
|
|
let resolve;
|
|
const targetPromise = new Promise(x => resolve = x);
|
|
this.on(Browser.Events.TargetCreated, check);
|
|
this.on('targetchanged', check);
|
|
try {
|
|
if (!timeout)
|
|
return await targetPromise;
|
|
return await helper.waitWithTimeout(targetPromise, 'target', timeout);
|
|
} finally {
|
|
this.removeListener(Browser.Events.TargetCreated, check);
|
|
this.removeListener('targetchanged', check);
|
|
}
|
|
|
|
/**
|
|
* @param {!Target} target
|
|
*/
|
|
function check(target) {
|
|
if (predicate(target))
|
|
resolve(target);
|
|
}
|
|
}
|
|
|
|
async newPage() {
|
|
const {pageId} = await this._connection.send('Browser.newPage');
|
|
const target = this._pageTargets.get(pageId);
|
|
return await target.page();
|
|
}
|
|
|
|
async pages() {
|
|
const pageTargets = Array.from(this._pageTargets.values());
|
|
return await Promise.all(pageTargets.map(target => target.page()));
|
|
}
|
|
|
|
targets() {
|
|
return Array.from(this._pageTargets.values());
|
|
}
|
|
|
|
_onTabOpened({pageId, url}) {
|
|
const target = new Target(this._connection, this, pageId, url);
|
|
this._pageTargets.set(pageId, target);
|
|
this.emit(Browser.Events.TargetCreated, target);
|
|
}
|
|
|
|
_onTabClosed({pageId}) {
|
|
const target = this._pageTargets.get(pageId);
|
|
this._pageTargets.delete(pageId);
|
|
this.emit(Browser.Events.TargetDestroyed, target);
|
|
}
|
|
|
|
_onTabNavigated({pageId, url}) {
|
|
const target = this._pageTargets.get(pageId);
|
|
target._url = url;
|
|
this.emit(Browser.Events.TargetChanged, target);
|
|
}
|
|
|
|
async close() {
|
|
helper.removeEventListeners(this._eventListeners);
|
|
await this._closeCallback();
|
|
}
|
|
}
|
|
|
|
/** @enum {string} */
|
|
Browser.Events = {
|
|
TargetCreated: 'targetcreated',
|
|
TargetChanged: 'targetchanged',
|
|
TargetDestroyed: 'targetdestroyed'
|
|
}
|
|
|
|
class Target {
|
|
/**
|
|
*
|
|
* @param {*} connection
|
|
* @param {!Browser} browser
|
|
* @param {string} pageId
|
|
* @param {string} url
|
|
*/
|
|
constructor(connection, browser, pageId, url) {
|
|
this._browser = browser;
|
|
this._connection = connection;
|
|
this._pageId = pageId;
|
|
/** @type {?Promise<!Page>} */
|
|
this._pagePromise = null;
|
|
this._url = url;
|
|
}
|
|
|
|
/**
|
|
* @return {"page"|"background_page"|"service_worker"|"other"|"browser"}
|
|
*/
|
|
type() {
|
|
return 'page';
|
|
}
|
|
|
|
url() {
|
|
return this._url;
|
|
}
|
|
|
|
async page() {
|
|
if (!this._pagePromise)
|
|
this._pagePromise = Page.create(this._connection, this, this._pageId, this._browser._defaultViewport);
|
|
return this._pagePromise;
|
|
}
|
|
|
|
browser() {
|
|
return this._browser;
|
|
}
|
|
}
|
|
|
|
module.exports = {Browser, Target};
|