mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
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
|
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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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 = {
|
||||||
|
@ -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`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 {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;
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
// 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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user