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.
150 lines
4.9 KiB
JavaScript
150 lines
4.9 KiB
JavaScript
"use strict";
|
|
|
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
const {PageHandler} = ChromeUtils.import("chrome://juggler/content/PageHandler.jsm");
|
|
const {InsecureSweepingOverride} = ChromeUtils.import("chrome://juggler/content/InsecureSweepingOverride.js");
|
|
|
|
class BrowserHandler {
|
|
constructor(session) {
|
|
this._session = session;
|
|
this._mainWindowPromise = waitForBrowserWindow();
|
|
this._pageHandlers = new Map();
|
|
this._tabsToPageHandlers = new Map();
|
|
this._initializePages();
|
|
this._sweepingOverride = null;
|
|
}
|
|
|
|
async setIgnoreHTTPSErrors({enabled}) {
|
|
if (!enabled && this._sweepingOverride) {
|
|
this._sweepingOverride.unregister();
|
|
this._sweepingOverride = null;
|
|
Services.prefs.setBoolPref('security.mixed_content.block_active_content', true);
|
|
} else if (enabled && !this._sweepingOverride) {
|
|
this._sweepingOverride = new InsecureSweepingOverride();
|
|
this._sweepingOverride.register();
|
|
Services.prefs.setBoolPref('security.mixed_content.block_active_content', false);
|
|
}
|
|
}
|
|
|
|
async getInfo() {
|
|
const win = await this._mainWindowPromise;
|
|
const version = Components.classes["@mozilla.org/xre/app-info;1"]
|
|
.getService(Components.interfaces.nsIXULAppInfo)
|
|
.version;
|
|
const userAgent = Components.classes["@mozilla.org/network/protocol;1?name=http"]
|
|
.getService(Components.interfaces.nsIHttpProtocolHandler)
|
|
.userAgent;
|
|
return {version: 'Firefox/' + version, userAgent};
|
|
}
|
|
|
|
async _initializePages() {
|
|
const win = await this._mainWindowPromise;
|
|
const tabs = win.gBrowser.tabs;
|
|
for (const tab of win.gBrowser.tabs)
|
|
this._ensurePageHandler(tab);
|
|
win.gBrowser.tabContainer.addEventListener('TabOpen', event => {
|
|
this._ensurePageHandler(event.target);
|
|
});
|
|
win.gBrowser.tabContainer.addEventListener('TabClose', event => {
|
|
this._removePageHandlerForTab(event.target);
|
|
});
|
|
}
|
|
|
|
pageForId(pageId) {
|
|
return this._pageHandlers.get(pageId) || null;
|
|
}
|
|
|
|
_ensurePageHandler(tab) {
|
|
if (this._tabsToPageHandlers.has(tab))
|
|
return this._tabsToPageHandlers.get(tab);
|
|
const pageHandler = new PageHandler(this._session, tab);
|
|
this._pageHandlers.set(pageHandler.id(), pageHandler);
|
|
this._tabsToPageHandlers.set(tab, pageHandler);
|
|
this._session.emitEvent('Browser.tabOpened', {
|
|
url: pageHandler.url(),
|
|
pageId: pageHandler.id()
|
|
});
|
|
return pageHandler;
|
|
}
|
|
|
|
_removePageHandlerForTab(tab) {
|
|
const pageHandler = this._tabsToPageHandlers.get(tab);
|
|
this._tabsToPageHandlers.delete(tab);
|
|
this._pageHandlers.delete(pageHandler.id());
|
|
pageHandler.dispose();
|
|
this._session.emitEvent('Browser.tabClosed', {pageId: pageHandler.id()});
|
|
}
|
|
|
|
async newPage() {
|
|
const win = await this._mainWindowPromise;
|
|
const tab = win.gBrowser.addTab('about:blank', {
|
|
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
|
|
});
|
|
win.gBrowser.selectedTab = tab;
|
|
// Await navigation to about:blank
|
|
await new Promise(resolve => {
|
|
const wpl = {
|
|
onLocationChange: function(aWebProgress, aRequest, aLocation) {
|
|
tab.linkedBrowser.removeProgressListener(wpl);
|
|
resolve();
|
|
},
|
|
QueryInterface: ChromeUtils.generateQI([
|
|
Ci.nsIWebProgressListener,
|
|
Ci.nsISupportsWeakReference,
|
|
]),
|
|
};
|
|
tab.linkedBrowser.addProgressListener(wpl);
|
|
});
|
|
const pageHandler = this._ensurePageHandler(tab);
|
|
return {pageId: pageHandler.id()};
|
|
}
|
|
|
|
async closePage({pageId}) {
|
|
const win = await this._mainWindowPromise;
|
|
const pageHandler = this._pageHandlers.get(pageId);
|
|
await win.gBrowser.removeTab(pageHandler.tab());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return {!Promise<Ci.nsIDOMChromeWindow>}
|
|
*/
|
|
async function waitForBrowserWindow() {
|
|
const windowsIt = Services.wm.getEnumerator('navigator:browser');
|
|
if (windowsIt.hasMoreElements())
|
|
return waitForWindowLoaded(windowsIt.getNext().QueryInterface(Ci.nsIDOMChromeWindow));
|
|
|
|
let fulfill;
|
|
let promise = new Promise(x => fulfill = x);
|
|
|
|
const listener = {
|
|
onOpenWindow: window => {
|
|
if (window instanceof Ci.nsIDOMChromeWindow) {
|
|
Services.wm.removeListener(listener);
|
|
fulfill(waitForWindowLoaded(window));
|
|
}
|
|
},
|
|
onCloseWindow: () => {}
|
|
};
|
|
Services.wm.addListener(listener);
|
|
return promise;
|
|
|
|
/**
|
|
* @param {!Ci.nsIDOMChromeWindow} window
|
|
* @return {!Promise<Ci.nsIDOMChromeWindow>}
|
|
*/
|
|
function waitForWindowLoaded(window) {
|
|
if (window.document.readyState === 'complete')
|
|
return window;
|
|
return new Promise(fulfill => {
|
|
window.addEventListener('load', function listener() {
|
|
window.removeEventListener('load', listener);
|
|
fulfill(window);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
var EXPORTED_SYMBOLS = ['BrowserHandler'];
|
|
this.BrowserHandler = BrowserHandler;
|