diff --git a/browser_patches/firefox/BUILD_NUMBER b/browser_patches/firefox/BUILD_NUMBER index 12fc4b2a28..05d04bfb36 100644 --- a/browser_patches/firefox/BUILD_NUMBER +++ b/browser_patches/firefox/BUILD_NUMBER @@ -1,2 +1,2 @@ -1178 -Changed: lushnikov@chromium.org Wed Sep 30 23:36:27 PDT 2020 +1179 +Changed: lushnikov@chromium.org Fri Oct 2 03:14:15 PDT 2020 diff --git a/browser_patches/firefox/juggler/SimpleChannel.js b/browser_patches/firefox/juggler/SimpleChannel.js index f8dd26a577..9cf9b75eb5 100644 --- a/browser_patches/firefox/juggler/SimpleChannel.js +++ b/browser_patches/firefox/juggler/SimpleChannel.js @@ -30,6 +30,7 @@ class SimpleChannel { this._connectorId = 0; this._pendingMessages = new Map(); this._handlers = new Map(); + this._bufferedRequests = []; this.transport = { sendMessage: null, dispose: null, @@ -70,6 +71,14 @@ class SimpleChannel { if (this._handlers.has(namespace)) throw new Error('ERROR: double-register for namespace ' + namespace); this._handlers.set(namespace, handler); + // Try to re-deliver all pending messages. + Promise.resolve().then(() => { + const bufferedRequests = this._bufferedRequests; + this._bufferedRequests = []; + for (const data of bufferedRequests) { + this._onMessage(data); + } + }); return () => this.unregister(namespace); } @@ -107,7 +116,7 @@ class SimpleChannel { const namespace = data.namespace; const handler = this._handlers.get(namespace); if (!handler) { - this.transport.sendMessage({responseId: data.requestId, error: `error in channel "${this._name}": No handler for namespace "${namespace}"`}); + this._bufferedRequests.push(data); return; } const method = handler[data.methodName]; diff --git a/browser_patches/firefox/juggler/TargetRegistry.js b/browser_patches/firefox/juggler/TargetRegistry.js index d3ebab6f89..7cb6660f6b 100644 --- a/browser_patches/firefox/juggler/TargetRegistry.js +++ b/browser_patches/firefox/juggler/TargetRegistry.js @@ -9,10 +9,6 @@ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm"); const {ContextualIdentityService} = ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm"); const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm'); -const {PageHandler} = ChromeUtils.import("chrome://juggler/content/protocol/PageHandler.js"); -const {NetworkHandler} = ChromeUtils.import("chrome://juggler/content/protocol/NetworkHandler.js"); -const {RuntimeHandler} = ChromeUtils.import("chrome://juggler/content/protocol/RuntimeHandler.js"); -const {AccessibilityHandler} = ChromeUtils.import("chrome://juggler/content/protocol/AccessibilityHandler.js"); const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); const helper = new Helper(); @@ -145,15 +141,10 @@ class TargetRegistry { if (!target) return; - const sessions = []; - const readyData = { sessions, target }; - target.markAsReported(); - this.emit(TargetRegistry.Events.TargetCreated, readyData); return { scriptsToEvaluateOnNewDocument: target.browserContext().scriptsToEvaluateOnNewDocument, bindings: target.browserContext().bindings, settings: target.browserContext().settings, - sessionIds: sessions.map(session => session.sessionId()), }; }, }); @@ -162,7 +153,7 @@ class TargetRegistry { const tab = event.target; const userContextId = tab.userContextId; const browserContext = this._userContextIdToBrowserContext.get(userContextId); - const hasExplicitSize = (appWindow.chromeFlags & Ci.nsIWebBrowserChrome.JUGGLER_WINDOW_EXPLICIT_SIZE) !== 0; + const hasExplicitSize = appWindow && (appWindow.chromeFlags & Ci.nsIWebBrowserChrome.JUGGLER_WINDOW_EXPLICIT_SIZE) !== 0; const openerContext = tab.linkedBrowser.browsingContext.opener; let openerTarget; if (openerContext) { @@ -191,9 +182,14 @@ class TargetRegistry { const domWindowTabListeners = new Map(); const onOpenWindow = async (appWindow) => { - if (!(appWindow instanceof Ci.nsIAppWindow)) - return; - const domWindow = appWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow); + + let domWindow; + if (appWindow instanceof Ci.nsIAppWindow) { + domWindow = appWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow); + } else { + domWindow = appWindow; + appWindow = null; + } if (!(domWindow instanceof Ci.nsIDOMChromeWindow)) return; // In persistent mode, window might be opened long ago and might be @@ -317,12 +313,11 @@ class TargetRegistry { if (await target.hasFailedToOverrideTimezone()) throw new Error('Failed to override timezone'); } - await target.reportedPromise(); return target.id(); } - reportedTargets() { - return Array.from(this._browserToTarget.values()).filter(pageTarget => pageTarget._isReported); + targets() { + return Array.from(this._browserToTarget.values()); } targetForBrowser(browser) { @@ -364,24 +359,12 @@ class PageTarget { helper.addProgressListener(tab.linkedBrowser, navigationListener, Ci.nsIWebProgress.NOTIFY_LOCATION), ]; - this._isReported = false; - this._reportedPromise = new Promise(resolve => { - this._reportedCallback = resolve; - }); - this._disposed = false; browserContext.pages.add(this); this._registry._browserToTarget.set(this._linkedBrowser, this); this._registry._browserBrowsingContextToTarget.set(this._linkedBrowser.browsingContext, this); - } - reportedPromise() { - return this._reportedPromise; - } - - markAsReported() { - this._isReported = true; - this._reportedCallback(); + this._registry.emit(TargetRegistry.Events.TargetCreated, this); } async windowReady() { @@ -411,7 +394,7 @@ class PageTarget { // Otherwise, explicitly set page viewport prevales over browser context // default viewport. const viewportSize = this._viewportSize || this._browserContext.defaultViewportSize; - const actualSize = setViewportSizeForBrowser(viewportSize, this._linkedBrowser, this._window); + const actualSize = await setViewportSizeForBrowser(viewportSize, this._linkedBrowser, this._window); await this._channel.connect('').send('awaitViewportDimensions', { width: actualSize.width, height: actualSize.height @@ -423,30 +406,14 @@ class PageTarget { await this.updateViewportSize(); } - connectSession(session) { - this._channel.connect('').send('attach', { sessionId: session.sessionId() }); - } - - disconnectSession(session) { - if (!this._disposed) - this._channel.connect('').emit('detach', { sessionId: session.sessionId() }); - } - async close(runBeforeUnload = false) { await this._gBrowser.removeTab(this._tab, { skipPermitUnload: !runBeforeUnload, }); } - initSession(session) { - const pageHandler = new PageHandler(this, session, this._channel); - const networkHandler = new NetworkHandler(this, session, this._channel); - session.registerHandler('Page', pageHandler); - session.registerHandler('Network', networkHandler); - session.registerHandler('Runtime', new RuntimeHandler(session, this._channel)); - session.registerHandler('Accessibility', new AccessibilityHandler(session, this._channel)); - pageHandler.enable(); - networkHandler.enable(); + channel() { + return this._channel; } id() { @@ -493,8 +460,7 @@ class PageTarget { this._registry._browserToTarget.delete(this._linkedBrowser); this._registry._browserBrowsingContextToTarget.delete(this._linkedBrowser.browsingContext); helper.removeListeners(this._eventListeners); - if (this._isReported) - this._registry.emit(TargetRegistry.Events.TargetDestroyed, this); + this._registry.emit(TargetRegistry.Events.TargetDestroyed, this); } } @@ -733,7 +699,8 @@ async function waitForWindowReady(window) { await helper.awaitEvent(window, 'load'); } -function setViewportSizeForBrowser(viewportSize, browser, window) { +async function setViewportSizeForBrowser(viewportSize, browser, window) { + await waitForWindowReady(window); if (viewportSize) { const {width, height} = viewportSize; const rect = browser.getBoundingClientRect(); diff --git a/browser_patches/firefox/juggler/content/PageAgent.js b/browser_patches/firefox/juggler/content/PageAgent.js index 172da22358..a600ab8af4 100644 --- a/browser_patches/firefox/juggler/content/PageAgent.js +++ b/browser_patches/firefox/juggler/content/PageAgent.js @@ -20,32 +20,31 @@ const obs = Cc["@mozilla.org/observer-service;1"].getService( const helper = new Helper(); class WorkerData { - constructor(pageAgent, browserChannel, sessionId, worker) { - this._workerRuntime = worker.channel().connect(sessionId + 'runtime'); - this._browserWorker = browserChannel.connect(sessionId + worker.id()); + constructor(pageAgent, browserChannel, worker) { + this._workerRuntime = worker.channel().connect('runtime'); + this._browserWorker = browserChannel.connect(worker.id()); this._worker = worker; - this._sessionId = sessionId; const emit = name => { return (...args) => this._browserWorker.emit(name, ...args); }; this._eventListeners = [ - worker.channel().register(sessionId + 'runtime', { + worker.channel().register('runtime', { runtimeConsole: emit('runtimeConsole'), runtimeExecutionContextCreated: emit('runtimeExecutionContextCreated'), runtimeExecutionContextDestroyed: emit('runtimeExecutionContextDestroyed'), }), - browserChannel.register(sessionId + worker.id(), { + browserChannel.register(worker.id(), { evaluate: (options) => this._workerRuntime.send('evaluate', options), callFunction: (options) => this._workerRuntime.send('callFunction', options), getObjectProperties: (options) => this._workerRuntime.send('getObjectProperties', options), disposeObject: (options) =>this._workerRuntime.send('disposeObject', options), }), ]; - worker.channel().connect('').emit('attach', {sessionId}); + worker.channel().connect('').emit('attach'); } dispose() { - this._worker.channel().connect('').emit('detach', {sessionId: this._sessionId}); + this._worker.channel().connect('').emit('detach'); this._workerRuntime.dispose(); this._browserWorker.dispose(); helper.removeListeners(this._eventListeners); @@ -112,12 +111,11 @@ class FrameData { } class PageAgent { - constructor(messageManager, browserChannel, sessionId, frameTree) { + constructor(messageManager, browserChannel, frameTree) { this._messageManager = messageManager; this._browserChannel = browserChannel; - this._sessionId = sessionId; - this._browserPage = browserChannel.connect(sessionId + 'page'); - this._browserRuntime = browserChannel.connect(sessionId + 'runtime'); + this._browserPage = browserChannel.connect('page'); + this._browserRuntime = browserChannel.connect('runtime'); this._frameTree = frameTree; this._runtime = frameTree.runtime(); @@ -127,7 +125,7 @@ class PageAgent { this._isolatedWorlds = new Map(); this._eventListeners = [ - browserChannel.register(sessionId + 'page', { + browserChannel.register('page', { addBinding: ({ name, script }) => this._frameTree.addBinding(name, script), addScriptToEvaluateOnNewDocument: this._addScriptToEvaluateOnNewDocument.bind(this), adoptNode: this._adoptNode.bind(this), @@ -152,7 +150,7 @@ class PageAgent { setFileInputFiles: this._setFileInputFiles.bind(this), setInterceptFileChooserDialog: this._setInterceptFileChooserDialog.bind(this), }), - browserChannel.register(sessionId + 'runtime', { + browserChannel.register('runtime', { evaluate: this._runtime.evaluate.bind(this._runtime), callFunction: this._runtime.callFunction.bind(this._runtime), getObjectProperties: this._runtime.getObjectProperties.bind(this._runtime), @@ -302,7 +300,7 @@ class PageAgent { } _onWorkerCreated(worker) { - const workerData = new WorkerData(this, this._browserChannel, this._sessionId, worker); + const workerData = new WorkerData(this, this._browserChannel, worker); this._workerData.set(worker.id(), workerData); this._browserPage.emit('pageWorkerCreated', { workerId: worker.id(), diff --git a/browser_patches/firefox/juggler/content/WorkerMain.js b/browser_patches/firefox/juggler/content/WorkerMain.js index 975c8e1d39..b78bc3923e 100644 --- a/browser_patches/firefox/juggler/content/WorkerMain.js +++ b/browser_patches/firefox/juggler/content/WorkerMain.js @@ -6,8 +6,6 @@ loadSubScript('chrome://juggler/content/content/Runtime.js'); loadSubScript('chrome://juggler/content/SimpleChannel.js'); -const runtimeAgents = new Map(); - const channel = new SimpleChannel('worker::worker'); const eventListener = event => channel._onMessage(JSON.parse(event.data)); this.addEventListener('message', eventListener); @@ -34,11 +32,11 @@ const runtime = new Runtime(true /* isWorker */); })(); class RuntimeAgent { - constructor(runtime, channel, sessionId) { + constructor(runtime, channel) { this._runtime = runtime; - this._browserRuntime = channel.connect(sessionId + 'runtime'); + this._browserRuntime = channel.connect('runtime'); this._eventListeners = [ - channel.register(sessionId + 'runtime', { + channel.register('runtime', { evaluate: this._runtime.evaluate.bind(this._runtime), callFunction: this._runtime.callFunction.bind(this._runtime), getObjectProperties: this._runtime.getObjectProperties.bind(this._runtime), @@ -72,15 +70,14 @@ class RuntimeAgent { } } +let runtimeAgent; + channel.register('', { - attach: ({sessionId}) => { - const runtimeAgent = new RuntimeAgent(runtime, channel, sessionId); - runtimeAgents.set(sessionId, runtimeAgent); + attach: () => { + runtimeAgent = new RuntimeAgent(runtime, channel); }, - detach: ({sessionId}) => { - const runtimeAgent = runtimeAgents.get(sessionId); - runtimeAgents.delete(sessionId); + detach: () => { runtimeAgent.dispose(); }, }); diff --git a/browser_patches/firefox/juggler/content/main.js b/browser_patches/firefox/juggler/content/main.js index 184096981b..6985964e2e 100644 --- a/browser_patches/firefox/juggler/content/main.js +++ b/browser_patches/firefox/juggler/content/main.js @@ -12,20 +12,7 @@ let frameTree; const helper = new Helper(); const messageManager = this; -const sessions = new Map(); - -function createContentSession(channel, sessionId) { - const pageAgent = new PageAgent(messageManager, channel, sessionId, frameTree); - sessions.set(sessionId, [pageAgent]); - pageAgent.enable(); -} - -function disposeContentSession(sessionId) { - const handlers = sessions.get(sessionId); - sessions.delete(sessionId); - for (const handler of handlers) - handler.dispose(); -} +let pageAgent; let failedToOverrideTimezone = false; @@ -89,6 +76,8 @@ const applySetting = { }, }; +const channel = SimpleChannel.createForMessageManager('content::page', messageManager); + function initialize() { const response = sendSyncMessage('juggler:content-ready')[0]; // If we didn't get a response, then we don't want to do anything @@ -96,7 +85,6 @@ function initialize() { if (!response) return; const { - sessionIds = [], scriptsToEvaluateOnNewDocument = [], bindings = [], settings = {} @@ -114,20 +102,10 @@ function initialize() { for (const { name, script } of bindings) frameTree.addBinding(name, script); - const channel = SimpleChannel.createForMessageManager('content::page', messageManager); - - for (const sessionId of sessionIds) - createContentSession(channel, sessionId); + pageAgent = new PageAgent(messageManager, channel, frameTree); + pageAgent.enable(); channel.register('', { - attach({sessionId}) { - createContentSession(channel, sessionId); - }, - - detach({sessionId}) { - disposeContentSession(sessionId); - }, - addScriptToEvaluateOnNewDocument(script) { frameTree.addScriptToEvaluateOnNewDocument(script); }, @@ -169,12 +147,9 @@ function initialize() { const gListeners = [ helper.addEventListener(messageManager, 'unload', msg => { helper.removeListeners(gListeners); - channel.dispose(); - - for (const sessionId of sessions.keys()) - disposeContentSession(sessionId); - + pageAgent.dispose(); frameTree.dispose(); + channel.dispose(); }), ]; } diff --git a/browser_patches/firefox/juggler/protocol/AccessibilityHandler.js b/browser_patches/firefox/juggler/protocol/AccessibilityHandler.js index dee87b10b2..8fe3e155fd 100644 --- a/browser_patches/firefox/juggler/protocol/AccessibilityHandler.js +++ b/browser_patches/firefox/juggler/protocol/AccessibilityHandler.js @@ -4,7 +4,7 @@ class AccessibilityHandler { constructor(session, contentChannel) { - this._contentPage = contentChannel.connect(session.sessionId() + 'page'); + this._contentPage = contentChannel.connect('page'); } async getFullAXTree(params) { diff --git a/browser_patches/firefox/juggler/protocol/BrowserHandler.js b/browser_patches/firefox/juggler/protocol/BrowserHandler.js index 07ba2567ab..d979c3765c 100644 --- a/browser_patches/firefox/juggler/protocol/BrowserHandler.js +++ b/browser_patches/firefox/juggler/protocol/BrowserHandler.js @@ -7,6 +7,10 @@ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js"); const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); +const {PageHandler} = ChromeUtils.import("chrome://juggler/content/protocol/PageHandler.js"); +const {NetworkHandler} = ChromeUtils.import("chrome://juggler/content/protocol/NetworkHandler.js"); +const {RuntimeHandler} = ChromeUtils.import("chrome://juggler/content/protocol/RuntimeHandler.js"); +const {AccessibilityHandler} = ChromeUtils.import("chrome://juggler/content/protocol/AccessibilityHandler.js"); const helper = new Helper(); @@ -29,19 +33,6 @@ class BrowserHandler { this._enabled = true; this._attachToDefaultContext = attachToDefaultContext; - for (const target of this._targetRegistry.reportedTargets()) { - if (!this._shouldAttachToTarget(target)) - continue; - const session = this._dispatcher.createSession(); - this._attachedSessions.set(target, session); - this._session.emitEvent('Browser.attachedToTarget', { - sessionId: session.sessionId(), - targetInfo: target.info() - }); - target.initSession(session); - target.connectSession(session); - } - this._eventListeners = [ helper.on(this._targetRegistry, TargetRegistry.Events.TargetCreated, this._onTargetCreated.bind(this)), helper.on(this._targetRegistry, TargetRegistry.Events.TargetDestroyed, this._onTargetDestroyed.bind(this)), @@ -54,6 +45,9 @@ class BrowserHandler { }; Services.obs.addObserver(onScreencastStopped, 'juggler-screencast-stopped'); this._eventListeners.push(() => Services.obs.removeObserver(onScreencastStopped, 'juggler-screencast-stopped')); + + for (const target of this._targetRegistry.targets()) + this._onTargetCreated(target); } async createBrowserContext({removeOnDetach}) { @@ -73,10 +67,8 @@ class BrowserHandler { dispose() { helper.removeListeners(this._eventListeners); - for (const [target, session] of this._attachedSessions) { - target.disconnectSession(session); + for (const [target, session] of this._attachedSessions) this._dispatcher.destroySession(session); - } this._attachedSessions.clear(); for (const browserContextId of this._createdBrowserContextIds) { const browserContext = this._targetRegistry.browserContextForId(browserContextId); @@ -87,24 +79,29 @@ class BrowserHandler { } _shouldAttachToTarget(target) { - if (!target._browserContext) - return false; if (this._createdBrowserContextIds.has(target._browserContext.browserContextId)) return true; return this._attachToDefaultContext && target._browserContext === this._targetRegistry.defaultContext(); } - _onTargetCreated({sessions, target}) { + _onTargetCreated(target) { if (!this._shouldAttachToTarget(target)) return; + const channel = target.channel(); const session = this._dispatcher.createSession(); this._attachedSessions.set(target, session); + const pageHandler = new PageHandler(target, session, channel); + const networkHandler = new NetworkHandler(target, session, channel); + session.registerHandler('Page', pageHandler); + session.registerHandler('Network', networkHandler); + session.registerHandler('Runtime', new RuntimeHandler(session, channel)); + session.registerHandler('Accessibility', new AccessibilityHandler(session, channel)); + pageHandler.enable(); + networkHandler.enable(); this._session.emitEvent('Browser.attachedToTarget', { sessionId: session.sessionId(), targetInfo: target.info() }); - target.initSession(session); - sessions.push(session); } _onTargetDestroyed(target) { diff --git a/browser_patches/firefox/juggler/protocol/PageHandler.js b/browser_patches/firefox/juggler/protocol/PageHandler.js index e5222d40a1..9bdb0d064d 100644 --- a/browser_patches/firefox/juggler/protocol/PageHandler.js +++ b/browser_patches/firefox/juggler/protocol/PageHandler.js @@ -17,7 +17,7 @@ const helper = new Helper(); class WorkerHandler { constructor(session, contentChannel, workerId) { this._session = session; - this._contentWorker = contentChannel.connect(session.sessionId() + workerId); + this._contentWorker = contentChannel.connect(workerId); this._workerId = workerId; const emitWrappedProtocolEvent = eventName => { @@ -30,7 +30,7 @@ class WorkerHandler { } this._eventListeners = [ - contentChannel.register(session.sessionId() + workerId, { + contentChannel.register(workerId, { runtimeConsole: emitWrappedProtocolEvent('Runtime.console'), runtimeExecutionContextCreated: emitWrappedProtocolEvent('Runtime.executionContextCreated'), runtimeExecutionContextDestroyed: emitWrappedProtocolEvent('Runtime.executionContextDestroyed'), @@ -59,7 +59,7 @@ class PageHandler { constructor(target, session, contentChannel) { this._session = session; this._contentChannel = contentChannel; - this._contentPage = contentChannel.connect(session.sessionId() + 'page'); + this._contentPage = contentChannel.connect('page'); this._workers = new Map(); const emitProtocolEvent = eventName => { @@ -67,7 +67,7 @@ class PageHandler { } this._eventListeners = [ - contentChannel.register(session.sessionId() + 'page', { + contentChannel.register('page', { pageBindingCalled: emitProtocolEvent('Page.bindingCalled'), pageDispatchMessageFromWorker: emitProtocolEvent('Page.dispatchMessageFromWorker'), pageEventFired: emitProtocolEvent('Page.eventFired'), diff --git a/browser_patches/firefox/juggler/protocol/RuntimeHandler.js b/browser_patches/firefox/juggler/protocol/RuntimeHandler.js index 6082a68935..cffa05933a 100644 --- a/browser_patches/firefox/juggler/protocol/RuntimeHandler.js +++ b/browser_patches/firefox/juggler/protocol/RuntimeHandler.js @@ -14,15 +14,14 @@ const helper = new Helper(); class RuntimeHandler { constructor(session, contentChannel) { - const sessionId = session.sessionId(); - this._contentRuntime = contentChannel.connect(sessionId + 'runtime'); + this._contentRuntime = contentChannel.connect('runtime'); const emitProtocolEvent = eventName => { return (...args) => session.emitEvent(eventName, ...args); } this._eventListeners = [ - contentChannel.register(sessionId + 'runtime', { + contentChannel.register('runtime', { runtimeConsole: emitProtocolEvent('Runtime.console'), runtimeExecutionContextCreated: emitProtocolEvent('Runtime.executionContextCreated'), runtimeExecutionContextDestroyed: emitProtocolEvent('Runtime.executionContextDestroyed'),