browser(firefox): enable document channel (#4065)
In the current tip-of-tree Firefox, document channel is enabled by default, so we have to enable it in order to roll further. This patch: 1. Removes content disposition sniffing from content process since it crashes renderer with document channel. 2. Merges all page-related handlers in a single `PageHandler` and serializes network events wrt the `Page.frameAttached` event. The serialization mentioned in (2) is necessary: frame attachment is reported from the content process, and network events are reported from the browsers process. This is an inherent race, that becomes exposed by the document channel. On a side note, (2) makes it possible to synchronously report all buffered events in `SimpleChannel` (cc offline discussion with @dgozman that highlighted an unsighty approach that we currently employ there: reporting events in a subsequent microtask.) References #3995
This commit is contained in:
		
							parent
							
								
									e403fd3912
								
							
						
					
					
						commit
						c8a64b88e1
					
				|  | @ -1,2 +1,2 @@ | |||
| 1181 | ||||
| Changed: pavel.feldman@gmail.com Mon, Oct  5, 2020  5:57:35 PM | ||||
| 1182 | ||||
| Changed: lushnikov@chromium.org Mon Oct  5 23:55:54 PDT 2020 | ||||
|  |  | |||
|  | @ -128,11 +128,24 @@ class NetworkRequest { | |||
|     this.httpChannel = httpChannel; | ||||
|     this._networkObserver._channelToRequest.set(this.httpChannel, this); | ||||
| 
 | ||||
|     const loadInfo = this.httpChannel.loadInfo; | ||||
|     let browsingContext = loadInfo?.frameBrowsingContext || loadInfo?.browsingContext; | ||||
|     // TODO: Unfortunately, requests from web workers don't have frameBrowsingContext or
 | ||||
|     // browsingContext.
 | ||||
|     //
 | ||||
|     // We fail to attribute them to the original frames on the browser side, but we
 | ||||
|     // can use load context top frame to attribute them to the top frame at least.
 | ||||
|     if (!browsingContext) { | ||||
|       const loadContext = helper.getLoadContext(this.httpChannel); | ||||
|       browsingContext = loadContext?.topFrameElement?.browsingContext; | ||||
|     } | ||||
| 
 | ||||
|     this._frameId = helper.browsingContextToFrameId(browsingContext); | ||||
| 
 | ||||
|     this.requestId = httpChannel.channelId + ''; | ||||
|     this.navigationId = httpChannel.isMainDocumentChannel ? this.requestId : undefined; | ||||
| 
 | ||||
|     const internalCauseType = this.httpChannel.loadInfo ? this.httpChannel.loadInfo.internalContentPolicyType : Ci.nsIContentPolicy.TYPE_OTHER; | ||||
|     this.channelKey = this.httpChannel.channelId + ':' + internalCauseType; | ||||
| 
 | ||||
|     this._redirectedIndex = 0; | ||||
|     const ignoredRedirect = redirectedFrom && !redirectedFrom._sentOnResponse; | ||||
|  | @ -140,13 +153,11 @@ class NetworkRequest { | |||
|       // We just ignore redirect that did not hit the network before being redirected.
 | ||||
|       // This happens, for example, for automatic http->https redirects.
 | ||||
|       this.navigationId = redirectedFrom.navigationId; | ||||
|       this.channelKey = redirectedFrom.channelKey; | ||||
|     } else if (redirectedFrom) { | ||||
|       this.redirectedFromId = redirectedFrom.requestId; | ||||
|       this._redirectedIndex = redirectedFrom._redirectedIndex + 1; | ||||
|       this.requestId = this.requestId + '-redirect' + this._redirectedIndex; | ||||
|       this.navigationId = redirectedFrom.navigationId; | ||||
|       this.channelKey = redirectedFrom.channelKey; | ||||
|       // Finish previous request now. Since we inherit the listener, we could in theory
 | ||||
|       // use onStopRequest, but that will only happen after the last redirect has finished.
 | ||||
|       redirectedFrom._sendOnRequestFinished(); | ||||
|  | @ -492,21 +503,9 @@ class NetworkRequest { | |||
|     const loadInfo = this.httpChannel.loadInfo; | ||||
|     const causeType = loadInfo?.externalContentPolicyType || Ci.nsIContentPolicy.TYPE_OTHER; | ||||
|     const internalCauseType = loadInfo?.internalContentPolicyType || Ci.nsIContentPolicy.TYPE_OTHER; | ||||
| 
 | ||||
|     let browsingContext = loadInfo?.frameBrowsingContext || loadInfo?.browsingContext; | ||||
|     // TODO: Unfortunately, requests from web workers don't have frameBrowsingContext or
 | ||||
|     // browsingContext.
 | ||||
|     //
 | ||||
|     // We fail to attribute them to the original frames on the browser side, but we
 | ||||
|     // can use load context top frame to attribute them to the top frame at least.
 | ||||
|     if (!browsingContext) { | ||||
|       const loadContext = helper.getLoadContext(this.httpChannel); | ||||
|       browsingContext = loadContext?.topFrameElement?.browsingContext; | ||||
|     } | ||||
| 
 | ||||
|     pageNetwork.emit(PageNetwork.Events.Request, { | ||||
|       url: this.httpChannel.URI.spec, | ||||
|       frameId: helper.browsingContextToFrameId(browsingContext), | ||||
|       frameId: this._frameId, | ||||
|       isIntercepted, | ||||
|       requestId: this.requestId, | ||||
|       redirectedFrom: this.redirectedFromId, | ||||
|  | @ -516,7 +515,7 @@ class NetworkRequest { | |||
|       navigationId: this.navigationId, | ||||
|       cause: causeTypeToString(causeType), | ||||
|       internalCause: causeTypeToString(internalCauseType), | ||||
|     }, this.channelKey); | ||||
|     }, this._frameId); | ||||
|   } | ||||
| 
 | ||||
|   _sendOnResponse(fromCache) { | ||||
|  | @ -563,7 +562,7 @@ class NetworkRequest { | |||
|       remotePort, | ||||
|       status, | ||||
|       statusText, | ||||
|     }); | ||||
|     }, this._frameId); | ||||
|   } | ||||
| 
 | ||||
|   _sendOnRequestFailed(error) { | ||||
|  | @ -572,7 +571,7 @@ class NetworkRequest { | |||
|       pageNetwork.emit(PageNetwork.Events.RequestFailed, { | ||||
|         requestId: this.requestId, | ||||
|         errorCode: helper.getNetworkErrorStatusText(error), | ||||
|       }); | ||||
|       }, this._frameId); | ||||
|     } | ||||
|     this._networkObserver._channelToRequest.delete(this.httpChannel); | ||||
|   } | ||||
|  | @ -582,7 +581,7 @@ class NetworkRequest { | |||
|     if (pageNetwork) { | ||||
|       pageNetwork.emit(PageNetwork.Events.RequestFinished, { | ||||
|         requestId: this.requestId, | ||||
|       }); | ||||
|       }, this._frameId); | ||||
|     } | ||||
|     this._networkObserver._channelToRequest.delete(this.httpChannel); | ||||
|   } | ||||
|  |  | |||
|  | @ -26,29 +26,16 @@ class DownloadInterceptor { | |||
|   constructor(registry) { | ||||
|     this._registry = registry | ||||
|     this._handlerToUuid = new Map(); | ||||
|     helper.addObserver(this._onRequest.bind(this), 'http-on-modify-request'); | ||||
|   } | ||||
| 
 | ||||
|   _onRequest(httpChannel, topic) { | ||||
|     let loadContext = helper.getLoadContext(httpChannel); | ||||
|     if (!loadContext) | ||||
|       return; | ||||
|     if (!loadContext.topFrameElement) | ||||
|       return; | ||||
|     const target = this._registry.targetForBrowser(loadContext.topFrameElement); | ||||
|     if (!target) | ||||
|       return; | ||||
|     target._channelIds.add(httpChannel.channelId); | ||||
|   } | ||||
| 
 | ||||
|   //
 | ||||
|   // nsIDownloadInterceptor implementation.
 | ||||
|   //
 | ||||
|   interceptDownloadRequest(externalAppHandler, request, browsingContext, outFile) { | ||||
|     let pageTarget = this._registry._browserBrowsingContextToTarget.get(browsingContext); | ||||
|     // New page downloads won't have browsing contex.
 | ||||
|     if (!pageTarget) | ||||
|       pageTarget = this._registry._targetForChannel(request); | ||||
|     if (!(request instanceof Ci.nsIChannel)) | ||||
|       return false; | ||||
|     const channel = request.QueryInterface(Ci.nsIChannel); | ||||
|     let pageTarget = this._registry._browserBrowsingContextToTarget.get(channel.loadInfo.browsingContext); | ||||
|     if (!pageTarget) | ||||
|       return false; | ||||
| 
 | ||||
|  | @ -324,15 +311,6 @@ class TargetRegistry { | |||
|   targetForBrowser(browser) { | ||||
|     return this._browserToTarget.get(browser); | ||||
|   } | ||||
| 
 | ||||
|   _targetForChannel(channel) { | ||||
|     const channelId = channel.channelId; | ||||
|     for (const target of this._browserToTarget.values()) { | ||||
|       if (target._channelIds.has(channelId)) | ||||
|         return target; | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class PageTarget { | ||||
|  | @ -350,7 +328,6 @@ class PageTarget { | |||
|     this._url = 'about:blank'; | ||||
|     this._openerId = opener ? opener.id() : undefined; | ||||
|     this._channel = SimpleChannel.createForMessageManager(`browser::page[${this._targetId}]`, this._linkedBrowser.messageManager); | ||||
|     this._channelIds = new Set(); | ||||
|     this._screencastInfo = undefined; | ||||
| 
 | ||||
|     const navigationListener = { | ||||
|  |  | |||
|  | @ -73,7 +73,7 @@ CommandLineHandler.prototype = { | |||
|             if (silent) | ||||
|               Services.startup.exitLastWindowClosingSurvivalArea(); | ||||
|           }); | ||||
|           dispatcher.rootSession().registerHandler('Browser', browserHandler); | ||||
|           dispatcher.rootSession().setHandler(browserHandler); | ||||
|         } | ||||
|       }); | ||||
|       loadFrameScript(); | ||||
|  | @ -101,9 +101,9 @@ CommandLineHandler.prototype = { | |||
|           pipe.stop(); | ||||
|         }); | ||||
|       }); | ||||
|       dispatcher.rootSession().registerHandler('Browser', browserHandler); | ||||
|       dispatcher.rootSession().setHandler(browserHandler); | ||||
|       loadFrameScript(); | ||||
|       dump(`Juggler listening to the pipe\n`); | ||||
|       dump(`\nJuggler listening to the pipe\n`); | ||||
|     } | ||||
|   }, | ||||
| 
 | ||||
|  |  | |||
|  | @ -197,19 +197,12 @@ class FrameTree { | |||
|     const isTransferring = flag & Ci.nsIWebProgressListener.STATE_TRANSFERRING; | ||||
|     const isStop = flag & Ci.nsIWebProgressListener.STATE_STOP; | ||||
| 
 | ||||
|     let isDownload = false; | ||||
|     try { | ||||
|       isDownload = (channel.contentDisposition === Ci.nsIChannel.DISPOSITION_ATTACHMENT); | ||||
|     } catch(e) { | ||||
|       // The method is expected to throw if it's not an attachment.
 | ||||
|     } | ||||
| 
 | ||||
|     if (isStart) { | ||||
|       // Starting a new navigation.
 | ||||
|       frame._pendingNavigationId = this._channelId(channel); | ||||
|       frame._pendingNavigationURL = channel.URI.spec; | ||||
|       this.emit(FrameTree.Events.NavigationStarted, frame); | ||||
|     } else if (isTransferring || (isStop && frame._pendingNavigationId && !status && !isDownload)) { | ||||
|     } else if (isTransferring || (isStop && frame._pendingNavigationId && !status)) { | ||||
|       // Navigation is committed.
 | ||||
|       for (const subframe of frame._children) | ||||
|         this._detachFrame(subframe); | ||||
|  | @ -221,15 +214,15 @@ class FrameTree { | |||
|       this.emit(FrameTree.Events.NavigationCommitted, frame); | ||||
|       if (frame === this._mainFrame) | ||||
|         this.forcePageReady(); | ||||
|     } else if (isStop && frame._pendingNavigationId && (status || isDownload)) { | ||||
|     } else if (isStop && frame._pendingNavigationId && status) { | ||||
|       // Navigation is aborted.
 | ||||
|       const navigationId = frame._pendingNavigationId; | ||||
|       frame._pendingNavigationId = null; | ||||
|       frame._pendingNavigationURL = null; | ||||
|       // Always report download navigation as failure to match other browsers.
 | ||||
|       const errorText = isDownload ? 'Will download to file' : helper.getNetworkErrorStatusText(status); | ||||
|       const errorText = helper.getNetworkErrorStatusText(status); | ||||
|       this.emit(FrameTree.Events.NavigationAborted, frame, navigationId, errorText); | ||||
|       if (frame === this._mainFrame && status !== Cr.NS_BINDING_ABORTED && !isDownload) | ||||
|       if (frame === this._mainFrame && status !== Cr.NS_BINDING_ABORTED) | ||||
|         this.forcePageReady(); | ||||
|     } | ||||
|   } | ||||
|  | @ -245,9 +238,9 @@ class FrameTree { | |||
|   } | ||||
| 
 | ||||
|   _channelId(channel) { | ||||
|     if (channel instanceof Ci.nsIHttpChannel) { | ||||
|       const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel); | ||||
|       return String(httpChannel.channelId); | ||||
|     if (channel instanceof Ci.nsIIdentChannel) { | ||||
|       const identChannel = channel.QueryInterface(Ci.nsIIdentChannel); | ||||
|       return String(identChannel.channelId); | ||||
|     } | ||||
|     return helper.generateId(); | ||||
|   } | ||||
|  |  | |||
|  | @ -12,10 +12,7 @@ juggler.jar: | |||
|   content/protocol/Protocol.js (protocol/Protocol.js) | ||||
|   content/protocol/Dispatcher.js (protocol/Dispatcher.js) | ||||
|   content/protocol/PageHandler.js (protocol/PageHandler.js) | ||||
|   content/protocol/RuntimeHandler.js (protocol/RuntimeHandler.js) | ||||
|   content/protocol/NetworkHandler.js (protocol/NetworkHandler.js) | ||||
|   content/protocol/BrowserHandler.js (protocol/BrowserHandler.js) | ||||
|   content/protocol/AccessibilityHandler.js (protocol/AccessibilityHandler.js) | ||||
|   content/content/main.js (content/main.js) | ||||
|   content/content/FrameTree.js (content/FrameTree.js) | ||||
|   content/content/PageAgent.js (content/PageAgent.js) | ||||
|  |  | |||
|  | @ -1,20 +0,0 @@ | |||
| /* 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 | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | ||||
| 
 | ||||
| class AccessibilityHandler { | ||||
|   constructor(session, contentChannel) { | ||||
|     this._contentPage = contentChannel.connect('page'); | ||||
|   } | ||||
| 
 | ||||
|   async getFullAXTree(params) { | ||||
|     return await this._contentPage.send('getFullAXTree', params); | ||||
|   } | ||||
| 
 | ||||
|   dispose() { | ||||
|     this._contentPage.dispose(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| var EXPORTED_SYMBOLS = ['AccessibilityHandler']; | ||||
| this.AccessibilityHandler = AccessibilityHandler; | ||||
|  | @ -8,9 +8,6 @@ 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(); | ||||
| 
 | ||||
|  | @ -27,7 +24,7 @@ class BrowserHandler { | |||
|     this._onclose = onclose; | ||||
|   } | ||||
| 
 | ||||
|   async enable({attachToDefaultContext}) { | ||||
|   async ['Browser.enable']({attachToDefaultContext}) { | ||||
|     if (this._enabled) | ||||
|       return; | ||||
|     this._enabled = true; | ||||
|  | @ -50,7 +47,7 @@ class BrowserHandler { | |||
|       this._onTargetCreated(target); | ||||
|   } | ||||
| 
 | ||||
|   async createBrowserContext({removeOnDetach}) { | ||||
|   async ['Browser.createBrowserContext']({removeOnDetach}) { | ||||
|     if (!this._enabled) | ||||
|       throw new Error('Browser domain is not enabled'); | ||||
|     const browserContext = this._targetRegistry.createBrowserContext(removeOnDetach); | ||||
|  | @ -58,7 +55,7 @@ class BrowserHandler { | |||
|     return {browserContextId: browserContext.browserContextId}; | ||||
|   } | ||||
| 
 | ||||
|   async removeBrowserContext({browserContextId}) { | ||||
|   async ['Browser.removeBrowserContext']({browserContextId}) { | ||||
|     if (!this._enabled) | ||||
|       throw new Error('Browser domain is not enabled'); | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).destroy(); | ||||
|  | @ -90,18 +87,11 @@ class BrowserHandler { | |||
|     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() | ||||
|     }); | ||||
|     session.setHandler(new PageHandler(target, session, channel)); | ||||
|   } | ||||
| 
 | ||||
|   _onTargetDestroyed(target) { | ||||
|  | @ -124,12 +114,12 @@ class BrowserHandler { | |||
|     this._session.emitEvent('Browser.downloadFinished', downloadInfo); | ||||
|   } | ||||
| 
 | ||||
|   async newPage({browserContextId}) { | ||||
|   async ['Browser.newPage']({browserContextId}) { | ||||
|     const targetId = await this._targetRegistry.newPage({browserContextId}); | ||||
|     return {targetId}; | ||||
|   } | ||||
| 
 | ||||
|   async close() { | ||||
|   async ['Browser.close']() { | ||||
|     let browserWindow = Services.wm.getMostRecentWindow( | ||||
|       "navigator:browser" | ||||
|     ); | ||||
|  | @ -140,109 +130,109 @@ class BrowserHandler { | |||
|     Services.startup.quit(Ci.nsIAppStartup.eForceQuit); | ||||
|   } | ||||
| 
 | ||||
|   async grantPermissions({browserContextId, origin, permissions}) { | ||||
|   async ['Browser.grantPermissions']({browserContextId, origin, permissions}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).grantPermissions(origin, permissions); | ||||
|   } | ||||
| 
 | ||||
|   resetPermissions({browserContextId}) { | ||||
|   async ['Browser.resetPermissions']({browserContextId}) { | ||||
|     this._targetRegistry.browserContextForId(browserContextId).resetPermissions(); | ||||
|   } | ||||
| 
 | ||||
|   setExtraHTTPHeaders({browserContextId, headers}) { | ||||
|   ['Browser.setExtraHTTPHeaders']({browserContextId, headers}) { | ||||
|     this._targetRegistry.browserContextForId(browserContextId).extraHTTPHeaders = headers; | ||||
|   } | ||||
| 
 | ||||
|   setHTTPCredentials({browserContextId, credentials}) { | ||||
|   ['Browser.setHTTPCredentials']({browserContextId, credentials}) { | ||||
|     this._targetRegistry.browserContextForId(browserContextId).httpCredentials = nullToUndefined(credentials); | ||||
|   } | ||||
| 
 | ||||
|   async setBrowserProxy({type, host, port, bypass, username, password}) { | ||||
|   async ['Browser.setBrowserProxy']({type, host, port, bypass, username, password}) { | ||||
|     this._targetRegistry.setBrowserProxy({ type, host, port, bypass, username, password}); | ||||
|   } | ||||
| 
 | ||||
|   async setContextProxy({browserContextId, type, host, port, bypass, username, password}) { | ||||
|   async ['Browser.setContextProxy']({browserContextId, type, host, port, bypass, username, password}) { | ||||
|     const browserContext = this._targetRegistry.browserContextForId(browserContextId); | ||||
|     browserContext.setProxy({ type, host, port, bypass, username, password }); | ||||
|   } | ||||
| 
 | ||||
|   setRequestInterception({browserContextId, enabled}) { | ||||
|   ['Browser.setRequestInterception']({browserContextId, enabled}) { | ||||
|     this._targetRegistry.browserContextForId(browserContextId).requestInterceptionEnabled = enabled; | ||||
|   } | ||||
| 
 | ||||
|   setIgnoreHTTPSErrors({browserContextId, ignoreHTTPSErrors}) { | ||||
|   ['Browser.setIgnoreHTTPSErrors']({browserContextId, ignoreHTTPSErrors}) { | ||||
|     this._targetRegistry.browserContextForId(browserContextId).setIgnoreHTTPSErrors(nullToUndefined(ignoreHTTPSErrors)); | ||||
|   } | ||||
| 
 | ||||
|   setDownloadOptions({browserContextId, downloadOptions}) { | ||||
|   ['Browser.setDownloadOptions']({browserContextId, downloadOptions}) { | ||||
|     this._targetRegistry.browserContextForId(browserContextId).downloadOptions = nullToUndefined(downloadOptions); | ||||
|   } | ||||
| 
 | ||||
|   async setGeolocationOverride({browserContextId, geolocation}) { | ||||
|   async ['Browser.setGeolocationOverride']({browserContextId, geolocation}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).applySetting('geolocation', nullToUndefined(geolocation)); | ||||
|   } | ||||
| 
 | ||||
|   async setOnlineOverride({browserContextId, override}) { | ||||
|   async ['Browser.setOnlineOverride']({browserContextId, override}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).applySetting('onlineOverride', nullToUndefined(override)); | ||||
|   } | ||||
| 
 | ||||
|   async setColorScheme({browserContextId, colorScheme}) { | ||||
|   async ['Browser.setColorScheme']({browserContextId, colorScheme}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).applySetting('colorScheme', nullToUndefined(colorScheme)); | ||||
|   } | ||||
| 
 | ||||
|   async setScreencastOptions({browserContextId, dir, width, height, scale}) { | ||||
|   async ['Browser.setScreencastOptions']({browserContextId, dir, width, height, scale}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).setScreencastOptions({dir, width, height, scale}); | ||||
|   } | ||||
| 
 | ||||
|   async setUserAgentOverride({browserContextId, userAgent}) { | ||||
|   async ['Browser.setUserAgentOverride']({browserContextId, userAgent}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).setDefaultUserAgent(userAgent); | ||||
|   } | ||||
| 
 | ||||
|   async setBypassCSP({browserContextId, bypassCSP}) { | ||||
|   async ['Browser.setBypassCSP']({browserContextId, bypassCSP}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).applySetting('bypassCSP', nullToUndefined(bypassCSP)); | ||||
|   } | ||||
| 
 | ||||
|   async setJavaScriptDisabled({browserContextId, javaScriptDisabled}) { | ||||
|   async ['Browser.setJavaScriptDisabled']({browserContextId, javaScriptDisabled}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).applySetting('javaScriptDisabled', nullToUndefined(javaScriptDisabled)); | ||||
|   } | ||||
| 
 | ||||
|   async setLocaleOverride({browserContextId, locale}) { | ||||
|   async ['Browser.setLocaleOverride']({browserContextId, locale}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).applySetting('locale', nullToUndefined(locale)); | ||||
|   } | ||||
| 
 | ||||
|   async setTimezoneOverride({browserContextId, timezoneId}) { | ||||
|   async ['Browser.setTimezoneOverride']({browserContextId, timezoneId}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).applySetting('timezoneId', nullToUndefined(timezoneId)); | ||||
|   } | ||||
| 
 | ||||
|   async setTouchOverride({browserContextId, hasTouch}) { | ||||
|   async ['Browser.setTouchOverride']({browserContextId, hasTouch}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).applySetting('hasTouch', nullToUndefined(hasTouch)); | ||||
|   } | ||||
| 
 | ||||
|   async setDefaultViewport({browserContextId, viewport}) { | ||||
|   async ['Browser.setDefaultViewport']({browserContextId, viewport}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).setDefaultViewport(nullToUndefined(viewport)); | ||||
|   } | ||||
| 
 | ||||
|   async addScriptToEvaluateOnNewDocument({browserContextId, script}) { | ||||
|   async ['Browser.addScriptToEvaluateOnNewDocument']({browserContextId, script}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).addScriptToEvaluateOnNewDocument(script); | ||||
|   } | ||||
| 
 | ||||
|   async addBinding({browserContextId, name, script}) { | ||||
|   async ['Browser.addBinding']({browserContextId, name, script}) { | ||||
|     await this._targetRegistry.browserContextForId(browserContextId).addBinding(name, script); | ||||
|   } | ||||
| 
 | ||||
|   setCookies({browserContextId, cookies}) { | ||||
|   ['Browser.setCookies']({browserContextId, cookies}) { | ||||
|     this._targetRegistry.browserContextForId(browserContextId).setCookies(cookies); | ||||
|   } | ||||
| 
 | ||||
|   clearCookies({browserContextId}) { | ||||
|   ['Browser.clearCookies']({browserContextId}) { | ||||
|     this._targetRegistry.browserContextForId(browserContextId).clearCookies(); | ||||
|   } | ||||
| 
 | ||||
|   getCookies({browserContextId}) { | ||||
|   ['Browser.getCookies']({browserContextId}) { | ||||
|     const cookies = this._targetRegistry.browserContextForId(browserContextId).getCookies(); | ||||
|     return {cookies}; | ||||
|   } | ||||
| 
 | ||||
|   async getInfo() { | ||||
|   async ['Browser.getInfo']() { | ||||
|     const version = Components.classes["@mozilla.org/xre/app-info;1"] | ||||
|                               .getService(Components.interfaces.nsIXULAppInfo) | ||||
|                               .version; | ||||
|  |  | |||
|  | @ -66,7 +66,7 @@ class Dispatcher { | |||
|       if (!checkScheme(descriptor.params || {}, params, details)) | ||||
|         throw new Error(`ERROR: failed to call method '${method}' with parameters ${JSON.stringify(params, null, 2)}\n${details.error}`); | ||||
| 
 | ||||
|       const result = await session.dispatch(domain, methodName, params); | ||||
|       const result = await session.dispatch(method, params); | ||||
| 
 | ||||
|       details = {}; | ||||
|       if ((descriptor.returns || result) && !checkScheme(descriptor.returns, result, details)) | ||||
|  | @ -97,24 +97,21 @@ class ProtocolSession { | |||
|   constructor(dispatcher, sessionId) { | ||||
|     this._sessionId = sessionId; | ||||
|     this._dispatcher = dispatcher; | ||||
|     this._handlers = new Map(); | ||||
|     this._handler = null; | ||||
|   } | ||||
| 
 | ||||
|   sessionId() { | ||||
|     return this._sessionId; | ||||
|   } | ||||
| 
 | ||||
|   registerHandler(domainName, handler) { | ||||
|     this._handlers.set(domainName, handler); | ||||
|   setHandler(handler) { | ||||
|     this._handler = handler; | ||||
|   } | ||||
| 
 | ||||
|   _dispose() { | ||||
|     for (const [domainName, handler] of this._handlers) { | ||||
|       if (typeof handler.dispose !== 'function') | ||||
|         throw new Error(`Handler for "${domainName}" domain does not define |dispose| method!`); | ||||
|       handler.dispose(); | ||||
|     } | ||||
|     this._handlers.clear(); | ||||
|     if (this._handler) | ||||
|       this._handler.dispose(); | ||||
|     this._handler = null; | ||||
|     this._dispatcher = null; | ||||
|   } | ||||
| 
 | ||||
|  | @ -124,13 +121,12 @@ class ProtocolSession { | |||
|     this._dispatcher._emitEvent(this._sessionId, eventName, params); | ||||
|   } | ||||
| 
 | ||||
|   async dispatch(domainName, methodName, params) { | ||||
|     const handler = this._handlers.get(domainName); | ||||
|     if (!handler) | ||||
|       throw new Error(`Domain "${domainName}" does not exist`); | ||||
|     if (!handler[methodName]) | ||||
|       throw new Error(`Handler for domain "${domainName}" does not implement method "${methodName}"`); | ||||
|     return await handler[methodName](params); | ||||
|   async dispatch(method, params) { | ||||
|     if (!this._handler) | ||||
|       throw new Error(`Session does not have a handler!`); | ||||
|     if (!this._handler[method]) | ||||
|       throw new Error(`Handler for does not implement method "${method}"`); | ||||
|     return await this._handler[method](params); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,86 +0,0 @@ | |||
| /* 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 | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); | ||||
| const {NetworkObserver, PageNetwork} = ChromeUtils.import('chrome://juggler/content/NetworkObserver.js'); | ||||
| 
 | ||||
| const Cc = Components.classes; | ||||
| const Ci = Components.interfaces; | ||||
| const Cu = Components.utils; | ||||
| const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; | ||||
| const helper = new Helper(); | ||||
| 
 | ||||
| class NetworkHandler { | ||||
|   constructor(target, session, contentChannel) { | ||||
|     this._session = session; | ||||
|     this._enabled = false; | ||||
|     this._pageNetwork = NetworkObserver.instance().pageNetworkForTarget(target); | ||||
|     this._eventListeners = []; | ||||
|   } | ||||
| 
 | ||||
|   async enable() { | ||||
|     if (this._enabled) | ||||
|       return; | ||||
|     this._enabled = true; | ||||
|     this._eventListeners = [ | ||||
|       helper.on(this._pageNetwork, PageNetwork.Events.Request, this._onRequest.bind(this)), | ||||
|       helper.on(this._pageNetwork, PageNetwork.Events.Response, this._onResponse.bind(this)), | ||||
|       helper.on(this._pageNetwork, PageNetwork.Events.RequestFinished, this._onRequestFinished.bind(this)), | ||||
|       helper.on(this._pageNetwork, PageNetwork.Events.RequestFailed, this._onRequestFailed.bind(this)), | ||||
|       this._pageNetwork.addSession(), | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   async getResponseBody({requestId}) { | ||||
|     return this._pageNetwork.getResponseBody(requestId); | ||||
|   } | ||||
| 
 | ||||
|   async setExtraHTTPHeaders({headers}) { | ||||
|     this._pageNetwork.setExtraHTTPHeaders(headers); | ||||
|   } | ||||
| 
 | ||||
|   async setRequestInterception({enabled}) { | ||||
|     if (enabled) | ||||
|       this._pageNetwork.enableRequestInterception(); | ||||
|     else | ||||
|     this._pageNetwork.disableRequestInterception(); | ||||
|   } | ||||
| 
 | ||||
|   async resumeInterceptedRequest({requestId, method, headers, postData}) { | ||||
|     this._pageNetwork.resumeInterceptedRequest(requestId, method, headers, postData); | ||||
|   } | ||||
| 
 | ||||
|   async abortInterceptedRequest({requestId, errorCode}) { | ||||
|     this._pageNetwork.abortInterceptedRequest(requestId, errorCode); | ||||
|   } | ||||
| 
 | ||||
|   async fulfillInterceptedRequest({requestId, status, statusText, headers, base64body}) { | ||||
|     this._pageNetwork.fulfillInterceptedRequest(requestId, status, statusText, headers, base64body); | ||||
|   } | ||||
| 
 | ||||
|   dispose() { | ||||
|     helper.removeListeners(this._eventListeners); | ||||
|   } | ||||
| 
 | ||||
|   async _onRequest(eventDetails, channelKey) { | ||||
|     this._session.emitEvent('Network.requestWillBeSent', eventDetails); | ||||
|   } | ||||
| 
 | ||||
|   async _onResponse(eventDetails) { | ||||
|     this._session.emitEvent('Network.responseReceived', eventDetails); | ||||
|   } | ||||
| 
 | ||||
|   async _onRequestFinished(eventDetails) { | ||||
|     this._session.emitEvent('Network.requestFinished', eventDetails); | ||||
|   } | ||||
| 
 | ||||
|   async _onRequestFailed(eventDetails) { | ||||
|     this._session.emitEvent('Network.requestFailed', eventDetails); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| var EXPORTED_SYMBOLS = ['NetworkHandler']; | ||||
| this.NetworkHandler = NetworkHandler; | ||||
|  | @ -6,6 +6,7 @@ | |||
| 
 | ||||
| const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); | ||||
| const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); | ||||
| const {NetworkObserver, PageNetwork} = ChromeUtils.import('chrome://juggler/content/NetworkObserver.js'); | ||||
| 
 | ||||
| const Cc = Components.classes; | ||||
| const Ci = Components.interfaces; | ||||
|  | @ -59,19 +60,28 @@ class PageHandler { | |||
|     this._session = session; | ||||
|     this._contentChannel = contentChannel; | ||||
|     this._contentPage = contentChannel.connect('page'); | ||||
|     this._contentRuntime = contentChannel.connect('runtime'); | ||||
|     this._workers = new Map(); | ||||
| 
 | ||||
|     this._pageTarget = target; | ||||
|     this._browser = target.linkedBrowser(); | ||||
|     this._dialogs = new Map(); | ||||
|     this._pageNetwork = NetworkObserver.instance().pageNetworkForTarget(target); | ||||
| 
 | ||||
|     const emitProtocolEvent = eventName => { | ||||
|       return (...args) => this._session.emitEvent(eventName, ...args); | ||||
|     } | ||||
| 
 | ||||
|     this._reportedFrameIds = new Set(); | ||||
|     this._networkEventsForUnreportedFrameIds = new Map(); | ||||
| 
 | ||||
|     this._eventListeners = [ | ||||
|       contentChannel.register('page', { | ||||
|         pageBindingCalled: emitProtocolEvent('Page.bindingCalled'), | ||||
|         pageDispatchMessageFromWorker: emitProtocolEvent('Page.dispatchMessageFromWorker'), | ||||
|         pageEventFired: emitProtocolEvent('Page.eventFired'), | ||||
|         pageFileChooserOpened: emitProtocolEvent('Page.fileChooserOpened'), | ||||
|         pageFrameAttached: emitProtocolEvent('Page.frameAttached'), | ||||
|         pageFrameAttached: this._onFrameAttached.bind(this), | ||||
|         pageFrameDetached: emitProtocolEvent('Page.frameDetached'), | ||||
|         pageLinkClicked: emitProtocolEvent('Page.linkClicked'), | ||||
|         pageWillOpenNewWindowAsynchronously: emitProtocolEvent('Page.willOpenNewWindowAsynchronously'), | ||||
|  | @ -84,12 +94,35 @@ class PageHandler { | |||
|         pageWorkerCreated: this._onWorkerCreated.bind(this), | ||||
|         pageWorkerDestroyed: this._onWorkerDestroyed.bind(this), | ||||
|       }), | ||||
|       contentChannel.register('runtime', { | ||||
|         runtimeConsole: emitProtocolEvent('Runtime.console'), | ||||
|         runtimeExecutionContextCreated: emitProtocolEvent('Runtime.executionContextCreated'), | ||||
|         runtimeExecutionContextDestroyed: emitProtocolEvent('Runtime.executionContextDestroyed'), | ||||
|       }), | ||||
|       helper.addEventListener(this._browser, 'DOMWillOpenModalDialog', async (event) => { | ||||
|         // wait for the dialog to be actually added to DOM.
 | ||||
|         await Promise.resolve(); | ||||
|         this._updateModalDialogs(); | ||||
|       }), | ||||
|       helper.addEventListener(this._browser, 'DOMModalDialogClosed', event => this._updateModalDialogs()), | ||||
|       helper.on(this._pageTarget, 'crashed', () => { | ||||
|         this._session.emitEvent('Page.crashed', {}); | ||||
|       }), | ||||
|       helper.on(this._pageTarget, 'screencastStarted', () => { | ||||
|         const info = this._pageTarget.screencastInfo(); | ||||
|         this._session.emitEvent('Page.screencastStarted', { screencastId: '' + info.videoSessionId, file: info.file }); | ||||
|       }), | ||||
|       helper.on(this._pageNetwork, PageNetwork.Events.Request, this._handleNetworkEvent.bind(this, 'Network.requestWillBeSent')), | ||||
|       helper.on(this._pageNetwork, PageNetwork.Events.Response, this._handleNetworkEvent.bind(this, 'Network.responseReceived')), | ||||
|       helper.on(this._pageNetwork, PageNetwork.Events.RequestFinished, this._handleNetworkEvent.bind(this, 'Network.requestFinished')), | ||||
|       helper.on(this._pageNetwork, PageNetwork.Events.RequestFailed, this._handleNetworkEvent.bind(this, 'Network.requestFailed')), | ||||
|       this._pageNetwork.addSession(), | ||||
|     ]; | ||||
|     this._pageTarget = target; | ||||
|     this._browser = target.linkedBrowser(); | ||||
|     this._dialogs = new Map(); | ||||
| 
 | ||||
|     this._enabled = false; | ||||
|     this._updateModalDialogs(); | ||||
|     const options = this._pageTarget.browserContext().screencastOptions; | ||||
|     if (options) | ||||
|       this._pageTarget.startVideoRecording(options); | ||||
|   } | ||||
| 
 | ||||
|   _onWorkerCreated({workerId, frameId, url}) { | ||||
|  | @ -107,49 +140,45 @@ class PageHandler { | |||
|     this._session.emitEvent('Page.workerDestroyed', {workerId}); | ||||
|   } | ||||
| 
 | ||||
|   async close({runBeforeUnload}) { | ||||
|   _handleNetworkEvent(protocolEventName, eventDetails, frameId) { | ||||
|     if (!this._reportedFrameIds.has(frameId)) { | ||||
|       let events = this._networkEventsForUnreportedFrameIds.get(frameId); | ||||
|       if (!events) { | ||||
|         events = []; | ||||
|         this._networkEventsForUnreportedFrameIds.set(frameId, events); | ||||
|       } | ||||
|       events.push({eventName: protocolEventName, eventDetails}); | ||||
|     } else { | ||||
|       this._session.emitEvent(protocolEventName, eventDetails); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   _onFrameAttached({frameId, parentFrameId}) { | ||||
|     this._session.emitEvent('Page.frameAttached', {frameId, parentFrameId}); | ||||
|     this._reportedFrameIds.add(frameId); | ||||
|     const events = this._networkEventsForUnreportedFrameIds.get(frameId) || []; | ||||
|     this._networkEventsForUnreportedFrameIds.delete(frameId); | ||||
|     for (const {eventName, eventDetails} of events) | ||||
|       this._session.emitEvent(eventName, eventDetails); | ||||
|   } | ||||
| 
 | ||||
|   async ['Page.close']({runBeforeUnload}) { | ||||
|     // Postpone target close to deliver response in session.
 | ||||
|     Services.tm.dispatchToMainThread(() => { | ||||
|       this._pageTarget.close(runBeforeUnload); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   async enable() { | ||||
|     if (this._enabled) | ||||
|       return; | ||||
|     this._enabled = true; | ||||
|     this._updateModalDialogs(); | ||||
| 
 | ||||
|     this._eventListeners.push(...[ | ||||
|       helper.addEventListener(this._browser, 'DOMWillOpenModalDialog', async (event) => { | ||||
|         // wait for the dialog to be actually added to DOM.
 | ||||
|         await Promise.resolve(); | ||||
|         this._updateModalDialogs(); | ||||
|       }), | ||||
|       helper.addEventListener(this._browser, 'DOMModalDialogClosed', event => this._updateModalDialogs()), | ||||
|       helper.on(this._pageTarget, 'crashed', () => { | ||||
|         this._session.emitEvent('Page.crashed', {}); | ||||
|       }), | ||||
|       helper.on(this._pageTarget, 'screencastStarted', () => { | ||||
|         const info = this._pageTarget.screencastInfo(); | ||||
|         this._session.emitEvent('Page.screencastStarted', { screencastId: '' + info.videoSessionId, file: info.file }); | ||||
|       }), | ||||
|     ]); | ||||
| 
 | ||||
|     const options = this._pageTarget.browserContext().screencastOptions; | ||||
|     if (options) | ||||
|       await this._pageTarget.startVideoRecording(options); | ||||
|   } | ||||
| 
 | ||||
|   async dispose() { | ||||
|     this._contentPage.dispose(); | ||||
|     this._contentRuntime.dispose(); | ||||
|     helper.removeListeners(this._eventListeners); | ||||
| 
 | ||||
|     if (this._pageTarget.screencastInfo()) | ||||
|       await this._pageTarget.stopVideoRecording().catch(e => dump(`stopVideoRecording failed:\n${e}\n`)); | ||||
|   } | ||||
| 
 | ||||
|   async setViewportSize({viewportSize}) { | ||||
|   async ['Page.setViewportSize']({viewportSize}) { | ||||
|     await this._pageTarget.setViewportSize(viewportSize === null ? undefined : viewportSize); | ||||
|   } | ||||
| 
 | ||||
|  | @ -179,107 +208,154 @@ class PageHandler { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   async setFileInputFiles(options) { | ||||
|   async ['Runtime.evaluate'](options) { | ||||
|     return await this._contentRuntime.send('evaluate', options); | ||||
|   } | ||||
| 
 | ||||
|   async ['Runtime.callFunction'](options) { | ||||
|     return await this._contentRuntime.send('callFunction', options); | ||||
|   } | ||||
| 
 | ||||
|   async ['Runtime.getObjectProperties'](options) { | ||||
|     return await this._contentRuntime.send('getObjectProperties', options); | ||||
|   } | ||||
| 
 | ||||
|   async ['Runtime.disposeObject'](options) { | ||||
|     return await this._contentRuntime.send('disposeObject', options); | ||||
|   } | ||||
| 
 | ||||
|   async ['Network.getResponseBody']({requestId}) { | ||||
|     return this._pageNetwork.getResponseBody(requestId); | ||||
|   } | ||||
| 
 | ||||
|   async ['Network.setExtraHTTPHeaders']({headers}) { | ||||
|     this._pageNetwork.setExtraHTTPHeaders(headers); | ||||
|   } | ||||
| 
 | ||||
|   async ['Network.setRequestInterception']({enabled}) { | ||||
|     if (enabled) | ||||
|       this._pageNetwork.enableRequestInterception(); | ||||
|     else | ||||
|       this._pageNetwork.disableRequestInterception(); | ||||
|   } | ||||
| 
 | ||||
|   async ['Network.resumeInterceptedRequest']({requestId, method, headers, postData}) { | ||||
|     this._pageNetwork.resumeInterceptedRequest(requestId, method, headers, postData); | ||||
|   } | ||||
| 
 | ||||
|   async ['Network.abortInterceptedRequest']({requestId, errorCode}) { | ||||
|     this._pageNetwork.abortInterceptedRequest(requestId, errorCode); | ||||
|   } | ||||
| 
 | ||||
|   async ['Network.fulfillInterceptedRequest']({requestId, status, statusText, headers, base64body}) { | ||||
|     this._pageNetwork.fulfillInterceptedRequest(requestId, status, statusText, headers, base64body); | ||||
|   } | ||||
| 
 | ||||
|   async ['Accessibility.getFullAXTree'](params) { | ||||
|     return await this._contentPage.send('getFullAXTree', params); | ||||
|   } | ||||
| 
 | ||||
|   async ['Page.setFileInputFiles'](options) { | ||||
|     return await this._contentPage.send('setFileInputFiles', options); | ||||
|   } | ||||
| 
 | ||||
|   async setEmulatedMedia(options) { | ||||
|   async ['Page.setEmulatedMedia'](options) { | ||||
|     return await this._contentPage.send('setEmulatedMedia', options); | ||||
|   } | ||||
| 
 | ||||
|   async bringToFront(options) { | ||||
|   async ['Page.bringToFront'](options) { | ||||
|     this._pageTarget._window.focus(); | ||||
|   } | ||||
| 
 | ||||
|   async setCacheDisabled(options) { | ||||
|   async ['Page.setCacheDisabled'](options) { | ||||
|     return await this._contentPage.send('setCacheDisabled', options); | ||||
|   } | ||||
| 
 | ||||
|   async addBinding(options) { | ||||
|   async ['Page.addBinding'](options) { | ||||
|     return await this._contentPage.send('addBinding', options); | ||||
|   } | ||||
| 
 | ||||
|   async adoptNode(options) { | ||||
|   async ['Page.adoptNode'](options) { | ||||
|     return await this._contentPage.send('adoptNode', options); | ||||
|   } | ||||
| 
 | ||||
|   async screenshot(options) { | ||||
|   async ['Page.screenshot'](options) { | ||||
|     return await this._contentPage.send('screenshot', options); | ||||
|   } | ||||
| 
 | ||||
|   async getBoundingBox(options) { | ||||
|   async ['Page.getBoundingBox'](options) { | ||||
|     return await this._contentPage.send('getBoundingBox', options); | ||||
|   } | ||||
| 
 | ||||
|   async getContentQuads(options) { | ||||
|   async ['Page.getContentQuads'](options) { | ||||
|     return await this._contentPage.send('getContentQuads', options); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @param {{frameId: string, url: string}} options | ||||
|    */ | ||||
|   async navigate(options) { | ||||
|   async ['Page.navigate'](options) { | ||||
|     return await this._contentPage.send('navigate', options); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @param {{frameId: string, url: string}} options | ||||
|    */ | ||||
|   async goBack(options) { | ||||
|   async ['Page.goBack'](options) { | ||||
|     return await this._contentPage.send('goBack', options); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @param {{frameId: string, url: string}} options | ||||
|    */ | ||||
|   async goForward(options) { | ||||
|   async ['Page.goForward'](options) { | ||||
|     return await this._contentPage.send('goForward', options); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @param {{frameId: string, url: string}} options | ||||
|    */ | ||||
|   async reload(options) { | ||||
|   async ['Page.reload'](options) { | ||||
|     return await this._contentPage.send('reload', options); | ||||
|   } | ||||
| 
 | ||||
|   async describeNode(options) { | ||||
|   async ['Page.describeNode'](options) { | ||||
|     return await this._contentPage.send('describeNode', options); | ||||
|   } | ||||
| 
 | ||||
|   async scrollIntoViewIfNeeded(options) { | ||||
|   async ['Page.scrollIntoViewIfNeeded'](options) { | ||||
|     return await this._contentPage.send('scrollIntoViewIfNeeded', options); | ||||
|   } | ||||
| 
 | ||||
|   async addScriptToEvaluateOnNewDocument(options) { | ||||
|   async ['Page.addScriptToEvaluateOnNewDocument'](options) { | ||||
|     return await this._contentPage.send('addScriptToEvaluateOnNewDocument', options); | ||||
|   } | ||||
| 
 | ||||
|   async removeScriptToEvaluateOnNewDocument(options) { | ||||
|   async ['Page.removeScriptToEvaluateOnNewDocument'](options) { | ||||
|     return await this._contentPage.send('removeScriptToEvaluateOnNewDocument', options); | ||||
|   } | ||||
| 
 | ||||
|   async dispatchKeyEvent(options) { | ||||
|   async ['Page.dispatchKeyEvent'](options) { | ||||
|     return await this._contentPage.send('dispatchKeyEvent', options); | ||||
|   } | ||||
| 
 | ||||
|   async dispatchTouchEvent(options) { | ||||
|   async ['Page.dispatchTouchEvent'](options) { | ||||
|     return await this._contentPage.send('dispatchTouchEvent', options); | ||||
|   } | ||||
| 
 | ||||
|   async dispatchMouseEvent(options) { | ||||
|   async ['Page.dispatchMouseEvent'](options) { | ||||
|     return await this._contentPage.send('dispatchMouseEvent', options); | ||||
|   } | ||||
| 
 | ||||
|   async insertText(options) { | ||||
|   async ['Page.insertText'](options) { | ||||
|     return await this._contentPage.send('insertText', options); | ||||
|   } | ||||
| 
 | ||||
|   async crash(options) { | ||||
|   async ['Page.crash'](options) { | ||||
|     return await this._contentPage.send('crash', options); | ||||
|   } | ||||
| 
 | ||||
|   async handleDialog({dialogId, accept, promptText}) { | ||||
|   async ['Page.handleDialog']({dialogId, accept, promptText}) { | ||||
|     const dialog = this._dialogs.get(dialogId); | ||||
|     if (!dialog) | ||||
|       throw new Error('Failed to find dialog with id = ' + dialogId); | ||||
|  | @ -289,18 +365,18 @@ class PageHandler { | |||
|       dialog.dismiss(); | ||||
|   } | ||||
| 
 | ||||
|   async setInterceptFileChooserDialog(options) { | ||||
|   async ['Page.setInterceptFileChooserDialog'](options) { | ||||
|     return await this._contentPage.send('setInterceptFileChooserDialog', options); | ||||
|   } | ||||
| 
 | ||||
|   async sendMessageToWorker({workerId, message}) { | ||||
|   async ['Page.sendMessageToWorker']({workerId, message}) { | ||||
|     const worker = this._workers.get(workerId); | ||||
|     if (!worker) | ||||
|       throw new Error('ERROR: cannot find worker with id ' + workerId); | ||||
|     return await worker.sendMessage(JSON.parse(message)); | ||||
|   } | ||||
| 
 | ||||
|   async stopVideoRecording() { | ||||
|   async ['Page.stopVideoRecording']() { | ||||
|     await this._pageTarget.stopVideoRecording(); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,55 +0,0 @@ | |||
| /* 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 | ||||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); | ||||
| const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); | ||||
| 
 | ||||
| const Cc = Components.classes; | ||||
| const Ci = Components.interfaces; | ||||
| const Cu = Components.utils; | ||||
| const helper = new Helper(); | ||||
| 
 | ||||
| class RuntimeHandler { | ||||
|   constructor(session, contentChannel) { | ||||
|     this._contentRuntime = contentChannel.connect('runtime'); | ||||
| 
 | ||||
|     const emitProtocolEvent = eventName => { | ||||
|       return (...args) => session.emitEvent(eventName, ...args); | ||||
|     } | ||||
| 
 | ||||
|     this._eventListeners = [ | ||||
|       contentChannel.register('runtime', { | ||||
|         runtimeConsole: emitProtocolEvent('Runtime.console'), | ||||
|         runtimeExecutionContextCreated: emitProtocolEvent('Runtime.executionContextCreated'), | ||||
|         runtimeExecutionContextDestroyed: emitProtocolEvent('Runtime.executionContextDestroyed'), | ||||
|       }), | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   async evaluate(options) { | ||||
|     return await this._contentRuntime.send('evaluate', options); | ||||
|   } | ||||
| 
 | ||||
|   async callFunction(options) { | ||||
|     return await this._contentRuntime.send('callFunction', options); | ||||
|   } | ||||
| 
 | ||||
|   async getObjectProperties(options) { | ||||
|     return await this._contentRuntime.send('getObjectProperties', options); | ||||
|   } | ||||
| 
 | ||||
|   async disposeObject(options) { | ||||
|     return await this._contentRuntime.send('disposeObject', options); | ||||
|   } | ||||
| 
 | ||||
|   dispose() { | ||||
|     this._contentRuntime.dispose(); | ||||
|     helper.removeListeners(this._eventListeners); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| var EXPORTED_SYMBOLS = ['RuntimeHandler']; | ||||
| this.RuntimeHandler = RuntimeHandler; | ||||
|  | @ -240,10 +240,6 @@ pref("security.notification_enable_delay", 0); | |||
| // Ensure blocklist updates do not hit the network | ||||
| pref("services.settings.server", ""); | ||||
| 
 | ||||
| // Disable DocumentChannel. | ||||
| // See https://github.com/microsoft/playwright/pull/451 | ||||
| pref("browser.tabs.documentchannel", false); | ||||
| 
 | ||||
| // Do not automatically fill sign-in forms with known usernames and | ||||
| // passwords | ||||
| pref("signon.autofillForms", false); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue