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:
Andrey Lushnikov 2020-10-06 00:15:24 -07:00 committed by GitHub
parent e403fd3912
commit c8a64b88e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 216 additions and 353 deletions

View File

@ -1,2 +1,2 @@
1181 1182
Changed: pavel.feldman@gmail.com Mon, Oct 5, 2020 5:57:35 PM Changed: lushnikov@chromium.org Mon Oct 5 23:55:54 PDT 2020

View File

@ -128,11 +128,24 @@ class NetworkRequest {
this.httpChannel = httpChannel; this.httpChannel = httpChannel;
this._networkObserver._channelToRequest.set(this.httpChannel, this); 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.requestId = httpChannel.channelId + '';
this.navigationId = httpChannel.isMainDocumentChannel ? this.requestId : undefined; this.navigationId = httpChannel.isMainDocumentChannel ? this.requestId : undefined;
const internalCauseType = this.httpChannel.loadInfo ? this.httpChannel.loadInfo.internalContentPolicyType : Ci.nsIContentPolicy.TYPE_OTHER; const internalCauseType = this.httpChannel.loadInfo ? this.httpChannel.loadInfo.internalContentPolicyType : Ci.nsIContentPolicy.TYPE_OTHER;
this.channelKey = this.httpChannel.channelId + ':' + internalCauseType;
this._redirectedIndex = 0; this._redirectedIndex = 0;
const ignoredRedirect = redirectedFrom && !redirectedFrom._sentOnResponse; const ignoredRedirect = redirectedFrom && !redirectedFrom._sentOnResponse;
@ -140,13 +153,11 @@ class NetworkRequest {
// We just ignore redirect that did not hit the network before being redirected. // We just ignore redirect that did not hit the network before being redirected.
// This happens, for example, for automatic http->https redirects. // This happens, for example, for automatic http->https redirects.
this.navigationId = redirectedFrom.navigationId; this.navigationId = redirectedFrom.navigationId;
this.channelKey = redirectedFrom.channelKey;
} else if (redirectedFrom) { } else if (redirectedFrom) {
this.redirectedFromId = redirectedFrom.requestId; this.redirectedFromId = redirectedFrom.requestId;
this._redirectedIndex = redirectedFrom._redirectedIndex + 1; this._redirectedIndex = redirectedFrom._redirectedIndex + 1;
this.requestId = this.requestId + '-redirect' + this._redirectedIndex; this.requestId = this.requestId + '-redirect' + this._redirectedIndex;
this.navigationId = redirectedFrom.navigationId; this.navigationId = redirectedFrom.navigationId;
this.channelKey = redirectedFrom.channelKey;
// Finish previous request now. Since we inherit the listener, we could in theory // 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. // use onStopRequest, but that will only happen after the last redirect has finished.
redirectedFrom._sendOnRequestFinished(); redirectedFrom._sendOnRequestFinished();
@ -492,21 +503,9 @@ class NetworkRequest {
const loadInfo = this.httpChannel.loadInfo; const loadInfo = this.httpChannel.loadInfo;
const causeType = loadInfo?.externalContentPolicyType || Ci.nsIContentPolicy.TYPE_OTHER; const causeType = loadInfo?.externalContentPolicyType || Ci.nsIContentPolicy.TYPE_OTHER;
const internalCauseType = loadInfo?.internalContentPolicyType || 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, { pageNetwork.emit(PageNetwork.Events.Request, {
url: this.httpChannel.URI.spec, url: this.httpChannel.URI.spec,
frameId: helper.browsingContextToFrameId(browsingContext), frameId: this._frameId,
isIntercepted, isIntercepted,
requestId: this.requestId, requestId: this.requestId,
redirectedFrom: this.redirectedFromId, redirectedFrom: this.redirectedFromId,
@ -516,7 +515,7 @@ class NetworkRequest {
navigationId: this.navigationId, navigationId: this.navigationId,
cause: causeTypeToString(causeType), cause: causeTypeToString(causeType),
internalCause: causeTypeToString(internalCauseType), internalCause: causeTypeToString(internalCauseType),
}, this.channelKey); }, this._frameId);
} }
_sendOnResponse(fromCache) { _sendOnResponse(fromCache) {
@ -563,7 +562,7 @@ class NetworkRequest {
remotePort, remotePort,
status, status,
statusText, statusText,
}); }, this._frameId);
} }
_sendOnRequestFailed(error) { _sendOnRequestFailed(error) {
@ -572,7 +571,7 @@ class NetworkRequest {
pageNetwork.emit(PageNetwork.Events.RequestFailed, { pageNetwork.emit(PageNetwork.Events.RequestFailed, {
requestId: this.requestId, requestId: this.requestId,
errorCode: helper.getNetworkErrorStatusText(error), errorCode: helper.getNetworkErrorStatusText(error),
}); }, this._frameId);
} }
this._networkObserver._channelToRequest.delete(this.httpChannel); this._networkObserver._channelToRequest.delete(this.httpChannel);
} }
@ -582,7 +581,7 @@ class NetworkRequest {
if (pageNetwork) { if (pageNetwork) {
pageNetwork.emit(PageNetwork.Events.RequestFinished, { pageNetwork.emit(PageNetwork.Events.RequestFinished, {
requestId: this.requestId, requestId: this.requestId,
}); }, this._frameId);
} }
this._networkObserver._channelToRequest.delete(this.httpChannel); this._networkObserver._channelToRequest.delete(this.httpChannel);
} }

View File

@ -26,29 +26,16 @@ class DownloadInterceptor {
constructor(registry) { constructor(registry) {
this._registry = registry this._registry = registry
this._handlerToUuid = new Map(); 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. // nsIDownloadInterceptor implementation.
// //
interceptDownloadRequest(externalAppHandler, request, browsingContext, outFile) { interceptDownloadRequest(externalAppHandler, request, browsingContext, outFile) {
let pageTarget = this._registry._browserBrowsingContextToTarget.get(browsingContext); if (!(request instanceof Ci.nsIChannel))
// New page downloads won't have browsing contex. return false;
if (!pageTarget) const channel = request.QueryInterface(Ci.nsIChannel);
pageTarget = this._registry._targetForChannel(request); let pageTarget = this._registry._browserBrowsingContextToTarget.get(channel.loadInfo.browsingContext);
if (!pageTarget) if (!pageTarget)
return false; return false;
@ -324,15 +311,6 @@ class TargetRegistry {
targetForBrowser(browser) { targetForBrowser(browser) {
return this._browserToTarget.get(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 { class PageTarget {
@ -350,7 +328,6 @@ class PageTarget {
this._url = 'about:blank'; this._url = 'about:blank';
this._openerId = opener ? opener.id() : undefined; this._openerId = opener ? opener.id() : undefined;
this._channel = SimpleChannel.createForMessageManager(`browser::page[${this._targetId}]`, this._linkedBrowser.messageManager); this._channel = SimpleChannel.createForMessageManager(`browser::page[${this._targetId}]`, this._linkedBrowser.messageManager);
this._channelIds = new Set();
this._screencastInfo = undefined; this._screencastInfo = undefined;
const navigationListener = { const navigationListener = {

View File

@ -73,7 +73,7 @@ CommandLineHandler.prototype = {
if (silent) if (silent)
Services.startup.exitLastWindowClosingSurvivalArea(); Services.startup.exitLastWindowClosingSurvivalArea();
}); });
dispatcher.rootSession().registerHandler('Browser', browserHandler); dispatcher.rootSession().setHandler(browserHandler);
} }
}); });
loadFrameScript(); loadFrameScript();
@ -101,9 +101,9 @@ CommandLineHandler.prototype = {
pipe.stop(); pipe.stop();
}); });
}); });
dispatcher.rootSession().registerHandler('Browser', browserHandler); dispatcher.rootSession().setHandler(browserHandler);
loadFrameScript(); loadFrameScript();
dump(`Juggler listening to the pipe\n`); dump(`\nJuggler listening to the pipe\n`);
} }
}, },

View File

@ -197,19 +197,12 @@ class FrameTree {
const isTransferring = flag & Ci.nsIWebProgressListener.STATE_TRANSFERRING; const isTransferring = flag & Ci.nsIWebProgressListener.STATE_TRANSFERRING;
const isStop = flag & Ci.nsIWebProgressListener.STATE_STOP; 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) { if (isStart) {
// Starting a new navigation. // Starting a new navigation.
frame._pendingNavigationId = this._channelId(channel); frame._pendingNavigationId = this._channelId(channel);
frame._pendingNavigationURL = channel.URI.spec; frame._pendingNavigationURL = channel.URI.spec;
this.emit(FrameTree.Events.NavigationStarted, frame); this.emit(FrameTree.Events.NavigationStarted, frame);
} else if (isTransferring || (isStop && frame._pendingNavigationId && !status && !isDownload)) { } else if (isTransferring || (isStop && frame._pendingNavigationId && !status)) {
// Navigation is committed. // Navigation is committed.
for (const subframe of frame._children) for (const subframe of frame._children)
this._detachFrame(subframe); this._detachFrame(subframe);
@ -221,15 +214,15 @@ class FrameTree {
this.emit(FrameTree.Events.NavigationCommitted, frame); this.emit(FrameTree.Events.NavigationCommitted, frame);
if (frame === this._mainFrame) if (frame === this._mainFrame)
this.forcePageReady(); this.forcePageReady();
} else if (isStop && frame._pendingNavigationId && (status || isDownload)) { } else if (isStop && frame._pendingNavigationId && status) {
// Navigation is aborted. // Navigation is aborted.
const navigationId = frame._pendingNavigationId; const navigationId = frame._pendingNavigationId;
frame._pendingNavigationId = null; frame._pendingNavigationId = null;
frame._pendingNavigationURL = null; frame._pendingNavigationURL = null;
// Always report download navigation as failure to match other browsers. // 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); 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(); this.forcePageReady();
} }
} }
@ -245,9 +238,9 @@ class FrameTree {
} }
_channelId(channel) { _channelId(channel) {
if (channel instanceof Ci.nsIHttpChannel) { if (channel instanceof Ci.nsIIdentChannel) {
const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel); const identChannel = channel.QueryInterface(Ci.nsIIdentChannel);
return String(httpChannel.channelId); return String(identChannel.channelId);
} }
return helper.generateId(); return helper.generateId();
} }

View File

@ -12,10 +12,7 @@ juggler.jar:
content/protocol/Protocol.js (protocol/Protocol.js) content/protocol/Protocol.js (protocol/Protocol.js)
content/protocol/Dispatcher.js (protocol/Dispatcher.js) content/protocol/Dispatcher.js (protocol/Dispatcher.js)
content/protocol/PageHandler.js (protocol/PageHandler.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/BrowserHandler.js (protocol/BrowserHandler.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)
content/content/PageAgent.js (content/PageAgent.js) content/content/PageAgent.js (content/PageAgent.js)

View File

@ -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;

View File

@ -8,9 +8,6 @@ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
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} = ChromeUtils.import('chrome://juggler/content/Helper.js');
const {PageHandler} = ChromeUtils.import("chrome://juggler/content/protocol/PageHandler.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(); const helper = new Helper();
@ -27,7 +24,7 @@ class BrowserHandler {
this._onclose = onclose; this._onclose = onclose;
} }
async enable({attachToDefaultContext}) { async ['Browser.enable']({attachToDefaultContext}) {
if (this._enabled) if (this._enabled)
return; return;
this._enabled = true; this._enabled = true;
@ -50,7 +47,7 @@ class BrowserHandler {
this._onTargetCreated(target); this._onTargetCreated(target);
} }
async createBrowserContext({removeOnDetach}) { async ['Browser.createBrowserContext']({removeOnDetach}) {
if (!this._enabled) if (!this._enabled)
throw new Error('Browser domain is not enabled'); throw new Error('Browser domain is not enabled');
const browserContext = this._targetRegistry.createBrowserContext(removeOnDetach); const browserContext = this._targetRegistry.createBrowserContext(removeOnDetach);
@ -58,7 +55,7 @@ class BrowserHandler {
return {browserContextId: browserContext.browserContextId}; return {browserContextId: browserContext.browserContextId};
} }
async removeBrowserContext({browserContextId}) { async ['Browser.removeBrowserContext']({browserContextId}) {
if (!this._enabled) if (!this._enabled)
throw new Error('Browser domain is not enabled'); throw new Error('Browser domain is not enabled');
await this._targetRegistry.browserContextForId(browserContextId).destroy(); await this._targetRegistry.browserContextForId(browserContextId).destroy();
@ -90,18 +87,11 @@ class BrowserHandler {
const channel = target.channel(); const channel = target.channel();
const session = this._dispatcher.createSession(); const session = this._dispatcher.createSession();
this._attachedSessions.set(target, session); 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', { this._session.emitEvent('Browser.attachedToTarget', {
sessionId: session.sessionId(), sessionId: session.sessionId(),
targetInfo: target.info() targetInfo: target.info()
}); });
session.setHandler(new PageHandler(target, session, channel));
} }
_onTargetDestroyed(target) { _onTargetDestroyed(target) {
@ -124,12 +114,12 @@ class BrowserHandler {
this._session.emitEvent('Browser.downloadFinished', downloadInfo); this._session.emitEvent('Browser.downloadFinished', downloadInfo);
} }
async newPage({browserContextId}) { async ['Browser.newPage']({browserContextId}) {
const targetId = await this._targetRegistry.newPage({browserContextId}); const targetId = await this._targetRegistry.newPage({browserContextId});
return {targetId}; return {targetId};
} }
async close() { async ['Browser.close']() {
let browserWindow = Services.wm.getMostRecentWindow( let browserWindow = Services.wm.getMostRecentWindow(
"navigator:browser" "navigator:browser"
); );
@ -140,109 +130,109 @@ class BrowserHandler {
Services.startup.quit(Ci.nsIAppStartup.eForceQuit); 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); await this._targetRegistry.browserContextForId(browserContextId).grantPermissions(origin, permissions);
} }
resetPermissions({browserContextId}) { async ['Browser.resetPermissions']({browserContextId}) {
this._targetRegistry.browserContextForId(browserContextId).resetPermissions(); this._targetRegistry.browserContextForId(browserContextId).resetPermissions();
} }
setExtraHTTPHeaders({browserContextId, headers}) { ['Browser.setExtraHTTPHeaders']({browserContextId, headers}) {
this._targetRegistry.browserContextForId(browserContextId).extraHTTPHeaders = headers; this._targetRegistry.browserContextForId(browserContextId).extraHTTPHeaders = headers;
} }
setHTTPCredentials({browserContextId, credentials}) { ['Browser.setHTTPCredentials']({browserContextId, credentials}) {
this._targetRegistry.browserContextForId(browserContextId).httpCredentials = nullToUndefined(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}); 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); const browserContext = this._targetRegistry.browserContextForId(browserContextId);
browserContext.setProxy({ type, host, port, bypass, username, password }); browserContext.setProxy({ type, host, port, bypass, username, password });
} }
setRequestInterception({browserContextId, enabled}) { ['Browser.setRequestInterception']({browserContextId, enabled}) {
this._targetRegistry.browserContextForId(browserContextId).requestInterceptionEnabled = enabled; this._targetRegistry.browserContextForId(browserContextId).requestInterceptionEnabled = enabled;
} }
setIgnoreHTTPSErrors({browserContextId, ignoreHTTPSErrors}) { ['Browser.setIgnoreHTTPSErrors']({browserContextId, ignoreHTTPSErrors}) {
this._targetRegistry.browserContextForId(browserContextId).setIgnoreHTTPSErrors(nullToUndefined(ignoreHTTPSErrors)); this._targetRegistry.browserContextForId(browserContextId).setIgnoreHTTPSErrors(nullToUndefined(ignoreHTTPSErrors));
} }
setDownloadOptions({browserContextId, downloadOptions}) { ['Browser.setDownloadOptions']({browserContextId, downloadOptions}) {
this._targetRegistry.browserContextForId(browserContextId).downloadOptions = nullToUndefined(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)); 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)); 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)); 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}); 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); 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)); 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)); 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)); 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)); 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)); 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)); await this._targetRegistry.browserContextForId(browserContextId).setDefaultViewport(nullToUndefined(viewport));
} }
async addScriptToEvaluateOnNewDocument({browserContextId, script}) { async ['Browser.addScriptToEvaluateOnNewDocument']({browserContextId, script}) {
await this._targetRegistry.browserContextForId(browserContextId).addScriptToEvaluateOnNewDocument(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); await this._targetRegistry.browserContextForId(browserContextId).addBinding(name, script);
} }
setCookies({browserContextId, cookies}) { ['Browser.setCookies']({browserContextId, cookies}) {
this._targetRegistry.browserContextForId(browserContextId).setCookies(cookies); this._targetRegistry.browserContextForId(browserContextId).setCookies(cookies);
} }
clearCookies({browserContextId}) { ['Browser.clearCookies']({browserContextId}) {
this._targetRegistry.browserContextForId(browserContextId).clearCookies(); this._targetRegistry.browserContextForId(browserContextId).clearCookies();
} }
getCookies({browserContextId}) { ['Browser.getCookies']({browserContextId}) {
const cookies = this._targetRegistry.browserContextForId(browserContextId).getCookies(); const cookies = this._targetRegistry.browserContextForId(browserContextId).getCookies();
return {cookies}; return {cookies};
} }
async getInfo() { async ['Browser.getInfo']() {
const version = Components.classes["@mozilla.org/xre/app-info;1"] const version = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULAppInfo) .getService(Components.interfaces.nsIXULAppInfo)
.version; .version;

View File

@ -66,7 +66,7 @@ class Dispatcher {
if (!checkScheme(descriptor.params || {}, params, details)) 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}`); 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 = {}; details = {};
if ((descriptor.returns || result) && !checkScheme(descriptor.returns, result, details)) if ((descriptor.returns || result) && !checkScheme(descriptor.returns, result, details))
@ -97,24 +97,21 @@ class ProtocolSession {
constructor(dispatcher, sessionId) { constructor(dispatcher, sessionId) {
this._sessionId = sessionId; this._sessionId = sessionId;
this._dispatcher = dispatcher; this._dispatcher = dispatcher;
this._handlers = new Map(); this._handler = null;
} }
sessionId() { sessionId() {
return this._sessionId; return this._sessionId;
} }
registerHandler(domainName, handler) { setHandler(handler) {
this._handlers.set(domainName, handler); this._handler = handler;
} }
_dispose() { _dispose() {
for (const [domainName, handler] of this._handlers) { if (this._handler)
if (typeof handler.dispose !== 'function') this._handler.dispose();
throw new Error(`Handler for "${domainName}" domain does not define |dispose| method!`); this._handler = null;
handler.dispose();
}
this._handlers.clear();
this._dispatcher = null; this._dispatcher = null;
} }
@ -124,13 +121,12 @@ class ProtocolSession {
this._dispatcher._emitEvent(this._sessionId, eventName, params); this._dispatcher._emitEvent(this._sessionId, eventName, params);
} }
async dispatch(domainName, methodName, params) { async dispatch(method, params) {
const handler = this._handlers.get(domainName); if (!this._handler)
if (!handler) throw new Error(`Session does not have a handler!`);
throw new Error(`Domain "${domainName}" does not exist`); if (!this._handler[method])
if (!handler[methodName]) throw new Error(`Handler for does not implement method "${method}"`);
throw new Error(`Handler for domain "${domainName}" does not implement method "${methodName}"`); return await this._handler[method](params);
return await handler[methodName](params);
} }
} }

View File

@ -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;

View File

@ -6,6 +6,7 @@
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const {NetworkObserver, PageNetwork} = ChromeUtils.import('chrome://juggler/content/NetworkObserver.js');
const Cc = Components.classes; const Cc = Components.classes;
const Ci = Components.interfaces; const Ci = Components.interfaces;
@ -59,19 +60,28 @@ class PageHandler {
this._session = session; this._session = session;
this._contentChannel = contentChannel; this._contentChannel = contentChannel;
this._contentPage = contentChannel.connect('page'); this._contentPage = contentChannel.connect('page');
this._contentRuntime = contentChannel.connect('runtime');
this._workers = new Map(); this._workers = new Map();
this._pageTarget = target;
this._browser = target.linkedBrowser();
this._dialogs = new Map();
this._pageNetwork = NetworkObserver.instance().pageNetworkForTarget(target);
const emitProtocolEvent = eventName => { const emitProtocolEvent = eventName => {
return (...args) => this._session.emitEvent(eventName, ...args); return (...args) => this._session.emitEvent(eventName, ...args);
} }
this._reportedFrameIds = new Set();
this._networkEventsForUnreportedFrameIds = new Map();
this._eventListeners = [ this._eventListeners = [
contentChannel.register('page', { contentChannel.register('page', {
pageBindingCalled: emitProtocolEvent('Page.bindingCalled'), pageBindingCalled: emitProtocolEvent('Page.bindingCalled'),
pageDispatchMessageFromWorker: emitProtocolEvent('Page.dispatchMessageFromWorker'), pageDispatchMessageFromWorker: emitProtocolEvent('Page.dispatchMessageFromWorker'),
pageEventFired: emitProtocolEvent('Page.eventFired'), pageEventFired: emitProtocolEvent('Page.eventFired'),
pageFileChooserOpened: emitProtocolEvent('Page.fileChooserOpened'), pageFileChooserOpened: emitProtocolEvent('Page.fileChooserOpened'),
pageFrameAttached: emitProtocolEvent('Page.frameAttached'), pageFrameAttached: this._onFrameAttached.bind(this),
pageFrameDetached: emitProtocolEvent('Page.frameDetached'), pageFrameDetached: emitProtocolEvent('Page.frameDetached'),
pageLinkClicked: emitProtocolEvent('Page.linkClicked'), pageLinkClicked: emitProtocolEvent('Page.linkClicked'),
pageWillOpenNewWindowAsynchronously: emitProtocolEvent('Page.willOpenNewWindowAsynchronously'), pageWillOpenNewWindowAsynchronously: emitProtocolEvent('Page.willOpenNewWindowAsynchronously'),
@ -84,12 +94,35 @@ class PageHandler {
pageWorkerCreated: this._onWorkerCreated.bind(this), pageWorkerCreated: this._onWorkerCreated.bind(this),
pageWorkerDestroyed: this._onWorkerDestroyed.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}) { _onWorkerCreated({workerId, frameId, url}) {
@ -107,49 +140,45 @@ class PageHandler {
this._session.emitEvent('Page.workerDestroyed', {workerId}); 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. // Postpone target close to deliver response in session.
Services.tm.dispatchToMainThread(() => { Services.tm.dispatchToMainThread(() => {
this._pageTarget.close(runBeforeUnload); 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() { async dispose() {
this._contentPage.dispose(); this._contentPage.dispose();
this._contentRuntime.dispose();
helper.removeListeners(this._eventListeners); helper.removeListeners(this._eventListeners);
if (this._pageTarget.screencastInfo()) if (this._pageTarget.screencastInfo())
await this._pageTarget.stopVideoRecording().catch(e => dump(`stopVideoRecording failed:\n${e}\n`)); 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); 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); return await this._contentPage.send('setFileInputFiles', options);
} }
async setEmulatedMedia(options) { async ['Page.setEmulatedMedia'](options) {
return await this._contentPage.send('setEmulatedMedia', options); return await this._contentPage.send('setEmulatedMedia', options);
} }
async bringToFront(options) { async ['Page.bringToFront'](options) {
this._pageTarget._window.focus(); this._pageTarget._window.focus();
} }
async setCacheDisabled(options) { async ['Page.setCacheDisabled'](options) {
return await this._contentPage.send('setCacheDisabled', options); return await this._contentPage.send('setCacheDisabled', options);
} }
async addBinding(options) { async ['Page.addBinding'](options) {
return await this._contentPage.send('addBinding', options); return await this._contentPage.send('addBinding', options);
} }
async adoptNode(options) { async ['Page.adoptNode'](options) {
return await this._contentPage.send('adoptNode', options); return await this._contentPage.send('adoptNode', options);
} }
async screenshot(options) { async ['Page.screenshot'](options) {
return await this._contentPage.send('screenshot', options); return await this._contentPage.send('screenshot', options);
} }
async getBoundingBox(options) { async ['Page.getBoundingBox'](options) {
return await this._contentPage.send('getBoundingBox', options); return await this._contentPage.send('getBoundingBox', options);
} }
async getContentQuads(options) { async ['Page.getContentQuads'](options) {
return await this._contentPage.send('getContentQuads', options); return await this._contentPage.send('getContentQuads', options);
} }
/** /**
* @param {{frameId: string, url: string}} options * @param {{frameId: string, url: string}} options
*/ */
async navigate(options) { async ['Page.navigate'](options) {
return await this._contentPage.send('navigate', options); return await this._contentPage.send('navigate', options);
} }
/** /**
* @param {{frameId: string, url: string}} options * @param {{frameId: string, url: string}} options
*/ */
async goBack(options) { async ['Page.goBack'](options) {
return await this._contentPage.send('goBack', options); return await this._contentPage.send('goBack', options);
} }
/** /**
* @param {{frameId: string, url: string}} options * @param {{frameId: string, url: string}} options
*/ */
async goForward(options) { async ['Page.goForward'](options) {
return await this._contentPage.send('goForward', options); return await this._contentPage.send('goForward', options);
} }
/** /**
* @param {{frameId: string, url: string}} options * @param {{frameId: string, url: string}} options
*/ */
async reload(options) { async ['Page.reload'](options) {
return await this._contentPage.send('reload', options); return await this._contentPage.send('reload', options);
} }
async describeNode(options) { async ['Page.describeNode'](options) {
return await this._contentPage.send('describeNode', options); return await this._contentPage.send('describeNode', options);
} }
async scrollIntoViewIfNeeded(options) { async ['Page.scrollIntoViewIfNeeded'](options) {
return await this._contentPage.send('scrollIntoViewIfNeeded', options); return await this._contentPage.send('scrollIntoViewIfNeeded', options);
} }
async addScriptToEvaluateOnNewDocument(options) { async ['Page.addScriptToEvaluateOnNewDocument'](options) {
return await this._contentPage.send('addScriptToEvaluateOnNewDocument', options); return await this._contentPage.send('addScriptToEvaluateOnNewDocument', options);
} }
async removeScriptToEvaluateOnNewDocument(options) { async ['Page.removeScriptToEvaluateOnNewDocument'](options) {
return await this._contentPage.send('removeScriptToEvaluateOnNewDocument', options); return await this._contentPage.send('removeScriptToEvaluateOnNewDocument', options);
} }
async dispatchKeyEvent(options) { async ['Page.dispatchKeyEvent'](options) {
return await this._contentPage.send('dispatchKeyEvent', options); return await this._contentPage.send('dispatchKeyEvent', options);
} }
async dispatchTouchEvent(options) { async ['Page.dispatchTouchEvent'](options) {
return await this._contentPage.send('dispatchTouchEvent', options); return await this._contentPage.send('dispatchTouchEvent', options);
} }
async dispatchMouseEvent(options) { async ['Page.dispatchMouseEvent'](options) {
return await this._contentPage.send('dispatchMouseEvent', options); return await this._contentPage.send('dispatchMouseEvent', options);
} }
async insertText(options) { async ['Page.insertText'](options) {
return await this._contentPage.send('insertText', options); return await this._contentPage.send('insertText', options);
} }
async crash(options) { async ['Page.crash'](options) {
return await this._contentPage.send('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); const dialog = this._dialogs.get(dialogId);
if (!dialog) if (!dialog)
throw new Error('Failed to find dialog with id = ' + dialogId); throw new Error('Failed to find dialog with id = ' + dialogId);
@ -289,18 +365,18 @@ class PageHandler {
dialog.dismiss(); dialog.dismiss();
} }
async setInterceptFileChooserDialog(options) { async ['Page.setInterceptFileChooserDialog'](options) {
return await this._contentPage.send('setInterceptFileChooserDialog', options); return await this._contentPage.send('setInterceptFileChooserDialog', options);
} }
async sendMessageToWorker({workerId, message}) { async ['Page.sendMessageToWorker']({workerId, message}) {
const worker = this._workers.get(workerId); const worker = this._workers.get(workerId);
if (!worker) if (!worker)
throw new Error('ERROR: cannot find worker with id ' + workerId); throw new Error('ERROR: cannot find worker with id ' + workerId);
return await worker.sendMessage(JSON.parse(message)); return await worker.sendMessage(JSON.parse(message));
} }
async stopVideoRecording() { async ['Page.stopVideoRecording']() {
await this._pageTarget.stopVideoRecording(); await this._pageTarget.stopVideoRecording();
} }
} }

View File

@ -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;

View File

@ -240,10 +240,6 @@ pref("security.notification_enable_delay", 0);
// Ensure blocklist updates do not hit the network // Ensure blocklist updates do not hit the network
pref("services.settings.server", ""); 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 // Do not automatically fill sign-in forms with known usernames and
// passwords // passwords
pref("signon.autofillForms", false); pref("signon.autofillForms", false);