browser(firefox): reland "instrument all windows, support silent mode" with Linux fix (#1634)

This commit is contained in:
Yury Semikhatsky 2020-04-01 21:06:44 -07:00 committed by GitHub
parent c345cfee54
commit e76f8de474
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 135 additions and 82 deletions

View file

@ -1 +1 @@
1069 1070

View file

@ -2123,10 +2123,10 @@ index 0000000000000000000000000000000000000000..ba34976ad05e7f5f1a99777f76ac08b1
+this.SimpleChannel = SimpleChannel; +this.SimpleChannel = SimpleChannel;
diff --git a/juggler/TargetRegistry.js b/juggler/TargetRegistry.js diff --git a/juggler/TargetRegistry.js b/juggler/TargetRegistry.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff244ac0f6ac index 0000000000000000000000000000000000000000..9dd2c096cd9fa72ecda79a5ee56ef6176dc485d3
--- /dev/null --- /dev/null
+++ b/juggler/TargetRegistry.js +++ b/juggler/TargetRegistry.js
@@ -0,0 +1,492 @@ @@ -0,0 +1,572 @@
+const {EventEmitter} = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm'); +const {EventEmitter} = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); +const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
+const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js'); +const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
@ -2138,6 +2138,7 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+const {NetworkHandler} = ChromeUtils.import("chrome://juggler/content/protocol/NetworkHandler.js"); +const {NetworkHandler} = ChromeUtils.import("chrome://juggler/content/protocol/NetworkHandler.js");
+const {RuntimeHandler} = ChromeUtils.import("chrome://juggler/content/protocol/RuntimeHandler.js"); +const {RuntimeHandler} = ChromeUtils.import("chrome://juggler/content/protocol/RuntimeHandler.js");
+const {AccessibilityHandler} = ChromeUtils.import("chrome://juggler/content/protocol/AccessibilityHandler.js"); +const {AccessibilityHandler} = ChromeUtils.import("chrome://juggler/content/protocol/AccessibilityHandler.js");
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+ +
+const Cc = Components.classes; +const Cc = Components.classes;
+const Ci = Components.interfaces; +const Ci = Components.interfaces;
@ -2154,7 +2155,7 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+]; +];
+ +
+class TargetRegistry { +class TargetRegistry {
+ constructor(mainWindow) { + constructor() {
+ EventEmitter.decorate(this); + EventEmitter.decorate(this);
+ +
+ this._browserContextIdToBrowserContext = new Map(); + this._browserContextIdToBrowserContext = new Map();
@ -2170,22 +2171,20 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+ +
+ this._defaultContext = new BrowserContext(this, undefined, undefined); + this._defaultContext = new BrowserContext(this, undefined, undefined);
+ +
+ this._mainWindow = mainWindow;
+ this._targets = new Map(); + this._targets = new Map();
+
+ this._tabToTarget = new Map(); + this._tabToTarget = new Map();
+ Services.obs.addObserver(this, 'oop-frameloader-crashed');
+ +
+ for (const tab of this._mainWindow.gBrowser.tabs) + const onTabOpenListener = event => {
+ this._createTargetForTab(tab);
+ this._mainWindow.gBrowser.tabContainer.addEventListener('TabOpen', event => {
+ const target = this._createTargetForTab(event.target); + const target = this._createTargetForTab(event.target);
+ // If we come here, content will have juggler script from the start, + // If we come here, content will have juggler script from the start,
+ // and we should wait for initial navigation. + // and we should wait for initial navigation.
+ target._waitForInitialNavigation = true; + target._waitForInitialNavigation = true;
+ // For pages created before we attach to them, we don't wait for initial + // For pages created before we attach to them, we don't wait for initial
+ // navigation (target._waitForInitialNavigation is false by default). + // navigation (target._waitForInitialNavigation is false by default).
+ }); + };
+ this._mainWindow.gBrowser.tabContainer.addEventListener('TabClose', event => { +
+ const onTabCloseListener = event => {
+ const tab = event.target; + const tab = event.target;
+ const target = this._tabToTarget.get(tab); + const target = this._tabToTarget.get(tab);
+ if (!target) + if (!target)
@ -2194,8 +2193,32 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+ this._tabToTarget.delete(tab); + this._tabToTarget.delete(tab);
+ target.dispose(); + target.dispose();
+ this.emit(TargetRegistry.Events.TargetDestroyed, target); + this.emit(TargetRegistry.Events.TargetDestroyed, target);
+ }); + };
+ Services.obs.addObserver(this, 'oop-frameloader-crashed'); +
+ const wmListener = {
+ onOpenWindow: async window => {
+ const domWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
+ if (!(domWindow instanceof Ci.nsIDOMChromeWindow))
+ return;
+ await this._waitForWindowLoad(domWindow);
+ for (const tab of domWindow.gBrowser.tabs)
+ this._createTargetForTab(tab);
+ domWindow.gBrowser.tabContainer.addEventListener('TabOpen', onTabOpenListener);
+ domWindow.gBrowser.tabContainer.addEventListener('TabClose', onTabCloseListener);
+ },
+ onCloseWindow: window => {
+ const domWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
+ if (!(domWindow instanceof Ci.nsIDOMChromeWindow))
+ return;
+ if (!domWindow.gBrowser)
+ return;
+ domWindow.gBrowser.tabContainer.removeEventListener('TabOpen', onTabOpenListener);
+ domWindow.gBrowser.tabContainer.removeEventListener('TabClose', onTabCloseListener);
+ for (const tab of domWindow.gBrowser.tabs)
+ onTabCloseListener({ target: tab });
+ },
+ };
+ Services.wm.addListener(wmListener);
+ } + }
+ +
+ defaultContext() { + defaultContext() {
@ -2210,13 +2233,42 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+ return this._browserContextIdToBrowserContext.get(browserContextId); + return this._browserContextIdToBrowserContext.get(browserContextId);
+ } + }
+ +
+ async _waitForWindowLoad(window) {
+ if (window.document.readyState === 'complete')
+ return;
+ await new Promise(fulfill => {
+ window.addEventListener('load', function listener() {
+ window.removeEventListener('load', listener);
+ fulfill();
+ });
+ });
+ }
+
+ async newPage({browserContextId}) { + async newPage({browserContextId}) {
+ let window;
+ let created = false;
+ const windowsIt = Services.wm.getEnumerator('navigator:browser');
+ if (windowsIt.hasMoreElements()) {
+ window = windowsIt.getNext();
+ } else {
+ const features = "chrome,dialog=no,all";
+ const args = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+ args.data = 'about:blank';
+ window = Services.ww.openWindow(null, AppConstants.BROWSER_CHROME_URL, '_blank', features, args);
+ created = true;
+ }
+ await this._waitForWindowLoad(window);
+ const browserContext = this.browserContextForId(browserContextId); + const browserContext = this.browserContextForId(browserContextId);
+ const tab = this._mainWindow.gBrowser.addTab('about:blank', { + const tab = window.gBrowser.addTab('about:blank', {
+ userContextId: browserContext.userContextId, + userContextId: browserContext.userContextId,
+ triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+ }); + });
+ this._mainWindow.gBrowser.selectedTab = tab; + if (created) {
+ window.gBrowser.removeTab(window.gBrowser.getTabForBrowser(window.gBrowser.getBrowserAtIndex(0)), {
+ skipPermitUnload: true,
+ });
+ }
+ window.gBrowser.selectedTab = tab;
+ const target = this._tabToTarget.get(tab); + const target = this._tabToTarget.get(tab);
+ await target._contentReadyPromise; + await target._contentReadyPromise;
+ if (browserContext.options.timezoneId) { + if (browserContext.options.timezoneId) {
@ -2257,14 +2309,25 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+ return this._targets.get(targetId); + return this._targets.get(targetId);
+ } + }
+ +
+ _tabForBrowser(browser) {
+ // TODO: replace all of this with browser -> target map.
+ const windowsIt = Services.wm.getEnumerator('navigator:browser');
+ while (windowsIt.hasMoreElements()) {
+ const window = windowsIt.getNext();
+ const tab = window.gBrowser.getTabForBrowser(browser);
+ if (tab)
+ return { tab, gBrowser: window.gBrowser };
+ }
+ }
+
+ _targetForBrowser(browser) { + _targetForBrowser(browser) {
+ const tab = this._mainWindow.gBrowser.getTabForBrowser(browser); + const tab = this._tabForBrowser(browser);
+ return tab ? this._tabToTarget.get(tab) : undefined; + return tab ? this._tabToTarget.get(tab.tab) : undefined;
+ } + }
+ +
+ browserContextForBrowser(browser) { + browserContextForBrowser(browser) {
+ const tab = this._mainWindow.gBrowser.getTabForBrowser(browser); + const tab = this._tabForBrowser(browser);
+ return tab ? this._userContextIdToBrowserContext.get(tab.userContextId) : undefined; + return tab ? this._userContextIdToBrowserContext.get(tab.tab.userContextId) : undefined;
+ } + }
+ +
+ _createTargetForTab(tab) { + _createTargetForTab(tab) {
@ -2287,6 +2350,10 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+ if (!target) + if (!target)
+ return; + return;
+ target.emit('crashed'); + target.emit('crashed');
+ this._targets.delete(target.id());
+ this._tabToTarget.delete(target._tab);
+ target.dispose();
+ this.emit(TargetRegistry.Events.TargetDestroyed, target);
+ return; + return;
+ } + }
+ } + }
@ -2311,7 +2378,7 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+ this._eventListeners = [ + this._eventListeners = [
+ helper.addProgressListener(tab.linkedBrowser, navigationListener, Ci.nsIWebProgress.NOTIFY_LOCATION), + helper.addProgressListener(tab.linkedBrowser, navigationListener, Ci.nsIWebProgress.NOTIFY_LOCATION),
+ helper.addMessageListener(tab.linkedBrowser.messageManager, 'juggler:content-ready', { + helper.addMessageListener(tab.linkedBrowser.messageManager, 'juggler:content-ready', {
+ receiveMessage: () => this._onContentReady() + receiveMessage: message => this._onContentReady(message.data)
+ }), + }),
+ ]; + ];
+ +
@ -2357,7 +2424,8 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+ } + }
+ +
+ async close(runBeforeUnload = false) { + async close(runBeforeUnload = false) {
+ await this._registry._mainWindow.gBrowser.removeTab(this._tab, { + const tab = this._registry._tabForBrowser(this._tab.linkedBrowser);
+ await tab.gBrowser.removeTab(this._tab, {
+ skipPermitUnload: !runBeforeUnload, + skipPermitUnload: !runBeforeUnload,
+ }); + });
+ } + }
@ -2373,7 +2441,9 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+ networkHandler.enable(); + networkHandler.enable();
+ } + }
+ +
+ _onContentReady() { + _onContentReady({ userContextId }) {
+ // TODO: this is the earliest when userContextId is available.
+ // We should create target here, while listening to onContentReady for every tab.
+ const sessions = []; + const sessions = [];
+ const data = { sessions, target: this }; + const data = { sessions, target: this };
+ this._registry.emit(TargetRegistry.Events.PageTargetReady, data); + this._registry.emit(TargetRegistry.Events.PageTargetReady, data);
@ -2469,10 +2539,20 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+ } + }
+ } + }
+ +
+ destroy() { + async destroy() {
+ if (this.userContextId !== 0) { + if (this.userContextId !== 0) {
+ ContextualIdentityService.remove(this.userContextId); + ContextualIdentityService.remove(this.userContextId);
+ ContextualIdentityService.closeContainerTabs(this.userContextId); + ContextualIdentityService.closeContainerTabs(this.userContextId);
+ if (this.pages.size) {
+ await new Promise(f => {
+ const listener = helper.on(this._registry, TargetRegistry.Events.TargetDestroyed, () => {
+ if (!this.pages.size) {
+ helper.removeListeners([listener]);
+ f();
+ }
+ });
+ });
+ }
+ } + }
+ this._registry._browserContextIdToBrowserContext.delete(this.browserContextId); + this._registry._browserContextIdToBrowserContext.delete(this.browserContextId);
+ this._registry._userContextIdToBrowserContext.delete(this.userContextId); + this._registry._userContextIdToBrowserContext.delete(this.userContextId);
@ -2621,10 +2701,10 @@ index 0000000000000000000000000000000000000000..b74ea28f1ee7bbfeb6ea3fa9c5a4ff24
+this.TargetRegistry = TargetRegistry; +this.TargetRegistry = TargetRegistry;
diff --git a/juggler/components/juggler.js b/juggler/components/juggler.js diff --git a/juggler/components/juggler.js b/juggler/components/juggler.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..50617b19845d32006873c15b446afc04651cb6b7 index 0000000000000000000000000000000000000000..3477b09f25c90f2b5492d0dd9eb16d7496ec6480
--- /dev/null --- /dev/null
+++ b/juggler/components/juggler.js +++ b/juggler/components/juggler.js
@@ -0,0 +1,117 @@ @@ -0,0 +1,85 @@
+const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {Dispatcher} = ChromeUtils.import("chrome://juggler/content/protocol/Dispatcher.js"); +const {Dispatcher} = ChromeUtils.import("chrome://juggler/content/protocol/Dispatcher.js");
@ -2641,7 +2721,6 @@ index 0000000000000000000000000000000000000000..50617b19845d32006873c15b446afc04
+ +
+// Command Line Handler +// Command Line Handler
+function CommandLineHandler() { +function CommandLineHandler() {
+ this._port = -1;
+}; +};
+ +
+CommandLineHandler.prototype = { +CommandLineHandler.prototype = {
@ -2658,31 +2737,39 @@ index 0000000000000000000000000000000000000000..50617b19845d32006873c15b446afc04
+ const jugglerFlag = cmdLine.handleFlagWithParam("juggler", false); + const jugglerFlag = cmdLine.handleFlagWithParam("juggler", false);
+ if (!jugglerFlag || isNaN(jugglerFlag)) + if (!jugglerFlag || isNaN(jugglerFlag))
+ return; + return;
+ this._port = parseInt(jugglerFlag, 10); + const port = parseInt(jugglerFlag, 10);
+ Services.obs.addObserver(this, 'sessionstore-windows-restored'); + const silent = cmdLine.preventDefault;
+ }, + if (silent)
+ Services.startup.enterLastWindowClosingSurvivalArea();
+ +
+ observe: async function(subject, topic) { + const targetRegistry = new TargetRegistry();
+ Services.obs.removeObserver(this, 'sessionstore-windows-restored');
+
+ const win = await waitForBrowserWindow();
+ const targetRegistry = new TargetRegistry(win);
+ new NetworkObserver(targetRegistry); + new NetworkObserver(targetRegistry);
+ +
+ const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm"); + const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
+ const WebSocketServer = require('devtools/server/socket/websocket-server'); + const WebSocketServer = require('devtools/server/socket/websocket-server');
+ this._server = Cc["@mozilla.org/network/server-socket;1"].createInstance(Ci.nsIServerSocket); + this._server = Cc["@mozilla.org/network/server-socket;1"].createInstance(Ci.nsIServerSocket);
+ this._server.initSpecialConnection(this._port, Ci.nsIServerSocket.KeepWhenOffline | Ci.nsIServerSocket.LoopbackOnly, 4); + this._server.initSpecialConnection(port, Ci.nsIServerSocket.KeepWhenOffline | Ci.nsIServerSocket.LoopbackOnly, 4);
+ +
+ const token = helper.generateId(); + const token = helper.generateId();
+ +
+ let windowsRestoredCallback;
+ const windowsRestored = new Promise(fulfill => windowsRestoredCallback = fulfill);
+ const removeObserver = helper.addObserver(() => {
+ windowsRestoredCallback();
+ removeObserver();
+ }, "sessionstore-windows-restored");
+
+ this._server.asyncListen({ + this._server.asyncListen({
+ onSocketAccepted: async(socket, transport) => { + onSocketAccepted: async(socket, transport) => {
+ await windowsRestored;
+ const input = transport.openInputStream(0, 0, 0); + const input = transport.openInputStream(0, 0, 0);
+ const output = transport.openOutputStream(0, 0, 0); + const output = transport.openOutputStream(0, 0, 0);
+ const webSocket = await WebSocketServer.accept(transport, input, output, "/" + token); + const webSocket = await WebSocketServer.accept(transport, input, output, "/" + token);
+ const dispatcher = new Dispatcher(webSocket); + const dispatcher = new Dispatcher(webSocket);
+ const browserHandler = new BrowserHandler(dispatcher.rootSession(), dispatcher, targetRegistry); + const browserHandler = new BrowserHandler(dispatcher.rootSession(), dispatcher, targetRegistry, () => {
+ if (silent)
+ Services.startup.exitLastWindowClosingSurvivalArea();
+ });
+ dispatcher.rootSession().registerHandler('Browser', browserHandler); + dispatcher.rootSession().registerHandler('Browser', browserHandler);
+ } + }
+ }); + });
@ -2703,45 +2790,6 @@ index 0000000000000000000000000000000000000000..50617b19845d32006873c15b446afc04
+}; +};
+ +
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([CommandLineHandler]); +var NSGetFactory = XPCOMUtils.generateNSGetFactory([CommandLineHandler]);
+
+/**
+ * @return {!Promise<Ci.nsIDOMChromeWindow>}
+ */
+async function waitForBrowserWindow() {
+ const windowsIt = Services.wm.getEnumerator('navigator:browser');
+ if (windowsIt.hasMoreElements())
+ return waitForWindowLoaded(windowsIt.getNext());
+
+ 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);
+ });
+ });
+ }
+}
diff --git a/juggler/components/juggler.manifest b/juggler/components/juggler.manifest diff --git a/juggler/components/juggler.manifest b/juggler/components/juggler.manifest
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..50f8930207563e0d6b8a7878fc602dbca54d77fc index 0000000000000000000000000000000000000000..50f8930207563e0d6b8a7878fc602dbca54d77fc
@ -5031,10 +5079,10 @@ index 0000000000000000000000000000000000000000..3a386425d3796d0a6786dea193b3402d
+ +
diff --git a/juggler/content/main.js b/juggler/content/main.js diff --git a/juggler/content/main.js b/juggler/content/main.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..1864328a47107621309c9b3726bb84535b780c2f index 0000000000000000000000000000000000000000..e2a8fc14afe9b851e2bf3893691ca98c69bd12ee
--- /dev/null --- /dev/null
+++ b/juggler/content/main.js +++ b/juggler/content/main.js
@@ -0,0 +1,153 @@ @@ -0,0 +1,156 @@
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); +const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
+const {FrameTree} = ChromeUtils.import('chrome://juggler/content/content/FrameTree.js'); +const {FrameTree} = ChromeUtils.import('chrome://juggler/content/content/FrameTree.js');
@ -5094,7 +5142,10 @@ index 0000000000000000000000000000000000000000..1864328a47107621309c9b3726bb8453
+} +}
+ +
+function initialize() { +function initialize() {
+ let response = sendSyncMessage('juggler:content-ready', {})[0]; + const loadContext = docShell.QueryInterface(Ci.nsILoadContext);
+ const userContextId = loadContext.originAttributes.userContextId;
+
+ let response = sendSyncMessage('juggler:content-ready', { userContextId })[0];
+ if (!response) + if (!response)
+ response = { sessionIds: [], browserContextOptions: {}, waitForInitialNavigation: false }; + response = { sessionIds: [], browserContextOptions: {}, waitForInitialNavigation: false };
+ +
@ -5267,10 +5318,10 @@ index 0000000000000000000000000000000000000000..bf37558bccc48f4d90eadc971c1eb3e4
+this.AccessibilityHandler = AccessibilityHandler; +this.AccessibilityHandler = AccessibilityHandler;
diff --git a/juggler/protocol/BrowserHandler.js b/juggler/protocol/BrowserHandler.js diff --git a/juggler/protocol/BrowserHandler.js b/juggler/protocol/BrowserHandler.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..f63ee04a355e7426e13bd7095365bf44bb44648d index 0000000000000000000000000000000000000000..b4f0e856efb2331525c54c4d5ff124d83ea71ee6
--- /dev/null --- /dev/null
+++ b/juggler/protocol/BrowserHandler.js +++ b/juggler/protocol/BrowserHandler.js
@@ -0,0 +1,183 @@ @@ -0,0 +1,185 @@
+"use strict"; +"use strict";
+ +
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
@ -5280,7 +5331,7 @@ index 0000000000000000000000000000000000000000..f63ee04a355e7426e13bd7095365bf44
+const helper = new Helper(); +const helper = new Helper();
+ +
+class BrowserHandler { +class BrowserHandler {
+ constructor(session, dispatcher, targetRegistry) { + constructor(session, dispatcher, targetRegistry, onclose) {
+ this._session = session; + this._session = session;
+ this._dispatcher = dispatcher; + this._dispatcher = dispatcher;
+ this._targetRegistry = targetRegistry; + this._targetRegistry = targetRegistry;
@ -5289,6 +5340,7 @@ index 0000000000000000000000000000000000000000..f63ee04a355e7426e13bd7095365bf44
+ this._eventListeners = []; + this._eventListeners = [];
+ this._createdBrowserContextIds = new Set(); + this._createdBrowserContextIds = new Set();
+ this._attachedSessions = new Map(); + this._attachedSessions = new Map();
+ this._onclose = onclose;
+ } + }
+ +
+ async enable({attachToDefaultContext}) { + async enable({attachToDefaultContext}) {
@ -5326,8 +5378,8 @@ index 0000000000000000000000000000000000000000..f63ee04a355e7426e13bd7095365bf44
+ async removeBrowserContext({browserContextId}) { + async removeBrowserContext({browserContextId}) {
+ if (!this._enabled) + if (!this._enabled)
+ throw new Error('Browser domain is not enabled'); + throw new Error('Browser domain is not enabled');
+ await this._targetRegistry.browserContextForId(browserContextId).destroy();
+ this._createdBrowserContextIds.delete(browserContextId); + this._createdBrowserContextIds.delete(browserContextId);
+ this._targetRegistry.browserContextForId(browserContextId).destroy();
+ } + }
+ +
+ dispose() { + dispose() {
@ -5383,6 +5435,7 @@ index 0000000000000000000000000000000000000000..f63ee04a355e7426e13bd7095365bf44
+ } + }
+ +
+ async close() { + async close() {
+ this._onclose();
+ let browserWindow = Services.wm.getMostRecentWindow( + let browserWindow = Services.wm.getMostRecentWindow(
+ "navigator:browser" + "navigator:browser"
+ ); + );