browser(firefox): merge Target domain into Browser, rework default context attach (#1259)

This commit is contained in:
Dmitry Gozman 2020-03-06 14:58:35 -08:00 committed by GitHub
parent 119df5a985
commit 29f243056c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 158 additions and 227 deletions

View file

@ -1 +1 @@
1039 1040

View file

@ -458,10 +458,10 @@ index 9b667d3a4c29e71297dc0bd33bfe30ab670a9f36..0971b5ca7930cfd6d7ac6e21f7187718
nsCOMPtr<nsIPrincipal> principal = nsCOMPtr<nsIPrincipal> principal =
diff --git a/juggler/BrowserContextManager.js b/juggler/BrowserContextManager.js diff --git a/juggler/BrowserContextManager.js b/juggler/BrowserContextManager.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..dfb1f50b3a6ad915b99481c987975cb99c268ed7 index 0000000000000000000000000000000000000000..670ffa0bf10b3bdc98a732b740c41c217f0bc720
--- /dev/null --- /dev/null
+++ b/juggler/BrowserContextManager.js +++ b/juggler/BrowserContextManager.js
@@ -0,0 +1,194 @@ @@ -0,0 +1,199 @@
+"use strict"; +"use strict";
+ +
+const {ContextualIdentityService} = ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm"); +const {ContextualIdentityService} = ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm");
@ -507,6 +507,10 @@ index 0000000000000000000000000000000000000000..dfb1f50b3a6ad915b99481c987975cb9
+ this._defaultContext = new BrowserContext(this, undefined, undefined); + this._defaultContext = new BrowserContext(this, undefined, undefined);
+ } + }
+ +
+ defaultContext() {
+ return this._defaultContext;
+ }
+
+ createBrowserContext(options) { + createBrowserContext(options) {
+ return new BrowserContext(this, helper.generateId(), options); + return new BrowserContext(this, helper.generateId(), options);
+ } + }
@ -530,7 +534,8 @@ index 0000000000000000000000000000000000000000..dfb1f50b3a6ad915b99481c987975cb9
+ +
+ this._manager = manager; + this._manager = manager;
+ this.browserContextId = browserContextId; + this.browserContextId = browserContextId;
+ this.userContextId = undefined; + // Default context has userContextId === 0, but we pass undefined to many APIs just in case.
+ this.userContextId = 0;
+ if (browserContextId !== undefined) { + if (browserContextId !== undefined) {
+ const identity = ContextualIdentityService.create(IDENTITY_NAME + browserContextId); + const identity = ContextualIdentityService.create(IDENTITY_NAME + browserContextId);
+ this.userContextId = identity.userContextId; + this.userContextId = identity.userContextId;
@ -543,7 +548,7 @@ index 0000000000000000000000000000000000000000..dfb1f50b3a6ad915b99481c987975cb9
+ } + }
+ +
+ destroy() { + destroy() {
+ if (this.userContextId !== undefined) { + if (this.userContextId !== 0) {
+ ContextualIdentityService.remove(this.userContextId); + ContextualIdentityService.remove(this.userContextId);
+ ContextualIdentityService.closeContainerTabs(this.userContextId); + ContextualIdentityService.closeContainerTabs(this.userContextId);
+ } + }
@ -557,7 +562,7 @@ index 0000000000000000000000000000000000000000..dfb1f50b3a6ad915b99481c987975cb9
+ } + }
+ +
+ grantPermissions(origin, permissions) { + grantPermissions(origin, permissions) {
+ const attrs = {userContextId: this.userContextId}; + const attrs = { userContextId: this.userContextId || undefined };
+ const principal = Services.scriptSecurityManager.createContentPrincipal(NetUtil.newURI(origin), attrs); + const principal = Services.scriptSecurityManager.createContentPrincipal(NetUtil.newURI(origin), attrs);
+ this._principals.push(principal); + this._principals.push(principal);
+ for (const permission of ALL_PERMISSIONS) { + for (const permission of ALL_PERMISSIONS) {
@ -605,14 +610,14 @@ index 0000000000000000000000000000000000000000..dfb1f50b3a6ad915b99481c987975cb9
+ cookie.httpOnly || false, + cookie.httpOnly || false,
+ cookie.expires === undefined || cookie.expires === -1 /* isSession */, + cookie.expires === undefined || cookie.expires === -1 /* isSession */,
+ cookie.expires === undefined ? Date.now() + HUNDRED_YEARS : cookie.expires, + cookie.expires === undefined ? Date.now() + HUNDRED_YEARS : cookie.expires,
+ { userContextId: this.userContextId } /* originAttributes */, + { userContextId: this.userContextId || undefined } /* originAttributes */,
+ protocolToSameSite[cookie.sameSite], + protocolToSameSite[cookie.sameSite],
+ ); + );
+ } + }
+ } + }
+ +
+ clearCookies() { + clearCookies() {
+ Services.cookies.removeCookiesWithOriginAttributes(JSON.stringify({ userContextId: this.userContextId })); + Services.cookies.removeCookiesWithOriginAttributes(JSON.stringify({ userContextId: this.userContextId || undefined }));
+ } + }
+ +
+ getCookies() { + getCookies() {
@ -623,7 +628,7 @@ index 0000000000000000000000000000000000000000..dfb1f50b3a6ad915b99481c987975cb9
+ [Ci.nsICookie.SAMESITE_STRICT]: 'Strict', + [Ci.nsICookie.SAMESITE_STRICT]: 'Strict',
+ }; + };
+ for (let cookie of Services.cookies.cookies) { + for (let cookie of Services.cookies.cookies) {
+ if (cookie.originAttributes.userContextId !== (this.userContextId || 0)) + if (cookie.originAttributes.userContextId !== this.userContextId)
+ continue; + continue;
+ if (cookie.host === 'addons.mozilla.org') + if (cookie.host === 'addons.mozilla.org')
+ continue; + continue;
@ -1592,10 +1597,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..1bcbafed549090fe533fb1340b2598e5962b855d index 0000000000000000000000000000000000000000..98071c0e2c5429cfe8f29c390de3944f7f6e52a9
--- /dev/null --- /dev/null
+++ b/juggler/TargetRegistry.js +++ b/juggler/TargetRegistry.js
@@ -0,0 +1,264 @@ @@ -0,0 +1,250 @@
+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');
@ -1648,7 +1653,7 @@ index 0000000000000000000000000000000000000000..1bcbafed549090fe533fb1340b2598e5
+ this._targets.delete(target.id()); + this._targets.delete(target.id());
+ this._tabToTarget.delete(tab); + this._tabToTarget.delete(tab);
+ target.dispose(); + target.dispose();
+ this.emit(TargetRegistry.Events.TargetDestroyed, target.info()); + this.emit(TargetRegistry.Events.TargetDestroyed, target);
+ }); + });
+ Services.obs.addObserver(this, 'oop-frameloader-crashed'); + Services.obs.addObserver(this, 'oop-frameloader-crashed');
+ } + }
@ -1679,8 +1684,8 @@ index 0000000000000000000000000000000000000000..1bcbafed549090fe533fb1340b2598e5
+ }); + });
+ } + }
+ +
+ targetInfos() { + targets() {
+ return Array.from(this._targets.values()).map(target => target.info()); + return Array.from(this._targets.values());
+ } + }
+ +
+ targetInfo(targetId) { + targetInfo(targetId) {
@ -1731,7 +1736,7 @@ index 0000000000000000000000000000000000000000..1bcbafed549090fe533fb1340b2598e5
+ const target = new PageTarget(this, tab, this._contextManager.browserContextForUserContextId(tab.userContextId), openerTarget); + const target = new PageTarget(this, tab, this._contextManager.browserContextForUserContextId(tab.userContextId), openerTarget);
+ this._targets.set(target.id(), target); + this._targets.set(target.id(), target);
+ this._tabToTarget.set(tab, target); + this._tabToTarget.set(tab, target);
+ this.emit(TargetRegistry.Events.TargetCreated, target.info()); + this.emit(TargetRegistry.Events.TargetCreated, target);
+ return target; + return target;
+ } + }
+ +
@ -1756,15 +1761,9 @@ index 0000000000000000000000000000000000000000..1bcbafed549090fe533fb1340b2598e5
+ this._tab = tab; + this._tab = tab;
+ this._browserContext = browserContext; + this._browserContext = browserContext;
+ this._openerId = opener ? opener.id() : undefined; + this._openerId = opener ? opener.id() : undefined;
+ this._url = tab.linkedBrowser.currentURI.spec;
+ this._channel = SimpleChannel.createForMessageManager(`browser::page[${this._targetId}]`, tab.linkedBrowser.messageManager); + this._channel = SimpleChannel.createForMessageManager(`browser::page[${this._targetId}]`, tab.linkedBrowser.messageManager);
+ +
+ const navigationListener = {
+ QueryInterface: ChromeUtils.generateQI([ Ci.nsIWebProgressListener]),
+ onLocationChange: (aWebProgress, aRequest, aLocation) => this._onNavigated(aLocation),
+ };
+ this._eventListeners = [ + this._eventListeners = [
+ 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: () => this._onContentReady()
+ }), + }),
@ -1802,7 +1801,7 @@ index 0000000000000000000000000000000000000000..1bcbafed549090fe533fb1340b2598e5
+ +
+ _onContentReady() { + _onContentReady() {
+ const sessionIds = []; + const sessionIds = [];
+ const data = { sessionIds, targetInfo: this.info() }; + const data = { sessionIds, target: this };
+ this._registry.emit(TargetRegistry.Events.PageTargetReady, data); + this._registry.emit(TargetRegistry.Events.PageTargetReady, data);
+ this._contentReadyCallback(); + this._contentReadyCallback();
+ return { + return {
@ -1820,17 +1819,11 @@ index 0000000000000000000000000000000000000000..1bcbafed549090fe533fb1340b2598e5
+ return { + return {
+ targetId: this.id(), + targetId: this.id(),
+ type: 'page', + type: 'page',
+ url: this._url,
+ browserContextId: this._browserContext ? this._browserContext.browserContextId : undefined, + browserContextId: this._browserContext ? this._browserContext.browserContextId : undefined,
+ openerId: this._openerId, + openerId: this._openerId,
+ }; + };
+ } + }
+ +
+ _onNavigated(aLocation) {
+ this._url = aLocation.spec;
+ this._registry.emit(TargetRegistry.Events.TargetChanged, this.info());
+ }
+
+ dispose() { + dispose() {
+ helper.removeListeners(this._eventListeners); + helper.removeListeners(this._eventListeners);
+ } + }
@ -1845,7 +1838,6 @@ index 0000000000000000000000000000000000000000..1bcbafed549090fe533fb1340b2598e5
+ return { + return {
+ targetId: this.id(), + targetId: this.id(),
+ type: 'browser', + type: 'browser',
+ url: '',
+ } + }
+ } + }
+} +}
@ -1853,7 +1845,6 @@ index 0000000000000000000000000000000000000000..1bcbafed549090fe533fb1340b2598e5
+TargetRegistry.Events = { +TargetRegistry.Events = {
+ TargetCreated: Symbol('TargetRegistry.Events.TargetCreated'), + TargetCreated: Symbol('TargetRegistry.Events.TargetCreated'),
+ TargetDestroyed: Symbol('TargetRegistry.Events.TargetDestroyed'), + TargetDestroyed: Symbol('TargetRegistry.Events.TargetDestroyed'),
+ TargetChanged: Symbol('TargetRegistry.Events.TargetChanged'),
+ TargetCrashed: Symbol('TargetRegistry.Events.TargetCrashed'), + TargetCrashed: Symbol('TargetRegistry.Events.TargetCrashed'),
+ PageTargetReady: Symbol('TargetRegistry.Events.PageTargetReady'), + PageTargetReady: Symbol('TargetRegistry.Events.PageTargetReady'),
+}; +};
@ -4272,10 +4263,10 @@ index 0000000000000000000000000000000000000000..212f1c1a218efebe8685b019e79fb553
+initialize(); +initialize();
diff --git a/juggler/jar.mn b/juggler/jar.mn diff --git a/juggler/jar.mn b/juggler/jar.mn
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..cf3a7fa162cf22146a23521b8d31b6ac38de839e index 0000000000000000000000000000000000000000..e8a057109be8b328aefc3af26715c00689ecd6d8
--- /dev/null --- /dev/null
+++ b/juggler/jar.mn +++ b/juggler/jar.mn
@@ -0,0 +1,30 @@ @@ -0,0 +1,29 @@
+# This Source Code Form is subject to the terms of the Mozilla Public +# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this +# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
@ -4294,7 +4285,6 @@ index 0000000000000000000000000000000000000000..cf3a7fa162cf22146a23521b8d31b6ac
+ content/protocol/RuntimeHandler.js (protocol/RuntimeHandler.js) + content/protocol/RuntimeHandler.js (protocol/RuntimeHandler.js)
+ content/protocol/NetworkHandler.js (protocol/NetworkHandler.js) + content/protocol/NetworkHandler.js (protocol/NetworkHandler.js)
+ content/protocol/BrowserHandler.js (protocol/BrowserHandler.js) + content/protocol/BrowserHandler.js (protocol/BrowserHandler.js)
+ content/protocol/TargetHandler.js (protocol/TargetHandler.js)
+ content/protocol/AccessibilityHandler.js (protocol/AccessibilityHandler.js) + content/protocol/AccessibilityHandler.js (protocol/AccessibilityHandler.js)
+ content/content/main.js (content/main.js) + content/content/main.js (content/main.js)
+ content/content/FrameTree.js (content/FrameTree.js) + content/content/FrameTree.js (content/FrameTree.js)
@ -4352,10 +4342,10 @@ index 0000000000000000000000000000000000000000..2f2b7ca247f6b6dff396fb4b644654de
+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..677ff969135d9b9e1d094a1e32ba0ed5d5485a54 index 0000000000000000000000000000000000000000..060174f997f4a607c9e6d7bf4e1e204f07fe86c7
--- /dev/null --- /dev/null
+++ b/juggler/protocol/BrowserHandler.js +++ b/juggler/protocol/BrowserHandler.js
@@ -0,0 +1,88 @@ @@ -0,0 +1,163 @@
+"use strict"; +"use strict";
+ +
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
@ -4364,15 +4354,92 @@ index 0000000000000000000000000000000000000000..677ff969135d9b9e1d094a1e32ba0ed5
+); +);
+const {BrowserContextManager} = ChromeUtils.import("chrome://juggler/content/BrowserContextManager.js"); +const {BrowserContextManager} = ChromeUtils.import("chrome://juggler/content/BrowserContextManager.js");
+const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js"); +const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
+
+const helper = new Helper();
+ +
+class BrowserHandler { +class BrowserHandler {
+ /** + /**
+ * @param {ChromeSession} session + * @param {ChromeSession} session
+ */ + */
+ constructor() { + constructor(session) {
+ this._sweepingOverride = null; + this._session = session;
+ this._contextManager = BrowserContextManager.instance(); + this._contextManager = BrowserContextManager.instance();
+ this._targetRegistry = TargetRegistry.instance(); + this._targetRegistry = TargetRegistry.instance();
+ this._enabled = false;
+ this._attachToDefaultContext = false;
+ this._eventListeners = [];
+ this._createdBrowserContextIds = new Set();
+ }
+
+ async enable({attachToDefaultContext}) {
+ if (this._enabled)
+ return;
+ this._enabled = true;
+ this._attachToDefaultContext = attachToDefaultContext;
+
+ for (const target of this._targetRegistry.targets()) {
+ if (!this._shouldAttachToTarget(target))
+ continue;
+ const sessionId = this._session.dispatcher().createSession(target.id(), true /* shouldConnect */);
+ this._session.emitEvent('Browser.attachedToTarget', {
+ sessionId,
+ targetInfo: target.info()
+ });
+ }
+
+ this._eventListeners = [
+ helper.on(this._targetRegistry, TargetRegistry.Events.PageTargetReady, this._onPageTargetReady.bind(this)),
+ ];
+ }
+
+ async createBrowserContext(options) {
+ if (!this._enabled)
+ throw new Error('Browser domain is not enabled');
+ const browserContext = this._contextManager.createBrowserContext(options);
+ this._createdBrowserContextIds.add(browserContext.browserContextId);
+ return {browserContextId: browserContext.browserContextId};
+ }
+
+ async removeBrowserContext({browserContextId}) {
+ if (!this._enabled)
+ throw new Error('Browser domain is not enabled');
+ this._createdBrowserContextIds.delete(browserContextId);
+ this._contextManager.browserContextForId(browserContextId).destroy();
+ }
+
+ dispose() {
+ helper.removeListeners(this._eventListeners);
+ for (const browserContextId of this._createdBrowserContextIds) {
+ const browserContext = this._contextManager.browserContextForId(browserContextId);
+ if (browserContext.options.removeOnDetach)
+ browserContext.destroy();
+ }
+ this._createdBrowserContextIds.clear();
+ }
+
+ _shouldAttachToTarget(target) {
+ if (!target._browserContext)
+ return false;
+ if (this._createdBrowserContextIds.has(target._browserContext.browserContextId))
+ return true;
+ return this._attachToDefaultContext && target._browserContext === this._contextManager.defaultContext();
+ }
+
+ _onPageTargetReady({sessionIds, target}) {
+ if (!this._shouldAttachToTarget(target))
+ return;
+ const sessionId = this._session.dispatcher().createSession(target.id(), false /* shouldConnect */);
+ sessionIds.push(sessionId);
+ this._session.emitEvent('Browser.attachedToTarget', {
+ sessionId,
+ targetInfo: target.info()
+ });
+ }
+
+ async newPage({browserContextId}) {
+ const targetId = await this._targetRegistry.newPage({browserContextId});
+ return {targetId};
+ } + }
+ +
+ async close() { + async close() {
@ -4438,18 +4505,16 @@ index 0000000000000000000000000000000000000000..677ff969135d9b9e1d094a1e32ba0ed5
+ .userAgent; + .userAgent;
+ return {version: 'Firefox/' + version, userAgent}; + return {version: 'Firefox/' + version, userAgent};
+ } + }
+
+ dispose() { }
+} +}
+ +
+var EXPORTED_SYMBOLS = ['BrowserHandler']; +var EXPORTED_SYMBOLS = ['BrowserHandler'];
+this.BrowserHandler = BrowserHandler; +this.BrowserHandler = BrowserHandler;
diff --git a/juggler/protocol/Dispatcher.js b/juggler/protocol/Dispatcher.js diff --git a/juggler/protocol/Dispatcher.js b/juggler/protocol/Dispatcher.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..42e4622ed51b28ee6a5c48cc59c5400d42da002f index 0000000000000000000000000000000000000000..b75f20324cb582b6ad85bfe5e7e530ccb8111742
--- /dev/null --- /dev/null
+++ b/juggler/protocol/Dispatcher.js +++ b/juggler/protocol/Dispatcher.js
@@ -0,0 +1,197 @@ @@ -0,0 +1,194 @@
+const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js"); +const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
+const {protocol, checkScheme} = ChromeUtils.import("chrome://juggler/content/protocol/Protocol.js"); +const {protocol, checkScheme} = ChromeUtils.import("chrome://juggler/content/protocol/Protocol.js");
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); +const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
@ -4460,7 +4525,6 @@ index 0000000000000000000000000000000000000000..42e4622ed51b28ee6a5c48cc59c5400d
+ Page: ChromeUtils.import("chrome://juggler/content/protocol/PageHandler.js").PageHandler, + Page: ChromeUtils.import("chrome://juggler/content/protocol/PageHandler.js").PageHandler,
+ Network: ChromeUtils.import("chrome://juggler/content/protocol/NetworkHandler.js").NetworkHandler, + Network: ChromeUtils.import("chrome://juggler/content/protocol/NetworkHandler.js").NetworkHandler,
+ Browser: ChromeUtils.import("chrome://juggler/content/protocol/BrowserHandler.js").BrowserHandler, + Browser: ChromeUtils.import("chrome://juggler/content/protocol/BrowserHandler.js").BrowserHandler,
+ Target: ChromeUtils.import("chrome://juggler/content/protocol/TargetHandler.js").TargetHandler,
+ Runtime: ChromeUtils.import("chrome://juggler/content/protocol/RuntimeHandler.js").RuntimeHandler, + Runtime: ChromeUtils.import("chrome://juggler/content/protocol/RuntimeHandler.js").RuntimeHandler,
+ Accessibility: ChromeUtils.import("chrome://juggler/content/protocol/AccessibilityHandler.js").AccessibilityHandler, + Accessibility: ChromeUtils.import("chrome://juggler/content/protocol/AccessibilityHandler.js").AccessibilityHandler,
+}; +};
@ -4500,10 +4564,6 @@ index 0000000000000000000000000000000000000000..42e4622ed51b28ee6a5c48cc59c5400d
+ const chromeSession = new ChromeSession(this, sessionId, contentChannel, targetInfo); + const chromeSession = new ChromeSession(this, sessionId, contentChannel, targetInfo);
+ targetSessions.set(sessionId, chromeSession); + targetSessions.set(sessionId, chromeSession);
+ this._sessions.set(sessionId, chromeSession); + this._sessions.set(sessionId, chromeSession);
+ this._emitEvent(this._rootSession._sessionId, 'Target.attachedToTarget', {
+ sessionId: sessionId,
+ targetInfo
+ });
+ return sessionId; + return sessionId;
+ } + }
+ +
@ -4519,7 +4579,8 @@ index 0000000000000000000000000000000000000000..42e4622ed51b28ee6a5c48cc59c5400d
+ this._targetSessions.clear(); + this._targetSessions.clear();
+ } + }
+ +
+ _onTargetDestroyed({targetId}) { + _onTargetDestroyed(target) {
+ const targetId = target.id();
+ const sessions = this._targetSessions.get(targetId); + const sessions = this._targetSessions.get(targetId);
+ if (!sessions) + if (!sessions)
+ return; + return;
@ -4624,8 +4685,9 @@ index 0000000000000000000000000000000000000000..42e4622ed51b28ee6a5c48cc59c5400d
+ } + }
+ // Root session don't have sessionId and don't emit detachedFromTarget. + // Root session don't have sessionId and don't emit detachedFromTarget.
+ if (this._sessionId) { + if (this._sessionId) {
+ this._dispatcher._emitEvent(this._sessionId, 'Target.detachedFromTarget', { + this._dispatcher._emitEvent(this._dispatcher._rootSession._sessionId, 'Browser.detachedFromTarget', {
+ sessionId: this._sessionId, + sessionId: this._sessionId,
+ targetId: this.targetId(),
+ }); + });
+ } + }
+ } + }
@ -5318,25 +5380,23 @@ index 0000000000000000000000000000000000000000..78b6601b91d0b7fcda61114e6846aa07
+this.EXPORTED_SYMBOLS = ['t', 'checkScheme']; +this.EXPORTED_SYMBOLS = ['t', 'checkScheme'];
diff --git a/juggler/protocol/Protocol.js b/juggler/protocol/Protocol.js diff --git a/juggler/protocol/Protocol.js b/juggler/protocol/Protocol.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..dfb92200ddd508ab2dc3738c5b90750f6b1fdfaf index 0000000000000000000000000000000000000000..9f7f4ce40454257ff83e8d39b278c1dbc1353991
--- /dev/null --- /dev/null
+++ b/juggler/protocol/Protocol.js +++ b/juggler/protocol/Protocol.js
@@ -0,0 +1,772 @@ @@ -0,0 +1,747 @@
+const {t, checkScheme} = ChromeUtils.import('chrome://juggler/content/protocol/PrimitiveTypes.js'); +const {t, checkScheme} = ChromeUtils.import('chrome://juggler/content/protocol/PrimitiveTypes.js');
+ +
+// Protocol-specific types. +// Protocol-specific types.
+const targetTypes = {}; +const browserTypes = {};
+targetTypes.TargetInfo = { +
+ type: t.Enum(['page', 'browser']), +browserTypes.TargetInfo = {
+ type: t.Enum(['page']),
+ targetId: t.String, + targetId: t.String,
+ browserContextId: t.Optional(t.String), + browserContextId: t.Optional(t.String),
+ url: t.String,
+ // PageId of parent tab, if any. + // PageId of parent tab, if any.
+ openerId: t.Optional(t.String), + openerId: t.Optional(t.String),
+}; +};
+ +
+const browserTypes = {};
+
+browserTypes.CookieOptions = { +browserTypes.CookieOptions = {
+ name: t.String, + name: t.String,
+ value: t.String, + value: t.String,
@ -5506,11 +5566,50 @@ index 0000000000000000000000000000000000000000..dfb92200ddd508ab2dc3738c5b90750f
+const Browser = { +const Browser = {
+ targets: ['browser'], + targets: ['browser'],
+ +
+ events: {},
+
+ types: browserTypes, + types: browserTypes,
+ +
+ events: {
+ 'attachedToTarget': {
+ sessionId: t.String,
+ targetInfo: browserTypes.TargetInfo,
+ },
+ 'detachedFromTarget': {
+ sessionId: t.String,
+ targetId: t.String,
+ },
+ },
+
+ methods: { + methods: {
+ 'enable': {
+ params: {
+ attachToDefaultContext: t.Boolean,
+ },
+ },
+ 'createBrowserContext': {
+ params: {
+ removeOnDetach: t.Optional(t.Boolean),
+ userAgent: t.Optional(t.String),
+ bypassCSP: t.Optional(t.Boolean),
+ javaScriptDisabled: t.Optional(t.Boolean),
+ viewport: t.Optional(pageTypes.Viewport),
+ },
+ returns: {
+ browserContextId: t.String,
+ },
+ },
+ 'removeBrowserContext': {
+ params: {
+ browserContextId: t.String,
+ },
+ },
+ 'newPage': {
+ params: {
+ browserContextId: t.Optional(t.String),
+ },
+ returns: {
+ targetId: t.String,
+ }
+ },
+ 'close': {}, + 'close': {},
+ 'getInfo': { + 'getInfo': {
+ returns: { + returns: {
@ -5577,68 +5676,6 @@ index 0000000000000000000000000000000000000000..dfb92200ddd508ab2dc3738c5b90750f
+ }, + },
+}; +};
+ +
+const Target = {
+ targets: ['browser'],
+
+ types: targetTypes,
+
+ events: {
+ 'attachedToTarget': {
+ sessionId: t.String,
+ targetInfo: targetTypes.TargetInfo,
+ },
+ 'detachedFromTarget': {
+ sessionId: t.String,
+ },
+ 'targetCreated': targetTypes.TargetInfo,
+ 'targetDestroyed': targetTypes.TargetInfo,
+ 'targetInfoChanged': targetTypes.TargetInfo,
+ },
+
+ methods: {
+ // Start emitting tagOpened/tabClosed events
+ 'enable': {},
+ 'attachToTarget': {
+ params: {
+ targetId: t.String,
+ },
+ returns: {
+ sessionId: t.String,
+ },
+ },
+ 'newPage': {
+ params: {
+ browserContextId: t.Optional(t.String),
+ },
+ returns: {
+ targetId: t.String,
+ }
+ },
+ 'createBrowserContext': {
+ params: {
+ removeOnDetach: t.Optional(t.Boolean),
+ userAgent: t.Optional(t.String),
+ bypassCSP: t.Optional(t.Boolean),
+ javaScriptDisabled: t.Optional(t.Boolean),
+ viewport: t.Optional(pageTypes.Viewport),
+ },
+ returns: {
+ browserContextId: t.String,
+ },
+ },
+ 'removeBrowserContext': {
+ params: {
+ browserContextId: t.String,
+ },
+ },
+ 'getBrowserContexts': {
+ returns: {
+ browserContextIds: t.Array(t.String),
+ },
+ },
+ },
+};
+
+const Network = { +const Network = {
+ targets: ['page'], + targets: ['page'],
+ types: networkTypes, + types: networkTypes,
@ -6090,7 +6127,7 @@ index 0000000000000000000000000000000000000000..dfb92200ddd508ab2dc3738c5b90750f
+} +}
+ +
+this.protocol = { +this.protocol = {
+ domains: {Browser, Target, Page, Runtime, Network, Accessibility}, + domains: {Browser, Page, Runtime, Network, Accessibility},
+}; +};
+this.checkScheme = checkScheme; +this.checkScheme = checkScheme;
+this.EXPORTED_SYMBOLS = ['protocol', 'checkScheme']; +this.EXPORTED_SYMBOLS = ['protocol', 'checkScheme'];
@ -6152,112 +6189,6 @@ index 0000000000000000000000000000000000000000..5cc68241bdb420668fd14b45f1a70228
+ +
+var EXPORTED_SYMBOLS = ['RuntimeHandler']; +var EXPORTED_SYMBOLS = ['RuntimeHandler'];
+this.RuntimeHandler = RuntimeHandler; +this.RuntimeHandler = RuntimeHandler;
diff --git a/juggler/protocol/TargetHandler.js b/juggler/protocol/TargetHandler.js
new file mode 100644
index 0000000000000000000000000000000000000000..c0bab449971de13f993ac9825ac13368f8d8e226
--- /dev/null
+++ b/juggler/protocol/TargetHandler.js
@@ -0,0 +1,100 @@
+"use strict";
+
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
+const {BrowserContextManager} = ChromeUtils.import("chrome://juggler/content/BrowserContextManager.js");
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
+const helper = new Helper();
+
+class TargetHandler {
+ /**
+ * @param {ChromeSession} session
+ */
+ constructor(session) {
+ this._session = session;
+ this._contextManager = BrowserContextManager.instance();
+ this._targetRegistry = TargetRegistry.instance();
+ this._enabled = false;
+ this._eventListeners = [];
+ this._createdBrowserContextIds = new Set();
+ }
+
+ async attachToTarget({targetId}) {
+ if (!this._enabled)
+ throw new Error('Target domain is not enabled');
+ const sessionId = this._session.dispatcher().createSession(targetId, true /* shouldConnect */);
+ return {sessionId};
+ }
+
+ async createBrowserContext(options) {
+ if (!this._enabled)
+ throw new Error('Target domain is not enabled');
+ const browserContext = this._contextManager.createBrowserContext(options);
+ this._createdBrowserContextIds.add(browserContext.browserContextId);
+ return {browserContextId: browserContext.browserContextId};
+ }
+
+ async removeBrowserContext({browserContextId}) {
+ if (!this._enabled)
+ throw new Error('Target domain is not enabled');
+ this._createdBrowserContextIds.delete(browserContextId);
+ this._contextManager.browserContextForId(browserContextId).destroy();
+ }
+
+ async getBrowserContexts() {
+ const browserContexts = this._contextManager.getBrowserContexts();
+ return {browserContextIds: browserContexts.map(bc => bc.browserContextId)};
+ }
+
+ async enable() {
+ if (this._enabled)
+ return;
+ this._enabled = true;
+ for (const targetInfo of this._targetRegistry.targetInfos())
+ this._onTargetCreated(targetInfo);
+
+ this._eventListeners = [
+ helper.on(this._targetRegistry, TargetRegistry.Events.TargetCreated, this._onTargetCreated.bind(this)),
+ helper.on(this._targetRegistry, TargetRegistry.Events.TargetChanged, this._onTargetChanged.bind(this)),
+ helper.on(this._targetRegistry, TargetRegistry.Events.TargetDestroyed, this._onTargetDestroyed.bind(this)),
+ helper.on(this._targetRegistry, TargetRegistry.Events.PageTargetReady, this._onPageTargetReady.bind(this)),
+ ];
+ }
+
+ dispose() {
+ helper.removeListeners(this._eventListeners);
+ for (const browserContextId of this._createdBrowserContextIds) {
+ const browserContext = this._contextManager.browserContextForId(browserContextId);
+ if (browserContext.options.removeOnDetach)
+ browserContext.destroy();
+ }
+ this._createdBrowserContextIds.clear();
+ }
+
+ _onTargetCreated(targetInfo) {
+ this._session.emitEvent('Target.targetCreated', targetInfo);
+ }
+
+ _onTargetChanged(targetInfo) {
+ this._session.emitEvent('Target.targetInfoChanged', targetInfo);
+ }
+
+ _onTargetDestroyed(targetInfo) {
+ this._session.emitEvent('Target.targetDestroyed', targetInfo);
+ }
+
+ _onPageTargetReady({sessionIds, targetInfo}) {
+ if (!this._createdBrowserContextIds.has(targetInfo.browserContextId))
+ return;
+ const sessionId = this._session.dispatcher().createSession(targetInfo.targetId, false /* shouldConnect */);
+ sessionIds.push(sessionId);
+ }
+
+ async newPage({browserContextId}) {
+ const targetId = await this._targetRegistry.newPage({browserContextId});
+ return {targetId};
+ }
+}
+
+var EXPORTED_SYMBOLS = ['TargetHandler'];
+this.TargetHandler = TargetHandler;
diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
index d99e7f91503e84690d711bca2c2d916e34e56214..b63e9d96219420f5e4fb58797e4c3d901cba77cc 100644 index d99e7f91503e84690d711bca2c2d916e34e56214..b63e9d96219420f5e4fb58797e4c3d901cba77cc 100644
--- a/parser/html/nsHtml5TreeOpExecutor.cpp --- a/parser/html/nsHtml5TreeOpExecutor.cpp