chore: update browser patches as of Dec 13, 2022 (#20297)
This commit is contained in:
Родитель
ba0189f8d7
Коммит
6c5317bd31
|
@ -1,3 +1,3 @@
|
||||||
REMOTE_URL="https://github.com/mozilla/gecko-dev"
|
REMOTE_URL="https://github.com/mozilla/gecko-dev"
|
||||||
BASE_BRANCH="release"
|
BASE_BRANCH="release"
|
||||||
BASE_REVISION="fd854580ffc6fba6a0acdf335c96a1b24b976cb9"
|
BASE_REVISION="e2956def6c181ca7375897992c5c821a5a6c886d"
|
||||||
|
|
|
@ -6,6 +6,16 @@ const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerat
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
class Helper {
|
class Helper {
|
||||||
|
decorateAsEventEmitter(objectToDecorate) {
|
||||||
|
const { EventEmitter } = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
|
||||||
|
const emitter = new EventEmitter();
|
||||||
|
objectToDecorate.on = emitter.on.bind(emitter);
|
||||||
|
objectToDecorate.addEventListener = emitter.on.bind(emitter);
|
||||||
|
objectToDecorate.off = emitter.off.bind(emitter);
|
||||||
|
objectToDecorate.removeEventListener = emitter.off.bind(emitter);
|
||||||
|
objectToDecorate.once = emitter.once.bind(emitter);
|
||||||
|
objectToDecorate.emit = emitter.emit.bind(emitter);
|
||||||
|
}
|
||||||
|
|
||||||
addObserver(handler, topic) {
|
addObserver(handler, topic) {
|
||||||
Services.obs.addObserver(handler, topic);
|
Services.obs.addObserver(handler, topic);
|
||||||
|
@ -19,7 +29,15 @@ class Helper {
|
||||||
|
|
||||||
addEventListener(receiver, eventName, handler) {
|
addEventListener(receiver, eventName, handler) {
|
||||||
receiver.addEventListener(eventName, handler);
|
receiver.addEventListener(eventName, handler);
|
||||||
return () => receiver.removeEventListener(eventName, handler);
|
return () => {
|
||||||
|
try {
|
||||||
|
receiver.removeEventListener(eventName, handler);
|
||||||
|
} catch (e) {
|
||||||
|
// This could fail when window has navigated cross-process
|
||||||
|
// and we remove the listener from WindowProxy.
|
||||||
|
dump(`WARNING: removeEventListener throws ${e} at ${new Error().stack}\n`);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
awaitEvent(receiver, eventName) {
|
awaitEvent(receiver, eventName) {
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { TargetRegistry } = ChromeUtils.import('chrome://juggler/content/TargetRegistry.js');
|
||||||
|
const { Helper } = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||||
|
|
||||||
|
const helper = new Helper();
|
||||||
|
|
||||||
|
var EXPORTED_SYMBOLS = ['JugglerFrameParent'];
|
||||||
|
|
||||||
|
class JugglerFrameParent extends JSWindowActorParent {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
receiveMessage() { }
|
||||||
|
|
||||||
|
async actorCreated() {
|
||||||
|
// Only interested in main frames for now.
|
||||||
|
if (this.browsingContext.parent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._target = TargetRegistry.instance()?.targetForBrowserId(this.browsingContext.browserId);
|
||||||
|
if (!this._target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.actorName = `browser::page[${this._target.id()}]/${this.browsingContext.browserId}/${this.browsingContext.id}/${this._target.nextActorSequenceNumber()}`;
|
||||||
|
this._target.setActor(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
didDestroy() {
|
||||||
|
if (!this._target)
|
||||||
|
return;
|
||||||
|
this._target.removeActor(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const {EventEmitter} = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
|
|
||||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
|
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
|
||||||
|
@ -41,7 +40,7 @@ class PageNetwork {
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(target) {
|
constructor(target) {
|
||||||
EventEmitter.decorate(this);
|
helper.decorateAsEventEmitter(this);
|
||||||
this._target = target;
|
this._target = target;
|
||||||
this._extraHTTPHeaders = null;
|
this._extraHTTPHeaders = null;
|
||||||
this._responseStorage = new ResponseStorage(MAX_RESPONSE_STORAGE_SIZE, MAX_RESPONSE_STORAGE_SIZE / 10);
|
this._responseStorage = new ResponseStorage(MAX_RESPONSE_STORAGE_SIZE, MAX_RESPONSE_STORAGE_SIZE / 10);
|
||||||
|
@ -217,8 +216,9 @@ class NetworkRequest {
|
||||||
_onInternalRedirect(newChannel) {
|
_onInternalRedirect(newChannel) {
|
||||||
// Intercepted requests produce "internal redirects" - this is both for our own
|
// Intercepted requests produce "internal redirects" - this is both for our own
|
||||||
// interception and service workers.
|
// interception and service workers.
|
||||||
// An internal redirect has the same channelId, inherits notificationCallbacks and
|
// An internal redirect does not necessarily have the same channelId,
|
||||||
// listener, and should be used instead of an old channel.
|
// but inherits notificationCallbacks and the listener,
|
||||||
|
// and should be used instead of an old channel.
|
||||||
this._networkObserver._channelToRequest.delete(this.httpChannel);
|
this._networkObserver._channelToRequest.delete(this.httpChannel);
|
||||||
this.httpChannel = newChannel;
|
this.httpChannel = newChannel;
|
||||||
this._networkObserver._channelToRequest.set(this.httpChannel, this);
|
this._networkObserver._channelToRequest.set(this.httpChannel, this);
|
||||||
|
@ -363,7 +363,7 @@ class NetworkRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
const browserContext = pageNetwork._target.browserContext();
|
const browserContext = pageNetwork._target.browserContext();
|
||||||
if (browserContext.settings.onlineOverride === 'offline') {
|
if (browserContext.crossProcessCookie.settings.onlineOverride === 'offline') {
|
||||||
// Implement offline.
|
// Implement offline.
|
||||||
this.abort(Cr.NS_ERROR_OFFLINE);
|
this.abort(Cr.NS_ERROR_OFFLINE);
|
||||||
return;
|
return;
|
||||||
|
@ -458,7 +458,7 @@ class NetworkRequest {
|
||||||
const browserContext = pageNetwork._target.browserContext();
|
const browserContext = pageNetwork._target.browserContext();
|
||||||
if (browserContext.requestInterceptionEnabled)
|
if (browserContext.requestInterceptionEnabled)
|
||||||
return true;
|
return true;
|
||||||
if (browserContext.settings.onlineOverride === 'offline')
|
if (browserContext.crossProcessCookie.settings.onlineOverride === 'offline')
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -581,7 +581,7 @@ class NetworkObserver {
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(targetRegistry) {
|
constructor(targetRegistry) {
|
||||||
EventEmitter.decorate(this);
|
helper.decorateAsEventEmitter(this);
|
||||||
NetworkObserver._instance = this;
|
NetworkObserver._instance = this;
|
||||||
|
|
||||||
this._targetRegistry = targetRegistry;
|
this._targetRegistry = targetRegistry;
|
||||||
|
|
|
@ -9,6 +9,12 @@
|
||||||
const SIMPLE_CHANNEL_MESSAGE_NAME = 'juggler:simplechannel';
|
const SIMPLE_CHANNEL_MESSAGE_NAME = 'juggler:simplechannel';
|
||||||
|
|
||||||
class SimpleChannel {
|
class SimpleChannel {
|
||||||
|
static createForActor(actor) {
|
||||||
|
const channel = new SimpleChannel('');
|
||||||
|
channel.bindToActor(actor);
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
static createForMessageManager(name, mm) {
|
static createForMessageManager(name, mm) {
|
||||||
const channel = new SimpleChannel(name);
|
const channel = new SimpleChannel(name);
|
||||||
|
|
||||||
|
@ -32,15 +38,34 @@ class SimpleChannel {
|
||||||
this._pendingMessages = new Map();
|
this._pendingMessages = new Map();
|
||||||
this._handlers = new Map();
|
this._handlers = new Map();
|
||||||
this._bufferedIncomingMessages = [];
|
this._bufferedIncomingMessages = [];
|
||||||
this._bufferedOutgoingMessages = [];
|
|
||||||
this.transport = {
|
this.transport = {
|
||||||
sendMessage: null,
|
sendMessage: null,
|
||||||
dispose: null,
|
dispose: () => {},
|
||||||
};
|
};
|
||||||
this._ready = false;
|
this._ready = false;
|
||||||
this._disposed = false;
|
this._disposed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bindToActor(actor) {
|
||||||
|
this.resetTransport();
|
||||||
|
this._name = actor.actorName;
|
||||||
|
const oldReceiveMessage = actor.receiveMessage;
|
||||||
|
actor.receiveMessage = message => this._onMessage(message.data);
|
||||||
|
this.setTransport({
|
||||||
|
sendMessage: obj => actor.sendAsyncMessage(SIMPLE_CHANNEL_MESSAGE_NAME, obj),
|
||||||
|
dispose: () => actor.receiveMessage = oldReceiveMessage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resetTransport() {
|
||||||
|
this.transport.dispose();
|
||||||
|
this.transport = {
|
||||||
|
sendMessage: null,
|
||||||
|
dispose: () => {},
|
||||||
|
};
|
||||||
|
this._ready = false;
|
||||||
|
}
|
||||||
|
|
||||||
setTransport(transport) {
|
setTransport(transport) {
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
// connection handshake:
|
// connection handshake:
|
||||||
|
@ -59,9 +84,8 @@ class SimpleChannel {
|
||||||
if (this._ready)
|
if (this._ready)
|
||||||
return;
|
return;
|
||||||
this._ready = true;
|
this._ready = true;
|
||||||
for (const msg of this._bufferedOutgoingMessages)
|
for (const { message } of this._pendingMessages.values())
|
||||||
this.transport.sendMessage(msg);
|
this.transport.sendMessage(message);
|
||||||
this._bufferedOutgoingMessages = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
|
@ -121,14 +145,12 @@ class SimpleChannel {
|
||||||
if (this._disposed)
|
if (this._disposed)
|
||||||
throw new Error(`ERROR: channel ${this._name} is already disposed! Cannot send "${methodName}" to "${namespace}"`);
|
throw new Error(`ERROR: channel ${this._name} is already disposed! Cannot send "${methodName}" to "${namespace}"`);
|
||||||
const id = ++this._messageId;
|
const id = ++this._messageId;
|
||||||
const promise = new Promise((resolve, reject) => {
|
|
||||||
this._pendingMessages.set(id, {connectorId, resolve, reject, methodName, namespace});
|
|
||||||
});
|
|
||||||
const message = {requestId: id, methodName, params, namespace};
|
const message = {requestId: id, methodName, params, namespace};
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
this._pendingMessages.set(id, {connectorId, resolve, reject, methodName, namespace, message});
|
||||||
|
});
|
||||||
if (this._ready)
|
if (this._ready)
|
||||||
this.transport.sendMessage(message);
|
this.transport.sendMessage(message);
|
||||||
else
|
|
||||||
this._bufferedOutgoingMessages.push(message);
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,12 +165,19 @@ class SimpleChannel {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.responseId) {
|
if (data.responseId) {
|
||||||
const {resolve, reject} = this._pendingMessages.get(data.responseId);
|
const message = this._pendingMessages.get(data.responseId);
|
||||||
|
if (!message) {
|
||||||
|
// During corss-process navigation, we might receive a response for
|
||||||
|
// the message sent by another process.
|
||||||
|
// TODO: consider events that are marked as "no-response" to avoid
|
||||||
|
// unneeded responses altogether.
|
||||||
|
return;
|
||||||
|
}
|
||||||
this._pendingMessages.delete(data.responseId);
|
this._pendingMessages.delete(data.responseId);
|
||||||
if (data.error)
|
if (data.error)
|
||||||
reject(new Error(data.error));
|
message.reject(new Error(data.error));
|
||||||
else
|
else
|
||||||
resolve(data.result);
|
message.resolve(data.result);
|
||||||
} else if (data.requestId) {
|
} else if (data.requestId) {
|
||||||
const namespace = data.namespace;
|
const namespace = data.namespace;
|
||||||
const handler = this._handlers.get(namespace);
|
const handler = this._handlers.get(namespace);
|
||||||
|
@ -169,9 +198,7 @@ class SimpleChannel {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dump(`
|
dump(`WARNING: unknown message in channel "${this._name}": ${JSON.stringify(data)}\n`);
|
||||||
ERROR: unknown message in channel "${this._name}": ${JSON.stringify(data)}
|
|
||||||
`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
const {EventEmitter} = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
|
|
||||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||||
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
@ -38,7 +37,7 @@ class DownloadInterceptor {
|
||||||
if (!(request instanceof Ci.nsIChannel))
|
if (!(request instanceof Ci.nsIChannel))
|
||||||
return false;
|
return false;
|
||||||
const channel = request.QueryInterface(Ci.nsIChannel);
|
const channel = request.QueryInterface(Ci.nsIChannel);
|
||||||
let pageTarget = this._registry._browserBrowsingContextToTarget.get(channel.loadInfo.browsingContext.top);
|
let pageTarget = this._registry._browserIdToTarget.get(channel.loadInfo.browsingContext.top.browserId);
|
||||||
if (!pageTarget)
|
if (!pageTarget)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -57,7 +56,7 @@ class DownloadInterceptor {
|
||||||
try {
|
try {
|
||||||
file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
|
file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dump(`interceptDownloadRequest failed to create file: ${e}\n`);
|
dump(`WARNING: interceptDownloadRequest failed to create file: ${e}\n`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +67,7 @@ class DownloadInterceptor {
|
||||||
uuid,
|
uuid,
|
||||||
browserContextId: browserContext.browserContextId,
|
browserContextId: browserContext.browserContextId,
|
||||||
pageTargetId: pageTarget.id(),
|
pageTargetId: pageTarget.id(),
|
||||||
|
frameId: helper.browsingContextToFrameId(channel.loadInfo.browsingContext),
|
||||||
url: request.name,
|
url: request.name,
|
||||||
suggestedFileName: externalAppHandler.suggestedFileName,
|
suggestedFileName: externalAppHandler.suggestedFileName,
|
||||||
};
|
};
|
||||||
|
@ -103,13 +103,18 @@ class DownloadInterceptor {
|
||||||
const screencastService = Cc['@mozilla.org/juggler/screencast;1'].getService(Ci.nsIScreencastService);
|
const screencastService = Cc['@mozilla.org/juggler/screencast;1'].getService(Ci.nsIScreencastService);
|
||||||
|
|
||||||
class TargetRegistry {
|
class TargetRegistry {
|
||||||
|
static instance() {
|
||||||
|
return TargetRegistry._instance || null;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
EventEmitter.decorate(this);
|
helper.decorateAsEventEmitter(this);
|
||||||
|
TargetRegistry._instance = this;
|
||||||
|
|
||||||
this._browserContextIdToBrowserContext = new Map();
|
this._browserContextIdToBrowserContext = new Map();
|
||||||
this._userContextIdToBrowserContext = new Map();
|
this._userContextIdToBrowserContext = new Map();
|
||||||
this._browserToTarget = new Map();
|
this._browserToTarget = new Map();
|
||||||
this._browserBrowsingContextToTarget = new Map();
|
this._browserIdToTarget = new Map();
|
||||||
|
|
||||||
this._browserProxy = null;
|
this._browserProxy = null;
|
||||||
|
|
||||||
|
@ -136,20 +141,14 @@ class TargetRegistry {
|
||||||
}
|
}
|
||||||
}, 'oop-frameloader-crashed');
|
}, 'oop-frameloader-crashed');
|
||||||
|
|
||||||
Services.mm.addMessageListener('juggler:content-ready', {
|
helper.addObserver((browsingContext, topic, why) => {
|
||||||
receiveMessage: message => {
|
if (why === 'replace') {
|
||||||
const linkedBrowser = message.target;
|
// Top-level browsingContext is replaced on cross-process navigations.
|
||||||
const target = this._browserToTarget.get(linkedBrowser);
|
const target = this._browserIdToTarget.get(browsingContext.browserId);
|
||||||
if (!target)
|
if (target)
|
||||||
return;
|
target.replaceTopBrowsingContext(browsingContext);
|
||||||
|
}
|
||||||
return {
|
}, 'browsing-context-attached');
|
||||||
initScripts: target.browserContext().initScripts,
|
|
||||||
bindings: target.browserContext().bindings,
|
|
||||||
settings: target.browserContext().settings,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const onTabOpenListener = (appWindow, window, event) => {
|
const onTabOpenListener = (appWindow, window, event) => {
|
||||||
const tab = event.target;
|
const tab = event.target;
|
||||||
|
@ -161,7 +160,7 @@ class TargetRegistry {
|
||||||
if (openerContext) {
|
if (openerContext) {
|
||||||
// Popups usually have opener context. Get top context for the case when opener is
|
// Popups usually have opener context. Get top context for the case when opener is
|
||||||
// an iframe.
|
// an iframe.
|
||||||
openerTarget = this._browserBrowsingContextToTarget.get(openerContext.top);
|
openerTarget = this._browserIdToTarget.get(openerContext.top.browserId);
|
||||||
} else if (tab.openerTab) {
|
} else if (tab.openerTab) {
|
||||||
// Noopener popups from the same window have opener tab instead.
|
// Noopener popups from the same window have opener tab instead.
|
||||||
openerTarget = this._browserToTarget.get(tab.openerTab.linkedBrowser);
|
openerTarget = this._browserToTarget.get(tab.openerTab.linkedBrowser);
|
||||||
|
@ -169,13 +168,7 @@ class TargetRegistry {
|
||||||
if (!browserContext)
|
if (!browserContext)
|
||||||
throw new Error(`Internal error: cannot find context for userContextId=${userContextId}`);
|
throw new Error(`Internal error: cannot find context for userContextId=${userContextId}`);
|
||||||
const target = new PageTarget(this, window, tab, browserContext, openerTarget);
|
const target = new PageTarget(this, window, tab, browserContext, openerTarget);
|
||||||
target.updateUserAgent();
|
target.updateOverridesForBrowsingContext(tab.linkedBrowser.browsingContext);
|
||||||
target.updatePlatform();
|
|
||||||
target.updateJavaScriptDisabled();
|
|
||||||
target.updateTouchOverride();
|
|
||||||
target.updateColorSchemeOverride();
|
|
||||||
target.updateReducedMotionOverride();
|
|
||||||
target.updateForcedColorsOverride();
|
|
||||||
if (!hasExplicitSize)
|
if (!hasExplicitSize)
|
||||||
target.updateViewportSize();
|
target.updateViewportSize();
|
||||||
if (browserContext.videoRecordingOptions)
|
if (browserContext.videoRecordingOptions)
|
||||||
|
@ -329,7 +322,7 @@ class TargetRegistry {
|
||||||
target = this._browserToTarget.get(browser);
|
target = this._browserToTarget.get(browser);
|
||||||
}
|
}
|
||||||
browser.focus();
|
browser.focus();
|
||||||
if (browserContext.settings.timezoneId) {
|
if (browserContext.crossProcessCookie.settings.timezoneId) {
|
||||||
if (await target.hasFailedToOverrideTimezone())
|
if (await target.hasFailedToOverrideTimezone())
|
||||||
throw new Error('Failed to override timezone');
|
throw new Error('Failed to override timezone');
|
||||||
}
|
}
|
||||||
|
@ -343,11 +336,15 @@ class TargetRegistry {
|
||||||
targetForBrowser(browser) {
|
targetForBrowser(browser) {
|
||||||
return this._browserToTarget.get(browser);
|
return this._browserToTarget.get(browser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targetForBrowserId(browserId) {
|
||||||
|
return this._browserIdToTarget.get(browserId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PageTarget {
|
class PageTarget {
|
||||||
constructor(registry, win, tab, browserContext, opener) {
|
constructor(registry, win, tab, browserContext, opener) {
|
||||||
EventEmitter.decorate(this);
|
helper.decorateAsEventEmitter(this);
|
||||||
|
|
||||||
this._targetId = helper.generateId();
|
this._targetId = helper.generateId();
|
||||||
this._registry = registry;
|
this._registry = registry;
|
||||||
|
@ -360,12 +357,19 @@ class PageTarget {
|
||||||
this._initialDPPX = this._linkedBrowser.browsingContext.overrideDPPX;
|
this._initialDPPX = this._linkedBrowser.browsingContext.overrideDPPX;
|
||||||
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._actor = undefined;
|
||||||
|
this._actorSequenceNumber = 0;
|
||||||
|
this._channel = new SimpleChannel(`browser::page[${this._targetId}]`);
|
||||||
this._videoRecordingInfo = undefined;
|
this._videoRecordingInfo = undefined;
|
||||||
this._screencastRecordingInfo = undefined;
|
this._screencastRecordingInfo = undefined;
|
||||||
this._dialogs = new Map();
|
this._dialogs = new Map();
|
||||||
this.forcedColors = 'no-override';
|
this.forcedColors = 'no-override';
|
||||||
this._pageInitScripts = [];
|
this.mediumOverride = '';
|
||||||
|
this.crossProcessCookie = {
|
||||||
|
initScripts: [],
|
||||||
|
bindings: [],
|
||||||
|
interceptFileChooserDialog: false,
|
||||||
|
};
|
||||||
|
|
||||||
const navigationListener = {
|
const navigationListener = {
|
||||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
|
QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
|
||||||
|
@ -380,11 +384,38 @@ class PageTarget {
|
||||||
this._disposed = false;
|
this._disposed = false;
|
||||||
browserContext.pages.add(this);
|
browserContext.pages.add(this);
|
||||||
this._registry._browserToTarget.set(this._linkedBrowser, this);
|
this._registry._browserToTarget.set(this._linkedBrowser, this);
|
||||||
this._registry._browserBrowsingContextToTarget.set(this._linkedBrowser.browsingContext, this);
|
this._registry._browserIdToTarget.set(this._linkedBrowser.browsingContext.browserId, this);
|
||||||
|
|
||||||
this._registry.emit(TargetRegistry.Events.TargetCreated, this);
|
this._registry.emit(TargetRegistry.Events.TargetCreated, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nextActorSequenceNumber() {
|
||||||
|
return ++this._actorSequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
setActor(actor) {
|
||||||
|
this._actor = actor;
|
||||||
|
this._channel.bindToActor(actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeActor(actor) {
|
||||||
|
// Note: the order between setActor and removeActor is non-deterministic.
|
||||||
|
// Therefore we check that we are still bound to the actor that is being removed.
|
||||||
|
if (this._actor !== actor)
|
||||||
|
return;
|
||||||
|
this._actor = undefined;
|
||||||
|
this._channel.resetTransport();
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceTopBrowsingContext(browsingContext) {
|
||||||
|
if (this._actor && this._actor.browsingContext !== browsingContext) {
|
||||||
|
// Disconnect early to avoid receiving protocol messages from the old actor.
|
||||||
|
this.removeActor(this._actor);
|
||||||
|
}
|
||||||
|
this.emit(PageTarget.Events.TopBrowsingContextReplaced);
|
||||||
|
this.updateOverridesForBrowsingContext(browsingContext);
|
||||||
|
}
|
||||||
|
|
||||||
dialog(dialogId) {
|
dialog(dialogId) {
|
||||||
return this._dialogs.get(dialogId);
|
return this._dialogs.get(dialogId);
|
||||||
}
|
}
|
||||||
|
@ -405,20 +436,31 @@ class PageTarget {
|
||||||
return this._browserContext;
|
return this._browserContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTouchOverride() {
|
updateOverridesForBrowsingContext(browsingContext = undefined) {
|
||||||
this._linkedBrowser.browsingContext.touchEventsOverride = this._browserContext.touchOverride ? 'enabled' : 'none';
|
this.updateTouchOverride(browsingContext);
|
||||||
|
this.updateUserAgent(browsingContext);
|
||||||
|
this.updatePlatform(browsingContext);
|
||||||
|
this.updateDPPXOverride(browsingContext);
|
||||||
|
this.updateEmulatedMedia(browsingContext);
|
||||||
|
this.updateColorSchemeOverride(browsingContext);
|
||||||
|
this.updateReducedMotionOverride(browsingContext);
|
||||||
|
this.updateForcedColorsOverride(browsingContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUserAgent() {
|
updateTouchOverride(browsingContext = undefined) {
|
||||||
this._linkedBrowser.browsingContext.customUserAgent = this._browserContext.defaultUserAgent;
|
(browsingContext || this._linkedBrowser.browsingContext).touchEventsOverride = this._browserContext.touchOverride ? 'enabled' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePlatform() {
|
updateUserAgent(browsingContext = undefined) {
|
||||||
this._linkedBrowser.browsingContext.customPlatform = this._browserContext.defaultPlatform;
|
(browsingContext || this._linkedBrowser.browsingContext).customUserAgent = this._browserContext.defaultUserAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateJavaScriptDisabled() {
|
updatePlatform(browsingContext = undefined) {
|
||||||
this._linkedBrowser.browsingContext.allowJavascript = !this._browserContext.javaScriptDisabled;
|
(browsingContext || this._linkedBrowser.browsingContext).customPlatform = this._browserContext.defaultPlatform;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDPPXOverride(browsingContext = undefined) {
|
||||||
|
(browsingContext || this._linkedBrowser.browsingContext).overrideDPPX = this._browserContext.deviceScaleFactor || this._initialDPPX;
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateModalDialogs() {
|
_updateModalDialogs() {
|
||||||
|
@ -452,7 +494,7 @@ class PageTarget {
|
||||||
// default viewport.
|
// default viewport.
|
||||||
const viewportSize = this._viewportSize || this._browserContext.defaultViewportSize;
|
const viewportSize = this._viewportSize || this._browserContext.defaultViewportSize;
|
||||||
const actualSize = await setViewportSizeForBrowser(viewportSize, this._linkedBrowser, this._window);
|
const actualSize = await setViewportSizeForBrowser(viewportSize, this._linkedBrowser, this._window);
|
||||||
this._linkedBrowser.browsingContext.overrideDPPX = this._browserContext.deviceScaleFactor || this._initialDPPX;
|
this.updateDPPXOverride();
|
||||||
await this._channel.connect('').send('awaitViewportDimensions', {
|
await this._channel.connect('').send('awaitViewportDimensions', {
|
||||||
width: actualSize.width,
|
width: actualSize.width,
|
||||||
height: actualSize.height,
|
height: actualSize.height,
|
||||||
|
@ -461,7 +503,12 @@ class PageTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
setEmulatedMedia(mediumOverride) {
|
setEmulatedMedia(mediumOverride) {
|
||||||
this._linkedBrowser.browsingContext.mediumOverride = mediumOverride || '';
|
this.mediumOverride = mediumOverride || '';
|
||||||
|
this.updateEmulatedMedia();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEmulatedMedia(browsingContext = undefined) {
|
||||||
|
(browsingContext || this._linkedBrowser.browsingContext).mediumOverride = this.mediumOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
setColorScheme(colorScheme) {
|
setColorScheme(colorScheme) {
|
||||||
|
@ -469,8 +516,8 @@ class PageTarget {
|
||||||
this.updateColorSchemeOverride();
|
this.updateColorSchemeOverride();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateColorSchemeOverride() {
|
updateColorSchemeOverride(browsingContext = undefined) {
|
||||||
this._linkedBrowser.browsingContext.prefersColorSchemeOverride = this.colorScheme || this._browserContext.colorScheme || 'none';
|
(browsingContext || this._linkedBrowser.browsingContext).prefersColorSchemeOverride = this.colorScheme || this._browserContext.colorScheme || 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
setReducedMotion(reducedMotion) {
|
setReducedMotion(reducedMotion) {
|
||||||
|
@ -478,8 +525,8 @@ class PageTarget {
|
||||||
this.updateReducedMotionOverride();
|
this.updateReducedMotionOverride();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateReducedMotionOverride() {
|
updateReducedMotionOverride(browsingContext = undefined) {
|
||||||
this._linkedBrowser.browsingContext.prefersReducedMotionOverride = this.reducedMotion || this._browserContext.reducedMotion || 'none';
|
(browsingContext || this._linkedBrowser.browsingContext).prefersReducedMotionOverride = this.reducedMotion || this._browserContext.reducedMotion || 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
setForcedColors(forcedColors) {
|
setForcedColors(forcedColors) {
|
||||||
|
@ -487,8 +534,14 @@ class PageTarget {
|
||||||
this.updateForcedColorsOverride();
|
this.updateForcedColorsOverride();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateForcedColorsOverride() {
|
updateForcedColorsOverride(browsingContext = undefined) {
|
||||||
this._linkedBrowser.browsingContext.forcedColorsOverride = (this.forcedColors !== 'no-override' ? this.forcedColors : this._browserContext.forcedColors) || 'no-override';
|
(browsingContext || this._linkedBrowser.browsingContext).forcedColorsOverride = (this.forcedColors !== 'no-override' ? this.forcedColors : this._browserContext.forcedColors) || 'no-override';
|
||||||
|
}
|
||||||
|
|
||||||
|
async setInterceptFileChooserDialog(enabled) {
|
||||||
|
this.crossProcessCookie.interceptFileChooserDialog = enabled;
|
||||||
|
this._updateCrossProcessCookie();
|
||||||
|
await this._channel.connect('').send('setInterceptFileChooserDialog', enabled).catch(e => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setViewportSize(viewportSize) {
|
async setViewportSize(viewportSize) {
|
||||||
|
@ -524,20 +577,28 @@ class PageTarget {
|
||||||
this._browserContext.grantPermissionsToOrigin(this._url);
|
this._browserContext.grantPermissionsToOrigin(this._url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updateCrossProcessCookie() {
|
||||||
|
Services.ppmm.sharedData.set('juggler:page-cookie-' + this._linkedBrowser.browsingContext.browserId, this.crossProcessCookie);
|
||||||
|
Services.ppmm.sharedData.flush();
|
||||||
|
}
|
||||||
|
|
||||||
async ensurePermissions() {
|
async ensurePermissions() {
|
||||||
await this._channel.connect('').send('ensurePermissions', {}).catch(e => void e);
|
await this._channel.connect('').send('ensurePermissions', {}).catch(e => void e);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setInitScripts(scripts) {
|
async setInitScripts(scripts) {
|
||||||
this._pageInitScripts = scripts;
|
this.crossProcessCookie.initScripts = scripts;
|
||||||
|
this._updateCrossProcessCookie();
|
||||||
await this.pushInitScripts();
|
await this.pushInitScripts();
|
||||||
}
|
}
|
||||||
|
|
||||||
async pushInitScripts() {
|
async pushInitScripts() {
|
||||||
await this._channel.connect('').send('setInitScripts', [...this._browserContext.initScripts, ...this._pageInitScripts]).catch(e => void e);
|
await this._channel.connect('').send('setInitScripts', [...this._browserContext.crossProcessCookie.initScripts, ...this.crossProcessCookie.initScripts]).catch(e => void e);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addBinding(worldName, name, script) {
|
async addBinding(worldName, name, script) {
|
||||||
|
this.crossProcessCookie.bindings.push({ worldName, name, script });
|
||||||
|
this._updateCrossProcessCookie();
|
||||||
await this._channel.connect('').send('addBinding', { worldName, name, script }).catch(e => void e);
|
await this._channel.connect('').send('addBinding', { worldName, name, script }).catch(e => void e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,7 +702,7 @@ class PageTarget {
|
||||||
this.stopScreencast();
|
this.stopScreencast();
|
||||||
this._browserContext.pages.delete(this);
|
this._browserContext.pages.delete(this);
|
||||||
this._registry._browserToTarget.delete(this._linkedBrowser);
|
this._registry._browserToTarget.delete(this._linkedBrowser);
|
||||||
this._registry._browserBrowsingContextToTarget.delete(this._linkedBrowser.browsingContext);
|
this._registry._browserIdToTarget.delete(this._linkedBrowser.browsingContext.browserId);
|
||||||
try {
|
try {
|
||||||
helper.removeListeners(this._eventListeners);
|
helper.removeListeners(this._eventListeners);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -660,6 +721,7 @@ PageTarget.Events = {
|
||||||
Crashed: Symbol('PageTarget.Crashed'),
|
Crashed: Symbol('PageTarget.Crashed'),
|
||||||
DialogOpened: Symbol('PageTarget.DialogOpened'),
|
DialogOpened: Symbol('PageTarget.DialogOpened'),
|
||||||
DialogClosed: Symbol('PageTarget.DialogClosed'),
|
DialogClosed: Symbol('PageTarget.DialogClosed'),
|
||||||
|
TopBrowsingContextReplaced: Symbol('PageTarget.TopBrowsingContextReplaced'),
|
||||||
};
|
};
|
||||||
|
|
||||||
function fromProtocolColorScheme(colorScheme) {
|
function fromProtocolColorScheme(colorScheme) {
|
||||||
|
@ -712,18 +774,24 @@ class BrowserContext {
|
||||||
this.deviceScaleFactor = undefined;
|
this.deviceScaleFactor = undefined;
|
||||||
this.defaultUserAgent = null;
|
this.defaultUserAgent = null;
|
||||||
this.defaultPlatform = null;
|
this.defaultPlatform = null;
|
||||||
this.javaScriptDisabled = false;
|
|
||||||
this.touchOverride = false;
|
this.touchOverride = false;
|
||||||
this.colorScheme = 'none';
|
this.colorScheme = 'none';
|
||||||
this.forcedColors = 'no-override';
|
this.forcedColors = 'no-override';
|
||||||
this.reducedMotion = 'none';
|
this.reducedMotion = 'none';
|
||||||
this.videoRecordingOptions = undefined;
|
this.videoRecordingOptions = undefined;
|
||||||
this.initScripts = [];
|
this.crossProcessCookie = {
|
||||||
this.bindings = [];
|
initScripts: [],
|
||||||
this.settings = {};
|
bindings: [],
|
||||||
|
settings: {},
|
||||||
|
};
|
||||||
this.pages = new Set();
|
this.pages = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updateCrossProcessCookie() {
|
||||||
|
Services.ppmm.sharedData.set('juggler:context-cookie-' + this.userContextId, this.crossProcessCookie);
|
||||||
|
Services.ppmm.sharedData.flush();
|
||||||
|
}
|
||||||
|
|
||||||
setColorScheme(colorScheme) {
|
setColorScheme(colorScheme) {
|
||||||
this.colorScheme = fromProtocolColorScheme(colorScheme);
|
this.colorScheme = fromProtocolColorScheme(colorScheme);
|
||||||
for (const page of this.pages)
|
for (const page of this.pages)
|
||||||
|
@ -796,12 +864,6 @@ class BrowserContext {
|
||||||
page.updatePlatform();
|
page.updatePlatform();
|
||||||
}
|
}
|
||||||
|
|
||||||
setJavaScriptDisabled(javaScriptDisabled) {
|
|
||||||
this.javaScriptDisabled = javaScriptDisabled;
|
|
||||||
for (const page of this.pages)
|
|
||||||
page.updateJavaScriptDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
setTouchOverride(touchOverride) {
|
setTouchOverride(touchOverride) {
|
||||||
this.touchOverride = touchOverride;
|
this.touchOverride = touchOverride;
|
||||||
for (const page of this.pages)
|
for (const page of this.pages)
|
||||||
|
@ -815,17 +877,20 @@ class BrowserContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
async setInitScripts(scripts) {
|
async setInitScripts(scripts) {
|
||||||
this.initScripts = scripts;
|
this.crossProcessCookie.initScripts = scripts;
|
||||||
|
this._updateCrossProcessCookie();
|
||||||
await Promise.all(Array.from(this.pages).map(page => page.pushInitScripts()));
|
await Promise.all(Array.from(this.pages).map(page => page.pushInitScripts()));
|
||||||
}
|
}
|
||||||
|
|
||||||
async addBinding(worldName, name, script) {
|
async addBinding(worldName, name, script) {
|
||||||
this.bindings.push({ worldName, name, script });
|
this.crossProcessCookie.bindings.push({ worldName, name, script });
|
||||||
|
this._updateCrossProcessCookie();
|
||||||
await Promise.all(Array.from(this.pages).map(page => page.addBinding(worldName, name, script)));
|
await Promise.all(Array.from(this.pages).map(page => page.addBinding(worldName, name, script)));
|
||||||
}
|
}
|
||||||
|
|
||||||
async applySetting(name, value) {
|
async applySetting(name, value) {
|
||||||
this.settings[name] = value;
|
this.crossProcessCookie.settings[name] = value;
|
||||||
|
this._updateCrossProcessCookie();
|
||||||
await Promise.all(Array.from(this.pages).map(page => page.applyContextSetting(name, value)));
|
await Promise.all(Array.from(this.pages).map(page => page.applyContextSetting(name, value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,34 @@ const {BrowserHandler} = ChromeUtils.import("chrome://juggler/content/protocol/B
|
||||||
const {NetworkObserver} = ChromeUtils.import("chrome://juggler/content/NetworkObserver.js");
|
const {NetworkObserver} = ChromeUtils.import("chrome://juggler/content/NetworkObserver.js");
|
||||||
const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
|
const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
|
||||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||||
|
const {ActorManagerParent} = ChromeUtils.import('resource://gre/modules/ActorManagerParent.jsm');
|
||||||
const helper = new Helper();
|
const helper = new Helper();
|
||||||
|
|
||||||
const Cc = Components.classes;
|
const Cc = Components.classes;
|
||||||
const Ci = Components.interfaces;
|
const Ci = Components.interfaces;
|
||||||
|
|
||||||
const FRAME_SCRIPT = "chrome://juggler/content/content/main.js";
|
// Register JSWindowActors that will be instantiated for each frame.
|
||||||
|
ActorManagerParent.addJSWindowActors({
|
||||||
|
JugglerFrame: {
|
||||||
|
parent: {
|
||||||
|
moduleURI: 'chrome://juggler/content/JugglerFrameParent.jsm',
|
||||||
|
},
|
||||||
|
child: {
|
||||||
|
moduleURI: 'chrome://juggler/content/content/JugglerFrameChild.jsm',
|
||||||
|
events: {
|
||||||
|
// Normally, we instantiate an actor when a new window is created.
|
||||||
|
DOMWindowCreated: {},
|
||||||
|
// However, for same-origin iframes, the navigation from about:blank
|
||||||
|
// to the URL will share the same window, so we need to also create
|
||||||
|
// an actor for a new document via DOMDocElementInserted.
|
||||||
|
DOMDocElementInserted: {},
|
||||||
|
// Also, listening to DOMContentLoaded.
|
||||||
|
DOMContentLoaded: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
allFrames: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
let browserStartupFinishedCallback;
|
let browserStartupFinishedCallback;
|
||||||
let browserStartupFinishedPromise = new Promise(x => browserStartupFinishedCallback = x);
|
let browserStartupFinishedPromise = new Promise(x => browserStartupFinishedCallback = x);
|
||||||
|
@ -72,8 +94,7 @@ class Juggler {
|
||||||
const targetRegistry = new TargetRegistry();
|
const targetRegistry = new TargetRegistry();
|
||||||
new NetworkObserver(targetRegistry);
|
new NetworkObserver(targetRegistry);
|
||||||
|
|
||||||
const loadFrameScript = () => {
|
const loadStyleSheet = () => {
|
||||||
Services.mm.loadFrameScript(FRAME_SCRIPT, true /* aAllowDelayedLoad */);
|
|
||||||
if (Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).isHeadless) {
|
if (Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).isHeadless) {
|
||||||
const styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Components.interfaces.nsIStyleSheetService);
|
const styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Components.interfaces.nsIStyleSheetService);
|
||||||
const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
|
const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
|
||||||
|
@ -118,7 +139,7 @@ class Juggler {
|
||||||
pipeStopped = true;
|
pipeStopped = true;
|
||||||
}, () => browserStartupFinishedPromise);
|
}, () => browserStartupFinishedPromise);
|
||||||
dispatcher.rootSession().setHandler(browserHandler);
|
dispatcher.rootSession().setHandler(browserHandler);
|
||||||
loadFrameScript();
|
loadStyleSheet();
|
||||||
dump(`\nJuggler listening to the pipe\n`);
|
dump(`\nJuggler listening to the pipe\n`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,14 +9,13 @@ const Cu = Components.utils;
|
||||||
|
|
||||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||||
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
||||||
const {EventEmitter} = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
|
|
||||||
const {Runtime} = ChromeUtils.import('chrome://juggler/content/content/Runtime.js');
|
const {Runtime} = ChromeUtils.import('chrome://juggler/content/content/Runtime.js');
|
||||||
|
|
||||||
const helper = new Helper();
|
const helper = new Helper();
|
||||||
|
|
||||||
class FrameTree {
|
class FrameTree {
|
||||||
constructor(rootDocShell) {
|
constructor(rootDocShell) {
|
||||||
EventEmitter.decorate(this);
|
helper.decorateAsEventEmitter(this);
|
||||||
|
|
||||||
this._browsingContextGroup = rootDocShell.browsingContext.group;
|
this._browsingContextGroup = rootDocShell.browsingContext.group;
|
||||||
if (!this._browsingContextGroup.__jugglerFrameTrees)
|
if (!this._browsingContextGroup.__jugglerFrameTrees)
|
||||||
|
@ -33,6 +32,7 @@ class FrameTree {
|
||||||
this._docShellToFrame = new Map();
|
this._docShellToFrame = new Map();
|
||||||
this._frameIdToFrame = new Map();
|
this._frameIdToFrame = new Map();
|
||||||
this._pageReady = false;
|
this._pageReady = false;
|
||||||
|
this._javaScriptDisabled = false;
|
||||||
this._mainFrame = this._createFrame(rootDocShell);
|
this._mainFrame = this._createFrame(rootDocShell);
|
||||||
const webProgress = rootDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
const webProgress = rootDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
.getInterface(Ci.nsIWebProgress);
|
.getInterface(Ci.nsIWebProgress);
|
||||||
|
@ -128,6 +128,12 @@ class FrameTree {
|
||||||
this.scrollbarsHidden = hidden;
|
this.scrollbarsHidden = hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setJavaScriptDisabled(javaScriptDisabled) {
|
||||||
|
this._javaScriptDisabled = javaScriptDisabled;
|
||||||
|
for (const frame of this.frames())
|
||||||
|
frame._updateJavaScriptDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
_onWorkerCreated(workerDebugger) {
|
_onWorkerCreated(workerDebugger) {
|
||||||
// Note: we do not interoperate with firefox devtools.
|
// Note: we do not interoperate with firefox devtools.
|
||||||
if (workerDebugger.isInitialized)
|
if (workerDebugger.isInitialized)
|
||||||
|
@ -214,7 +220,7 @@ class FrameTree {
|
||||||
const docShell = progress.DOMWindow.docShell;
|
const docShell = progress.DOMWindow.docShell;
|
||||||
const frame = this._docShellToFrame.get(docShell);
|
const frame = this._docShellToFrame.get(docShell);
|
||||||
if (!frame) {
|
if (!frame) {
|
||||||
dump(`ERROR: got a state changed event for un-tracked docshell!\n`);
|
dump(`WARNING: got a state changed event for un-tracked docshell!\n`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +233,6 @@ class FrameTree {
|
||||||
const isStart = flag & Ci.nsIWebProgressListener.STATE_START;
|
const isStart = flag & Ci.nsIWebProgressListener.STATE_START;
|
||||||
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;
|
||||||
const isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
|
|
||||||
|
|
||||||
if (isStart) {
|
if (isStart) {
|
||||||
// Starting a new navigation.
|
// Starting a new navigation.
|
||||||
|
@ -257,9 +262,6 @@ class FrameTree {
|
||||||
if (frame === this._mainFrame && status !== Cr.NS_BINDING_ABORTED)
|
if (frame === this._mainFrame && status !== Cr.NS_BINDING_ABORTED)
|
||||||
this.forcePageReady();
|
this.forcePageReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isStop && isDocument)
|
|
||||||
this.emit(FrameTree.Events.Load, frame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onLocationChange(progress, request, location, flags) {
|
onLocationChange(progress, request, location, flags) {
|
||||||
|
@ -287,6 +289,10 @@ class FrameTree {
|
||||||
|
|
||||||
_createFrame(docShell) {
|
_createFrame(docShell) {
|
||||||
const parentFrame = this._docShellToFrame.get(docShell.parent) || null;
|
const parentFrame = this._docShellToFrame.get(docShell.parent) || null;
|
||||||
|
if (!parentFrame && this._mainFrame) {
|
||||||
|
dump(`WARNING: found docShell with the same root, but no parent!\n`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const frame = new Frame(this, this._runtime, docShell, parentFrame);
|
const frame = new Frame(this, this._runtime, docShell, parentFrame);
|
||||||
this._docShellToFrame.set(docShell, frame);
|
this._docShellToFrame.set(docShell, frame);
|
||||||
this._frameIdToFrame.set(frame.id(), frame);
|
this._frameIdToFrame.set(frame.id(), frame);
|
||||||
|
@ -308,6 +314,11 @@ class FrameTree {
|
||||||
// Detach all children first
|
// Detach all children first
|
||||||
for (const subframe of frame._children)
|
for (const subframe of frame._children)
|
||||||
this._detachFrame(subframe);
|
this._detachFrame(subframe);
|
||||||
|
if (frame === this._mainFrame) {
|
||||||
|
// Do not detach main frame (happens during cross-process navigation),
|
||||||
|
// as it confuses the client.
|
||||||
|
return;
|
||||||
|
}
|
||||||
this._docShellToFrame.delete(frame._docShell);
|
this._docShellToFrame.delete(frame._docShell);
|
||||||
this._frameIdToFrame.delete(frame.id());
|
this._frameIdToFrame.delete(frame.id());
|
||||||
if (frame._parentFrame)
|
if (frame._parentFrame)
|
||||||
|
@ -333,7 +344,6 @@ FrameTree.Events = {
|
||||||
NavigationAborted: 'navigationaborted',
|
NavigationAborted: 'navigationaborted',
|
||||||
SameDocumentNavigation: 'samedocumentnavigation',
|
SameDocumentNavigation: 'samedocumentnavigation',
|
||||||
PageReady: 'pageready',
|
PageReady: 'pageready',
|
||||||
Load: 'load',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class IsolatedWorld {
|
class IsolatedWorld {
|
||||||
|
@ -518,6 +528,20 @@ class Frame {
|
||||||
for (const script of world._scriptsToEvaluateOnNewDocument)
|
for (const script of world._scriptsToEvaluateOnNewDocument)
|
||||||
executionContext.evaluateScriptSafely(script);
|
executionContext.evaluateScriptSafely(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const url = this.domWindow().location?.href;
|
||||||
|
if (url === 'about:blank' && !this._url) {
|
||||||
|
// Sometimes FrameTree is created too early, before the location has been set.
|
||||||
|
this._url = url;
|
||||||
|
this._frameTree.emit(FrameTree.Events.NavigationCommitted, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateJavaScriptDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateJavaScriptDisabled() {
|
||||||
|
if (this._docShell.browsingContext.currentWindowContext)
|
||||||
|
this._docShell.browsingContext.currentWindowContext.allowJavascript = !this._frameTree._javaScriptDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
mainExecutionContext() {
|
mainExecutionContext() {
|
||||||
|
@ -592,7 +616,7 @@ class Worker {
|
||||||
onMessage: msg => void this._channel._onMessage(JSON.parse(msg)),
|
onMessage: msg => void this._channel._onMessage(JSON.parse(msg)),
|
||||||
onClose: () => void this._channel.dispose(),
|
onClose: () => void this._channel.dispose(),
|
||||||
onError: (filename, lineno, message) => {
|
onError: (filename, lineno, message) => {
|
||||||
dump(`Error in worker: ${message} @${filename}:${lineno}\n`);
|
dump(`WARNING: Error in worker: ${message} @${filename}:${lineno}\n`);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
workerDebugger.addListener(this._workerDebuggerListener);
|
workerDebugger.addListener(this._workerDebuggerListener);
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { Helper } = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||||
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
const { initialize } = ChromeUtils.import('chrome://juggler/content/content/main.js');
|
||||||
|
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
const helper = new Helper();
|
||||||
|
|
||||||
|
let sameProcessInstanceNumber = 0;
|
||||||
|
|
||||||
|
class JugglerFrameChild extends JSWindowActorChild {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._eventListeners = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(aEvent) {
|
||||||
|
if (this._agents && aEvent.target === this.document)
|
||||||
|
this._agents.pageAgent.onWindowEvent(aEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
actorCreated() {
|
||||||
|
this.actorName = `content::${this.browsingContext.browserId}/${this.browsingContext.id}/${++sameProcessInstanceNumber}`;
|
||||||
|
|
||||||
|
this._eventListeners.push(helper.addEventListener(this.contentWindow, 'load', event => {
|
||||||
|
this._agents?.pageAgent.onWindowEvent(event);
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (this.document.documentURI.startsWith('moz-extension://'))
|
||||||
|
return;
|
||||||
|
this._agents = initialize(this.browsingContext, this.docShell, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_dispose() {
|
||||||
|
helper.removeListeners(this._eventListeners);
|
||||||
|
// We do not cleanup since agents are shared for all frames in the process.
|
||||||
|
|
||||||
|
// TODO: restore the cleanup.
|
||||||
|
// Reset transport so that all messages will be pending and will not throw any errors.
|
||||||
|
// this._channel.resetTransport();
|
||||||
|
// this._agents.pageAgent.dispose();
|
||||||
|
// this._agents.frameTree.dispose();
|
||||||
|
// this._agents = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
didDestroy() {
|
||||||
|
this._dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
receiveMessage() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
var EXPORTED_SYMBOLS = ['JugglerFrameChild'];
|
|
@ -50,8 +50,7 @@ class WorkerData {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PageAgent {
|
class PageAgent {
|
||||||
constructor(messageManager, browserChannel, frameTree) {
|
constructor(browserChannel, frameTree) {
|
||||||
this._messageManager = messageManager;
|
|
||||||
this._browserChannel = browserChannel;
|
this._browserChannel = browserChannel;
|
||||||
this._browserPage = browserChannel.connect('page');
|
this._browserPage = browserChannel.connect('page');
|
||||||
this._frameTree = frameTree;
|
this._frameTree = frameTree;
|
||||||
|
@ -78,6 +77,7 @@ class PageAgent {
|
||||||
this._onWorkerCreated(worker);
|
this._onWorkerCreated(worker);
|
||||||
|
|
||||||
// Report execution contexts.
|
// Report execution contexts.
|
||||||
|
this._browserPage.emit('runtimeExecutionContextsCleared', {});
|
||||||
for (const context of this._runtime.executionContexts())
|
for (const context of this._runtime.executionContexts())
|
||||||
this._onExecutionContextCreated(context);
|
this._onExecutionContextCreated(context);
|
||||||
|
|
||||||
|
@ -99,9 +99,7 @@ class PageAgent {
|
||||||
helper.addObserver(this._linkClicked.bind(this, true), 'juggler-link-click-sync'),
|
helper.addObserver(this._linkClicked.bind(this, true), 'juggler-link-click-sync'),
|
||||||
helper.addObserver(this._onWindowOpenInNewContext.bind(this), 'juggler-window-open-in-new-context'),
|
helper.addObserver(this._onWindowOpenInNewContext.bind(this), 'juggler-window-open-in-new-context'),
|
||||||
helper.addObserver(this._filePickerShown.bind(this), 'juggler-file-picker-shown'),
|
helper.addObserver(this._filePickerShown.bind(this), 'juggler-file-picker-shown'),
|
||||||
helper.addEventListener(this._messageManager, 'DOMContentLoaded', this._onDOMContentLoaded.bind(this)),
|
|
||||||
helper.addObserver(this._onDocumentOpenLoad.bind(this), 'juggler-document-open-loaded'),
|
helper.addObserver(this._onDocumentOpenLoad.bind(this), 'juggler-document-open-loaded'),
|
||||||
helper.on(this._frameTree, 'load', this._onLoad.bind(this)),
|
|
||||||
helper.on(this._frameTree, 'frameattached', this._onFrameAttached.bind(this)),
|
helper.on(this._frameTree, 'frameattached', this._onFrameAttached.bind(this)),
|
||||||
helper.on(this._frameTree, 'framedetached', this._onFrameDetached.bind(this)),
|
helper.on(this._frameTree, 'framedetached', this._onFrameDetached.bind(this)),
|
||||||
helper.on(this._frameTree, 'navigationstarted', this._onNavigationStarted.bind(this)),
|
helper.on(this._frameTree, 'navigationstarted', this._onNavigationStarted.bind(this)),
|
||||||
|
@ -133,7 +131,6 @@ class PageAgent {
|
||||||
this._runtime.events.onExecutionContextDestroyed(this._onExecutionContextDestroyed.bind(this)),
|
this._runtime.events.onExecutionContextDestroyed(this._onExecutionContextDestroyed.bind(this)),
|
||||||
this._runtime.events.onBindingCalled(this._onBindingCalled.bind(this)),
|
this._runtime.events.onBindingCalled(this._onBindingCalled.bind(this)),
|
||||||
browserChannel.register('page', {
|
browserChannel.register('page', {
|
||||||
addBinding: ({ worldName, name, script }) => this._frameTree.addBinding(worldName, name, script),
|
|
||||||
adoptNode: this._adoptNode.bind(this),
|
adoptNode: this._adoptNode.bind(this),
|
||||||
crash: this._crash.bind(this),
|
crash: this._crash.bind(this),
|
||||||
describeNode: this._describeNode.bind(this),
|
describeNode: this._describeNode.bind(this),
|
||||||
|
@ -143,15 +140,11 @@ class PageAgent {
|
||||||
dispatchTapEvent: this._dispatchTapEvent.bind(this),
|
dispatchTapEvent: this._dispatchTapEvent.bind(this),
|
||||||
getContentQuads: this._getContentQuads.bind(this),
|
getContentQuads: this._getContentQuads.bind(this),
|
||||||
getFullAXTree: this._getFullAXTree.bind(this),
|
getFullAXTree: this._getFullAXTree.bind(this),
|
||||||
goBack: this._goBack.bind(this),
|
|
||||||
goForward: this._goForward.bind(this),
|
|
||||||
insertText: this._insertText.bind(this),
|
insertText: this._insertText.bind(this),
|
||||||
navigate: this._navigate.bind(this),
|
navigate: this._navigate.bind(this),
|
||||||
reload: this._reload.bind(this),
|
|
||||||
scrollIntoViewIfNeeded: this._scrollIntoViewIfNeeded.bind(this),
|
scrollIntoViewIfNeeded: this._scrollIntoViewIfNeeded.bind(this),
|
||||||
setCacheDisabled: this._setCacheDisabled.bind(this),
|
setCacheDisabled: this._setCacheDisabled.bind(this),
|
||||||
setFileInputFiles: this._setFileInputFiles.bind(this),
|
setFileInputFiles: this._setFileInputFiles.bind(this),
|
||||||
setInterceptFileChooserDialog: this._setInterceptFileChooserDialog.bind(this),
|
|
||||||
evaluate: this._runtime.evaluate.bind(this._runtime),
|
evaluate: this._runtime.evaluate.bind(this._runtime),
|
||||||
callFunction: this._runtime.callFunction.bind(this._runtime),
|
callFunction: this._runtime.callFunction.bind(this._runtime),
|
||||||
getObjectProperties: this._runtime.getObjectProperties.bind(this._runtime),
|
getObjectProperties: this._runtime.getObjectProperties.bind(this._runtime),
|
||||||
|
@ -224,10 +217,6 @@ class PageAgent {
|
||||||
this._emitAllEvents(this._frameTree.mainFrame());
|
this._emitAllEvents(this._frameTree.mainFrame());
|
||||||
}
|
}
|
||||||
|
|
||||||
_setInterceptFileChooserDialog({enabled}) {
|
|
||||||
this._docShell.fileInputInterceptionEnabled = !!enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
_linkClicked(sync, anchorElement) {
|
_linkClicked(sync, anchorElement) {
|
||||||
if (anchorElement.ownerGlobal.docShell !== this._docShell)
|
if (anchorElement.ownerGlobal.docShell !== this._docShell)
|
||||||
return;
|
return;
|
||||||
|
@ -259,7 +248,9 @@ class PageAgent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDOMContentLoaded(event) {
|
onWindowEvent(event) {
|
||||||
|
if (event.type !== 'DOMContentLoaded' && event.type !== 'load')
|
||||||
|
return;
|
||||||
if (!event.target.ownerGlobal)
|
if (!event.target.ownerGlobal)
|
||||||
return;
|
return;
|
||||||
const docShell = event.target.ownerGlobal.docShell;
|
const docShell = event.target.ownerGlobal.docShell;
|
||||||
|
@ -268,7 +259,7 @@ class PageAgent {
|
||||||
return;
|
return;
|
||||||
this._browserPage.emit('pageEventFired', {
|
this._browserPage.emit('pageEventFired', {
|
||||||
frameId: frame.id(),
|
frameId: frame.id(),
|
||||||
name: 'DOMContentLoaded',
|
name: event.type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,13 +282,6 @@ class PageAgent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_onLoad(frame) {
|
|
||||||
this._browserPage.emit('pageEventFired', {
|
|
||||||
frameId: frame.id(),
|
|
||||||
name: 'load'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_onNavigationStarted(frame) {
|
_onNavigationStarted(frame) {
|
||||||
this._browserPage.emit('pageNavigationStarted', {
|
this._browserPage.emit('pageNavigationStarted', {
|
||||||
frameId: frame.id(),
|
frameId: frame.id(),
|
||||||
|
@ -395,35 +379,16 @@ class PageAgent {
|
||||||
return {navigationId: frame.pendingNavigationId(), navigationURL: frame.pendingNavigationURL()};
|
return {navigationId: frame.pendingNavigationId(), navigationURL: frame.pendingNavigationURL()};
|
||||||
}
|
}
|
||||||
|
|
||||||
async _reload({frameId, url}) {
|
|
||||||
const frame = this._frameTree.frame(frameId);
|
|
||||||
const docShell = frame.docShell().QueryInterface(Ci.nsIWebNavigation);
|
|
||||||
docShell.reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _goBack({frameId, url}) {
|
|
||||||
const frame = this._frameTree.frame(frameId);
|
|
||||||
const docShell = frame.docShell();
|
|
||||||
if (!docShell.canGoBack)
|
|
||||||
return {success: false};
|
|
||||||
docShell.goBack();
|
|
||||||
return {success: true};
|
|
||||||
}
|
|
||||||
|
|
||||||
async _goForward({frameId, url}) {
|
|
||||||
const frame = this._frameTree.frame(frameId);
|
|
||||||
const docShell = frame.docShell();
|
|
||||||
if (!docShell.canGoForward)
|
|
||||||
return {success: false};
|
|
||||||
docShell.goForward();
|
|
||||||
return {success: true};
|
|
||||||
}
|
|
||||||
|
|
||||||
async _adoptNode({frameId, objectId, executionContextId}) {
|
async _adoptNode({frameId, objectId, executionContextId}) {
|
||||||
const frame = this._frameTree.frame(frameId);
|
const frame = this._frameTree.frame(frameId);
|
||||||
if (!frame)
|
if (!frame)
|
||||||
throw new Error('Failed to find frame with id = ' + frameId);
|
throw new Error('Failed to find frame with id = ' + frameId);
|
||||||
const unsafeObject = frame.unsafeObject(objectId);
|
let unsafeObject;
|
||||||
|
if (!objectId) {
|
||||||
|
unsafeObject = frame.domWindow().frameElement;
|
||||||
|
} else {
|
||||||
|
unsafeObject = frame.unsafeObject(objectId);
|
||||||
|
}
|
||||||
const context = this._runtime.findExecutionContext(executionContextId);
|
const context = this._runtime.findExecutionContext(executionContextId);
|
||||||
const fromPrincipal = unsafeObject.nodePrincipal;
|
const fromPrincipal = unsafeObject.nodePrincipal;
|
||||||
const toFrame = this._frameTree.frame(context.auxData().frameId);
|
const toFrame = this._frameTree.frame(context.auxData().frameId);
|
||||||
|
@ -655,30 +620,23 @@ class PageAgent {
|
||||||
}
|
}
|
||||||
|
|
||||||
_simulateDragEvent(type, x, y, modifiers) {
|
_simulateDragEvent(type, x, y, modifiers) {
|
||||||
|
if (type !== 'drop' || dragService.dragAction) {
|
||||||
const window = this._frameTree.mainFrame().domWindow();
|
const window = this._frameTree.mainFrame().domWindow();
|
||||||
const element = window.windowUtils.elementFromPoint(x, y, false, false);
|
window.windowUtils.sendMouseEvent(
|
||||||
const event = window.document.createEvent('DragEvent');
|
|
||||||
|
|
||||||
event.initDragEvent(
|
|
||||||
type,
|
type,
|
||||||
true /* bubble */,
|
|
||||||
true /* cancelable */,
|
|
||||||
window,
|
|
||||||
0 /* clickCount */,
|
|
||||||
window.mozInnerScreenX + x,
|
|
||||||
window.mozInnerScreenY + y,
|
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
modifiers & 2 /* ctrlkey */,
|
0, /*button*/
|
||||||
modifiers & 1 /* altKey */,
|
0, /*clickCount*/
|
||||||
modifiers & 4 /* shiftKey */,
|
modifiers,
|
||||||
modifiers & 8 /* metaKey */,
|
false /*aIgnoreRootScrollFrame*/,
|
||||||
0 /* button */, // firefox always has the button as 0 on drops, regardless of which was pressed
|
undefined /*pressure*/,
|
||||||
null /* relatedTarget */,
|
undefined /*inputSource*/,
|
||||||
null,
|
undefined /*isDOMEventSynthesized*/,
|
||||||
|
undefined /*isWidgetEventSynthesized*/,
|
||||||
|
0, /*buttons*/
|
||||||
);
|
);
|
||||||
if (type !== 'drop' || dragService.dragAction)
|
}
|
||||||
window.windowUtils.dispatchDOMEventViaPresShellForTesting(element, event);
|
|
||||||
if (type === 'drop')
|
if (type === 'drop')
|
||||||
this._cancelDragIfNeeded();
|
this._cancelDragIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,7 +367,7 @@ class ExecutionContext {
|
||||||
try {
|
try {
|
||||||
this._debuggee.executeInGlobal(script);
|
this._debuggee.executeInGlobal(script);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dump(`ERROR: ${e.message}\n${e.stack}\n`);
|
dump(`WARNING: ${e.message}\n${e.stack}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,7 +450,7 @@ class ExecutionContext {
|
||||||
subtype = 'array';
|
subtype = 'array';
|
||||||
else if (Object.is(rawObj, null))
|
else if (Object.is(rawObj, null))
|
||||||
subtype = 'null';
|
subtype = 'null';
|
||||||
else if (this._instanceOf(debuggerObj, rawObj, 'Node'))
|
else if (typeof Node !== 'undefined' && Node.isInstance(rawObj))
|
||||||
subtype = 'node';
|
subtype = 'node';
|
||||||
else if (this._instanceOf(debuggerObj, rawObj, 'RegExp'))
|
else if (this._instanceOf(debuggerObj, rawObj, 'RegExp'))
|
||||||
subtype = 'regexp';
|
subtype = 'regexp';
|
||||||
|
|
|
@ -8,15 +8,26 @@ const {FrameTree} = ChromeUtils.import('chrome://juggler/content/content/FrameTr
|
||||||
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
||||||
const {PageAgent} = ChromeUtils.import('chrome://juggler/content/content/PageAgent.js');
|
const {PageAgent} = ChromeUtils.import('chrome://juggler/content/content/PageAgent.js');
|
||||||
|
|
||||||
let frameTree;
|
const browsingContextToAgents = new Map();
|
||||||
const helper = new Helper();
|
const helper = new Helper();
|
||||||
const messageManager = this;
|
|
||||||
|
|
||||||
let pageAgent;
|
function initialize(browsingContext, docShell, actor) {
|
||||||
|
if (browsingContext.parent) {
|
||||||
|
// For child frames, return agents from the main frame.
|
||||||
|
return browsingContextToAgents.get(browsingContext.top);
|
||||||
|
}
|
||||||
|
|
||||||
let failedToOverrideTimezone = false;
|
let data = browsingContextToAgents.get(browsingContext);
|
||||||
|
if (data) {
|
||||||
|
// Rebind from one main frame actor to another one.
|
||||||
|
data.channel.bindToActor(actor);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
const applySetting = {
|
data = { channel: undefined, pageAgent: undefined, frameTree: undefined, failedToOverrideTimezone: false };
|
||||||
|
browsingContextToAgents.set(browsingContext, data);
|
||||||
|
|
||||||
|
const applySetting = {
|
||||||
geolocation: (geolocation) => {
|
geolocation: (geolocation) => {
|
||||||
if (geolocation) {
|
if (geolocation) {
|
||||||
docShell.setGeolocationOverride({
|
docShell.setGeolocationOverride({
|
||||||
|
@ -51,7 +62,7 @@ const applySetting = {
|
||||||
},
|
},
|
||||||
|
|
||||||
timezoneId: (timezoneId) => {
|
timezoneId: (timezoneId) => {
|
||||||
failedToOverrideTimezone = !docShell.overrideTimezone(timezoneId);
|
data.failedToOverrideTimezone = !docShell.overrideTimezone(timezoneId);
|
||||||
},
|
},
|
||||||
|
|
||||||
locale: (locale) => {
|
locale: (locale) => {
|
||||||
|
@ -59,68 +70,56 @@ const applySetting = {
|
||||||
},
|
},
|
||||||
|
|
||||||
scrollbarsHidden: (hidden) => {
|
scrollbarsHidden: (hidden) => {
|
||||||
frameTree.setScrollbarsHidden(hidden);
|
data.frameTree.setScrollbarsHidden(hidden);
|
||||||
},
|
},
|
||||||
|
|
||||||
colorScheme: (colorScheme) => {
|
javaScriptDisabled: (javaScriptDisabled) => {
|
||||||
frameTree.setColorScheme(colorScheme);
|
data.frameTree.setJavaScriptDisabled(javaScriptDisabled);
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
reducedMotion: (reducedMotion) => {
|
const contextCrossProcessCookie = Services.cpmm.sharedData.get('juggler:context-cookie-' + browsingContext.originAttributes.userContextId) || { initScripts: [], bindings: [], settings: {} };
|
||||||
frameTree.setReducedMotion(reducedMotion);
|
const pageCrossProcessCookie = Services.cpmm.sharedData.get('juggler:page-cookie-' + browsingContext.browserId) || { initScripts: [], bindings: [], interceptFileChooserDialog: false };
|
||||||
},
|
|
||||||
|
|
||||||
forcedColors: (forcedColors) => {
|
|
||||||
frameTree.setForcedColors(forcedColors);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const channel = SimpleChannel.createForMessageManager('content::page', messageManager);
|
|
||||||
|
|
||||||
function initialize() {
|
|
||||||
const response = sendSyncMessage('juggler:content-ready')[0];
|
|
||||||
// If we didn't get a response, then we don't want to do anything
|
|
||||||
// as a part of this frame script.
|
|
||||||
if (!response)
|
|
||||||
return;
|
|
||||||
const {
|
|
||||||
initScripts = [],
|
|
||||||
bindings = [],
|
|
||||||
settings = {}
|
|
||||||
} = response || {};
|
|
||||||
// Enforce focused state for all top level documents.
|
// Enforce focused state for all top level documents.
|
||||||
docShell.overrideHasFocus = true;
|
docShell.overrideHasFocus = true;
|
||||||
docShell.forceActiveState = true;
|
docShell.forceActiveState = true;
|
||||||
frameTree = new FrameTree(docShell);
|
docShell.disallowBFCache = true;
|
||||||
for (const [name, value] of Object.entries(settings)) {
|
data.frameTree = new FrameTree(docShell);
|
||||||
|
for (const [name, value] of Object.entries(contextCrossProcessCookie.settings)) {
|
||||||
if (value !== undefined)
|
if (value !== undefined)
|
||||||
applySetting[name](value);
|
applySetting[name](value);
|
||||||
}
|
}
|
||||||
for (const { worldName, name, script } of bindings)
|
for (const { worldName, name, script } of [...contextCrossProcessCookie.bindings, ...pageCrossProcessCookie.bindings])
|
||||||
frameTree.addBinding(worldName, name, script);
|
data.frameTree.addBinding(worldName, name, script);
|
||||||
frameTree.setInitScripts(initScripts);
|
data.frameTree.setInitScripts([...contextCrossProcessCookie.initScripts, ...pageCrossProcessCookie.initScripts]);
|
||||||
|
data.channel = SimpleChannel.createForActor(actor);
|
||||||
|
data.pageAgent = new PageAgent(data.channel, data.frameTree);
|
||||||
|
docShell.fileInputInterceptionEnabled = !!pageCrossProcessCookie.interceptFileChooserDialog;
|
||||||
|
|
||||||
pageAgent = new PageAgent(messageManager, channel, frameTree);
|
data.channel.register('', {
|
||||||
|
|
||||||
channel.register('', {
|
|
||||||
setInitScripts(scripts) {
|
setInitScripts(scripts) {
|
||||||
frameTree.setInitScripts(scripts);
|
data.frameTree.setInitScripts(scripts);
|
||||||
},
|
},
|
||||||
|
|
||||||
addBinding({worldName, name, script}) {
|
addBinding({worldName, name, script}) {
|
||||||
frameTree.addBinding(worldName, name, script);
|
data.frameTree.addBinding(worldName, name, script);
|
||||||
},
|
},
|
||||||
|
|
||||||
applyContextSetting({name, value}) {
|
applyContextSetting({name, value}) {
|
||||||
applySetting[name](value);
|
applySetting[name](value);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setInterceptFileChooserDialog(enabled) {
|
||||||
|
docShell.fileInputInterceptionEnabled = !!enabled;
|
||||||
|
},
|
||||||
|
|
||||||
ensurePermissions() {
|
ensurePermissions() {
|
||||||
// noop, just a rountrip.
|
// noop, just a rountrip.
|
||||||
},
|
},
|
||||||
|
|
||||||
hasFailedToOverrideTimezone() {
|
hasFailedToOverrideTimezone() {
|
||||||
return failedToOverrideTimezone;
|
return data.failedToOverrideTimezone;
|
||||||
},
|
},
|
||||||
|
|
||||||
async awaitViewportDimensions({width, height, deviceSizeIsPageSize}) {
|
async awaitViewportDimensions({width, height, deviceSizeIsPageSize}) {
|
||||||
|
@ -142,14 +141,8 @@ function initialize() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const gListeners = [
|
return data;
|
||||||
helper.addEventListener(messageManager, 'unload', msg => {
|
|
||||||
helper.removeListeners(gListeners);
|
|
||||||
pageAgent.dispose();
|
|
||||||
frameTree.dispose();
|
|
||||||
channel.dispose();
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize();
|
var EXPORTED_SYMBOLS = ['initialize'];
|
||||||
|
this.initialize = initialize;
|
||||||
|
|
|
@ -11,11 +11,13 @@ juggler.jar:
|
||||||
content/NetworkObserver.js (NetworkObserver.js)
|
content/NetworkObserver.js (NetworkObserver.js)
|
||||||
content/TargetRegistry.js (TargetRegistry.js)
|
content/TargetRegistry.js (TargetRegistry.js)
|
||||||
content/SimpleChannel.js (SimpleChannel.js)
|
content/SimpleChannel.js (SimpleChannel.js)
|
||||||
|
content/JugglerFrameParent.jsm (JugglerFrameParent.jsm)
|
||||||
content/protocol/PrimitiveTypes.js (protocol/PrimitiveTypes.js)
|
content/protocol/PrimitiveTypes.js (protocol/PrimitiveTypes.js)
|
||||||
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/BrowserHandler.js (protocol/BrowserHandler.js)
|
content/protocol/BrowserHandler.js (protocol/BrowserHandler.js)
|
||||||
|
content/content/JugglerFrameChild.jsm (content/JugglerFrameChild.jsm)
|
||||||
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)
|
||||||
|
|
|
@ -214,7 +214,7 @@ class BrowserHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
async ['Browser.setJavaScriptDisabled']({browserContextId, javaScriptDisabled}) {
|
async ['Browser.setJavaScriptDisabled']({browserContextId, javaScriptDisabled}) {
|
||||||
await this._targetRegistry.browserContextForId(browserContextId).setJavaScriptDisabled(javaScriptDisabled);
|
await this._targetRegistry.browserContextForId(browserContextId).applySetting('javaScriptDisabled', nullToUndefined(javaScriptDisabled));
|
||||||
}
|
}
|
||||||
|
|
||||||
async ['Browser.setLocaleOverride']({browserContextId, locale}) {
|
async ['Browser.setLocaleOverride']({browserContextId, locale}) {
|
||||||
|
|
|
@ -89,6 +89,11 @@ class PageHandler {
|
||||||
// to be ignored by the protocol clients.
|
// to be ignored by the protocol clients.
|
||||||
this._isPageReady = false;
|
this._isPageReady = false;
|
||||||
|
|
||||||
|
// Whether the page is about to go cross-process after navigation.
|
||||||
|
this._isTransferringCrossProcessNavigation = false;
|
||||||
|
this._mainFrameId = undefined;
|
||||||
|
this._lastMainFrameNavigationId = undefined;
|
||||||
|
|
||||||
if (this._pageTarget.videoRecordingInfo())
|
if (this._pageTarget.videoRecordingInfo())
|
||||||
this._onVideoRecordingStarted();
|
this._onVideoRecordingStarted();
|
||||||
|
|
||||||
|
@ -100,6 +105,7 @@ class PageHandler {
|
||||||
}),
|
}),
|
||||||
helper.on(this._pageTarget, PageTarget.Events.ScreencastStarted, this._onVideoRecordingStarted.bind(this)),
|
helper.on(this._pageTarget, PageTarget.Events.ScreencastStarted, this._onVideoRecordingStarted.bind(this)),
|
||||||
helper.on(this._pageTarget, PageTarget.Events.ScreencastFrame, this._onScreencastFrame.bind(this)),
|
helper.on(this._pageTarget, PageTarget.Events.ScreencastFrame, this._onScreencastFrame.bind(this)),
|
||||||
|
helper.on(this._pageTarget, PageTarget.Events.TopBrowsingContextReplaced, this._onTopBrowsingContextReplaced.bind(this)),
|
||||||
helper.on(this._pageNetwork, PageNetwork.Events.Request, this._handleNetworkEvent.bind(this, 'Network.requestWillBeSent')),
|
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.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.RequestFinished, this._handleNetworkEvent.bind(this, 'Network.requestFinished')),
|
||||||
|
@ -113,9 +119,9 @@ class PageHandler {
|
||||||
pageFrameDetached: emitProtocolEvent('Page.frameDetached'),
|
pageFrameDetached: emitProtocolEvent('Page.frameDetached'),
|
||||||
pageLinkClicked: emitProtocolEvent('Page.linkClicked'),
|
pageLinkClicked: emitProtocolEvent('Page.linkClicked'),
|
||||||
pageWillOpenNewWindowAsynchronously: emitProtocolEvent('Page.willOpenNewWindowAsynchronously'),
|
pageWillOpenNewWindowAsynchronously: emitProtocolEvent('Page.willOpenNewWindowAsynchronously'),
|
||||||
pageNavigationAborted: emitProtocolEvent('Page.navigationAborted'),
|
pageNavigationAborted: params => this._handleNavigationEvent('Page.navigationAborted', params),
|
||||||
pageNavigationCommitted: emitProtocolEvent('Page.navigationCommitted'),
|
pageNavigationCommitted: params => this._handleNavigationEvent('Page.navigationCommitted', params),
|
||||||
pageNavigationStarted: emitProtocolEvent('Page.navigationStarted'),
|
pageNavigationStarted: params => this._handleNavigationEvent('Page.navigationStarted', params),
|
||||||
pageReady: this._onPageReady.bind(this),
|
pageReady: this._onPageReady.bind(this),
|
||||||
pageSameDocumentNavigation: emitProtocolEvent('Page.sameDocumentNavigation'),
|
pageSameDocumentNavigation: emitProtocolEvent('Page.sameDocumentNavigation'),
|
||||||
pageUncaughtError: emitProtocolEvent('Page.uncaughtError'),
|
pageUncaughtError: emitProtocolEvent('Page.uncaughtError'),
|
||||||
|
@ -129,10 +135,11 @@ class PageHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emitProtocolEvent('Runtime.console')(params);
|
this._session.emitEvent('Runtime.console', params);
|
||||||
},
|
},
|
||||||
runtimeExecutionContextCreated: emitProtocolEvent('Runtime.executionContextCreated'),
|
runtimeExecutionContextCreated: emitProtocolEvent('Runtime.executionContextCreated'),
|
||||||
runtimeExecutionContextDestroyed: emitProtocolEvent('Runtime.executionContextDestroyed'),
|
runtimeExecutionContextDestroyed: emitProtocolEvent('Runtime.executionContextDestroyed'),
|
||||||
|
runtimeExecutionContextsCleared: emitProtocolEvent('Runtime.executionContextsCleared'),
|
||||||
|
|
||||||
webSocketCreated: emitProtocolEvent('Page.webSocketCreated'),
|
webSocketCreated: emitProtocolEvent('Page.webSocketCreated'),
|
||||||
webSocketOpened: emitProtocolEvent('Page.webSocketOpened'),
|
webSocketOpened: emitProtocolEvent('Page.webSocketOpened'),
|
||||||
|
@ -157,6 +164,28 @@ class PageHandler {
|
||||||
this._session.emitEvent('Page.screencastFrame', params);
|
this._session.emitEvent('Page.screencastFrame', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onTopBrowsingContextReplaced() {
|
||||||
|
this._isTransferringCrossProcessNavigation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleNavigationEvent(event, params) {
|
||||||
|
if (this._isTransferringCrossProcessNavigation && params.frameId === this._mainFrameId) {
|
||||||
|
// During a cross-process navigation, http channel in the new process might not be
|
||||||
|
// the same as the original one in the old process, for example after a redirect/interception.
|
||||||
|
// Therefore, the new proces has a new navigationId.
|
||||||
|
//
|
||||||
|
// To preserve protocol consistency, we replace the new navigationId with
|
||||||
|
// the old navigationId.
|
||||||
|
params.navigationId = this._lastMainFrameNavigationId || params.navigationId;
|
||||||
|
if (event === 'Page.navigationCommitted' || event === 'Page.navigationAborted')
|
||||||
|
this._isTransferringCrossProcessNavigation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event === 'Page.navigationStarted' && params.frameId === this._mainFrameId)
|
||||||
|
this._lastMainFrameNavigationId = params.navigationId;
|
||||||
|
this._session.emitEvent(event, params);
|
||||||
|
}
|
||||||
|
|
||||||
_onPageReady(event) {
|
_onPageReady(event) {
|
||||||
this._isPageReady = true;
|
this._isPageReady = true;
|
||||||
this._session.emitEvent('Page.ready');
|
this._session.emitEvent('Page.ready');
|
||||||
|
@ -210,6 +239,8 @@ class PageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFrameAttached({frameId, parentFrameId}) {
|
_onFrameAttached({frameId, parentFrameId}) {
|
||||||
|
if (!parentFrameId)
|
||||||
|
this._mainFrameId = frameId;
|
||||||
this._session.emitEvent('Page.frameAttached', {frameId, parentFrameId});
|
this._session.emitEvent('Page.frameAttached', {frameId, parentFrameId});
|
||||||
this._reportedFrameIds.add(frameId);
|
this._reportedFrameIds.add(frameId);
|
||||||
const events = this._networkEventsForUnreportedFrameIds.get(frameId) || [];
|
const events = this._networkEventsForUnreportedFrameIds.get(frameId) || [];
|
||||||
|
@ -295,8 +326,8 @@ class PageHandler {
|
||||||
return await this._contentPage.send('setCacheDisabled', options);
|
return await this._contentPage.send('setCacheDisabled', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ['Page.addBinding'](options) {
|
async ['Page.addBinding']({ worldName, name, script }) {
|
||||||
return await this._contentPage.send('addBinding', options);
|
return await this._pageTarget.addBinding(worldName, name, script);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ['Page.adoptNode'](options) {
|
async ['Page.adoptNode'](options) {
|
||||||
|
@ -358,16 +389,25 @@ class PageHandler {
|
||||||
return await this._contentPage.send('navigate', options);
|
return await this._contentPage.send('navigate', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ['Page.goBack'](options) {
|
async ['Page.goBack']({}) {
|
||||||
return await this._contentPage.send('goBack', options);
|
const browsingContext = this._pageTarget.linkedBrowser().browsingContext;
|
||||||
|
if (!browsingContext.embedderElement?.canGoBack)
|
||||||
|
return { success: false };
|
||||||
|
browsingContext.goBack();
|
||||||
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
async ['Page.goForward'](options) {
|
async ['Page.goForward']({}) {
|
||||||
return await this._contentPage.send('goForward', options);
|
const browsingContext = this._pageTarget.linkedBrowser().browsingContext;
|
||||||
|
if (!browsingContext.embedderElement?.canGoForward)
|
||||||
|
return { success: false };
|
||||||
|
browsingContext.goForward();
|
||||||
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
async ['Page.reload'](options) {
|
async ['Page.reload']({}) {
|
||||||
return await this._contentPage.send('reload', options);
|
const browsingContext = this._pageTarget.linkedBrowser().browsingContext;
|
||||||
|
browsingContext.reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ['Page.describeNode'](options) {
|
async ['Page.describeNode'](options) {
|
||||||
|
@ -438,8 +478,8 @@ class PageHandler {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
async ['Page.setInterceptFileChooserDialog'](options) {
|
async ['Page.setInterceptFileChooserDialog']({ enabled }) {
|
||||||
return await this._contentPage.send('setInterceptFileChooserDialog', options);
|
return await this._pageTarget.setInterceptFileChooserDialog(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ['Page.startScreencast'](options) {
|
async ['Page.startScreencast'](options) {
|
||||||
|
|
|
@ -227,6 +227,7 @@ const Browser = {
|
||||||
uuid: t.String,
|
uuid: t.String,
|
||||||
browserContextId: t.Optional(t.String),
|
browserContextId: t.Optional(t.String),
|
||||||
pageTargetId: t.String,
|
pageTargetId: t.String,
|
||||||
|
frameId: t.String,
|
||||||
url: t.String,
|
url: t.String,
|
||||||
suggestedFileName: t.String,
|
suggestedFileName: t.String,
|
||||||
},
|
},
|
||||||
|
@ -573,6 +574,8 @@ const Runtime = {
|
||||||
'executionContextDestroyed': {
|
'executionContextDestroyed': {
|
||||||
executionContextId: t.String,
|
executionContextId: t.String,
|
||||||
},
|
},
|
||||||
|
'executionContextsCleared': {
|
||||||
|
},
|
||||||
'console': {
|
'console': {
|
||||||
executionContextId: t.String,
|
executionContextId: t.String,
|
||||||
args: t.Array(runtimeTypes.RemoteObject),
|
args: t.Array(runtimeTypes.RemoteObject),
|
||||||
|
@ -847,7 +850,8 @@ const Page = {
|
||||||
'adoptNode': {
|
'adoptNode': {
|
||||||
params: {
|
params: {
|
||||||
frameId: t.String,
|
frameId: t.String,
|
||||||
objectId: t.String,
|
// Missing objectId adopts frame owner.
|
||||||
|
objectId: t.Optional(t.String),
|
||||||
executionContextId: t.String,
|
executionContextId: t.String,
|
||||||
},
|
},
|
||||||
returns: {
|
returns: {
|
||||||
|
|
|
@ -19,7 +19,9 @@ using namespace webrtc;
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
rtc::scoped_refptr<webrtc::VideoCaptureModuleEx> HeadlessWindowCapturer::Create(HeadlessWidget* headlessWindow) {
|
rtc::scoped_refptr<webrtc::VideoCaptureModuleEx> HeadlessWindowCapturer::Create(HeadlessWidget* headlessWindow) {
|
||||||
return new rtc::RefCountedObject<HeadlessWindowCapturer>(headlessWindow);
|
return rtc::scoped_refptr<webrtc::VideoCaptureModuleEx>(
|
||||||
|
new rtc::RefCountedObject<HeadlessWindowCapturer>(headlessWindow)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
HeadlessWindowCapturer::HeadlessWindowCapturer(mozilla::widget::HeadlessWidget* window)
|
HeadlessWindowCapturer::HeadlessWindowCapturer(mozilla::widget::HeadlessWidget* window)
|
||||||
|
|
|
@ -55,7 +55,7 @@ rtc::scoped_refptr<webrtc::VideoCaptureModuleEx> CreateWindowCapturer(nsIWidget*
|
||||||
windowId.AppendPrintf("%" PRIuPTR, rawWindowId);
|
windowId.AppendPrintf("%" PRIuPTR, rawWindowId);
|
||||||
bool captureCursor = false;
|
bool captureCursor = false;
|
||||||
static int moduleId = 0;
|
static int moduleId = 0;
|
||||||
return webrtc::DesktopCaptureImpl::Create(++moduleId, windowId.get(), CaptureDeviceType::Window, captureCursor);
|
return rtc::scoped_refptr<webrtc::VideoCaptureModuleEx>(webrtc::DesktopCaptureImpl::Create(++moduleId, windowId.get(), CaptureDeviceType::Window, captureCursor));
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult generateUid(nsString& uid) {
|
nsresult generateUid(nsString& uid) {
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -8,24 +8,88 @@ pref("datareporting.policy.dataSubmissionEnabled", false);
|
||||||
pref("datareporting.policy.dataSubmissionPolicyAccepted", false);
|
pref("datareporting.policy.dataSubmissionPolicyAccepted", false);
|
||||||
pref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
|
pref("datareporting.policy.dataSubmissionPolicyBypassNotification", true);
|
||||||
|
|
||||||
// @see https://github.com/microsoft/playwright/issues/4297
|
// Force pdfs into downloads.
|
||||||
pref("browser.tabs.remote.useCrossOriginEmbedderPolicy", false);
|
|
||||||
pref("browser.tabs.remote.useCrossOriginOpenerPolicy", false);
|
|
||||||
|
|
||||||
pref("browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", false);
|
|
||||||
|
|
||||||
pref("pdfjs.disabled", true);
|
pref("pdfjs.disabled", true);
|
||||||
|
|
||||||
// Disable all kinds of cross-process navigations until we are ready.
|
// Disable cross-process iframes, but not cross-process navigations.
|
||||||
pref("fission.autostart", false);
|
|
||||||
pref("fission.webContentIsolationStrategy", 0);
|
pref("fission.webContentIsolationStrategy", 0);
|
||||||
|
|
||||||
|
// Disable BFCache in parent process.
|
||||||
|
// We also separately disable BFCache in content via docSchell property.
|
||||||
pref("fission.bfcacheInParent", false);
|
pref("fission.bfcacheInParent", false);
|
||||||
// Avoid about:blank loading cross-process until we are ready.
|
|
||||||
pref("browser.tabs.remote.systemTriggeredAboutBlankAnywhere", true);
|
// File url navigations behave differently from http, we are not ready.
|
||||||
|
pref("browser.tabs.remote.separateFileUriProcess", false);
|
||||||
|
|
||||||
|
// Disable first-party-based cookie partitioning.
|
||||||
|
// When it is enabled, we have to retain "thirdPartyCookie^" permissions
|
||||||
|
// in the storageState.
|
||||||
|
pref("network.cookie.cookieBehavior", 4);
|
||||||
|
|
||||||
|
// Increase max number of child web processes so that new pages
|
||||||
|
// get a new process by default and we have a process isolation
|
||||||
|
// between pages from different contexts. If this becomes a performance
|
||||||
|
// issue we can povide custom '@mozilla.org/ipc/processselector;1'
|
||||||
|
pref("dom.ipc.processCount", 60000);
|
||||||
|
|
||||||
|
// Never reuse processes as they may keep previously overridden values
|
||||||
|
// (locale, timezone etc.).
|
||||||
|
pref("dom.ipc.processPrelaunch.enabled", false);
|
||||||
|
|
||||||
|
// Isolate permissions by user context.
|
||||||
|
pref("permissions.isolateBy.userContext", true);
|
||||||
|
|
||||||
|
// We need this to issue Page.navigate from inside the renderer
|
||||||
|
// to cross-process domains, for example file urls.
|
||||||
|
pref("security.sandbox.content.level", 2);
|
||||||
|
|
||||||
|
// Allow creating files in content process - required for
|
||||||
|
// |Page.setFileInputFiles| protocol method.
|
||||||
|
pref("dom.file.createInChild", true);
|
||||||
|
|
||||||
|
// Do not warn when closing all open tabs
|
||||||
|
pref("browser.tabs.warnOnClose", false);
|
||||||
|
|
||||||
|
// Do not warn when closing all other open tabs
|
||||||
|
pref("browser.tabs.warnOnCloseOtherTabs", false);
|
||||||
|
|
||||||
|
// Do not warn when multiple tabs will be opened
|
||||||
|
pref("browser.tabs.warnOnOpen", false);
|
||||||
|
|
||||||
|
// Do not warn on quitting Firefox
|
||||||
|
pref("browser.warnOnQuit", false);
|
||||||
|
|
||||||
|
// Disable popup-blocker
|
||||||
|
pref("dom.disable_open_during_load", false);
|
||||||
|
|
||||||
|
// Disable the ProcessHangMonitor
|
||||||
|
pref("dom.ipc.reportProcessHangs", false);
|
||||||
|
pref("hangmonitor.timeout", 0);
|
||||||
|
|
||||||
|
// Allow the application to have focus even it runs in the background
|
||||||
|
pref("focusmanager.testmode", true);
|
||||||
|
|
||||||
|
// No ICC color correction. We need this for reproducible screenshots.
|
||||||
|
// See https://developer.mozilla.org/en/docs/Mozilla/Firefox/Releases/3.5/ICC_color_correction_in_Firefox.
|
||||||
|
pref("gfx.color_management.mode", 0);
|
||||||
|
pref("gfx.color_management.rendering_intent", 3);
|
||||||
|
|
||||||
|
// Always use network provider for geolocation tests so we bypass the
|
||||||
|
// macOS dialog raised by the corelocation provider
|
||||||
|
pref("geo.provider.testing", true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================================================================
|
// =================================================================
|
||||||
|
// THESE ARE NICHE PROPERTIES THAT ARE NICE TO HAVE
|
||||||
// =================================================================
|
// =================================================================
|
||||||
|
|
||||||
|
// Avoid stalling on shutdown, after "xpcom-will-shutdown" phase.
|
||||||
|
// This at least happens when shutting down soon after launching.
|
||||||
|
// See AppShutdown.cpp for more details on shutdown phases.
|
||||||
|
pref("toolkit.shutdown.fastShutdownStage", 3);
|
||||||
|
|
||||||
// @see https://github.com/microsoft/playwright/issues/8178
|
// @see https://github.com/microsoft/playwright/issues/8178
|
||||||
pref("dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", true);
|
pref("dom.postMessage.sharedArrayBuffer.bypassCOOP_COEP.insecure.enabled", true);
|
||||||
|
|
||||||
|
@ -36,23 +100,9 @@ pref("ui.systemUsesDarkTheme", 0);
|
||||||
// support for the new modal UI (see Bug 1686743).
|
// support for the new modal UI (see Bug 1686743).
|
||||||
pref("prompts.contentPromptSubDialog", false);
|
pref("prompts.contentPromptSubDialog", false);
|
||||||
|
|
||||||
// Increase max number of child web processes so that new pages
|
|
||||||
// get a new process by default and we have a process isolation
|
|
||||||
// between pages from different contexts. If this becomes a performance
|
|
||||||
// issue we can povide custom '@mozilla.org/ipc/processselector;1'
|
|
||||||
//
|
|
||||||
pref("dom.ipc.processCount", 60000);
|
|
||||||
|
|
||||||
// Never reuse processes as they may keep previously overridden values
|
|
||||||
// (locale, timezone etc.).
|
|
||||||
pref("dom.ipc.processPrelaunch.enabled", false);
|
|
||||||
|
|
||||||
// Do not use system colors - they are affected by themes.
|
// Do not use system colors - they are affected by themes.
|
||||||
pref("ui.use_standins_for_native_colors", true);
|
pref("ui.use_standins_for_native_colors", true);
|
||||||
|
|
||||||
// Isolate permissions by user context.
|
|
||||||
pref("permissions.isolateBy.userContext", true);
|
|
||||||
|
|
||||||
pref("dom.push.serverURL", "");
|
pref("dom.push.serverURL", "");
|
||||||
// This setting breaks settings loading.
|
// This setting breaks settings loading.
|
||||||
pref("services.settings.server", "");
|
pref("services.settings.server", "");
|
||||||
|
@ -87,14 +137,10 @@ pref("browser.newtabpage.enabled", false);
|
||||||
// Do not redirect user when a milstone upgrade of Firefox is detected
|
// Do not redirect user when a milstone upgrade of Firefox is detected
|
||||||
pref("browser.startup.homepage_override.mstone", "ignore");
|
pref("browser.startup.homepage_override.mstone", "ignore");
|
||||||
|
|
||||||
pref("browser.tabs.remote.separateFileUriProcess", false);
|
|
||||||
pref("security.sandbox.content.level", 2);
|
|
||||||
|
|
||||||
// Disable topstories
|
// Disable topstories
|
||||||
pref("browser.newtabpage.activity-stream.feeds.section.topstories", false);
|
pref("browser.newtabpage.activity-stream.feeds.section.topstories", false);
|
||||||
// DevTools JSONViewer sometimes fails to load dependencies with its require.js.
|
// DevTools JSONViewer sometimes fails to load dependencies with its require.js.
|
||||||
// This doesn't affect Puppeteer operations, but spams console with a lot of
|
// This spams console with a lot of unpleasant errors.
|
||||||
// unpleasant errors.
|
|
||||||
// (bug 1424372)
|
// (bug 1424372)
|
||||||
pref("devtools.jsonview.enabled", false);
|
pref("devtools.jsonview.enabled", false);
|
||||||
|
|
||||||
|
@ -107,10 +153,6 @@ pref("devtools.jsonview.enabled", false);
|
||||||
// (bug 1176798, bug 1177018, bug 1210465)
|
// (bug 1176798, bug 1177018, bug 1210465)
|
||||||
pref("apz.content_response_timeout", 60000);
|
pref("apz.content_response_timeout", 60000);
|
||||||
|
|
||||||
// Allow creating files in content process - required for
|
|
||||||
// |Page.setFileInputFiles| protocol method.
|
|
||||||
pref("dom.file.createInChild", true);
|
|
||||||
|
|
||||||
// Indicate that the download panel has been shown once so that
|
// Indicate that the download panel has been shown once so that
|
||||||
// whichever download test runs first doesn't show the popup
|
// whichever download test runs first doesn't show the popup
|
||||||
// inconsistently.
|
// inconsistently.
|
||||||
|
@ -142,15 +184,6 @@ pref("browser.tabs.closeWindowWithLastTab", true);
|
||||||
// unloaded
|
// unloaded
|
||||||
pref("browser.tabs.disableBackgroundZombification", false);
|
pref("browser.tabs.disableBackgroundZombification", false);
|
||||||
|
|
||||||
// Do not warn when closing all open tabs
|
|
||||||
pref("browser.tabs.warnOnClose", false);
|
|
||||||
|
|
||||||
// Do not warn when closing all other open tabs
|
|
||||||
pref("browser.tabs.warnOnCloseOtherTabs", false);
|
|
||||||
|
|
||||||
// Do not warn when multiple tabs will be opened
|
|
||||||
pref("browser.tabs.warnOnOpen", false);
|
|
||||||
|
|
||||||
// Disable first run splash page on Windows 10
|
// Disable first run splash page on Windows 10
|
||||||
pref("browser.usedOnWindows10.introURL", "");
|
pref("browser.usedOnWindows10.introURL", "");
|
||||||
|
|
||||||
|
@ -163,9 +196,6 @@ pref("browser.uitour.enabled", false);
|
||||||
// network connections.
|
// network connections.
|
||||||
pref("browser.urlbar.suggest.searches", false);
|
pref("browser.urlbar.suggest.searches", false);
|
||||||
|
|
||||||
// Do not warn on quitting Firefox
|
|
||||||
pref("browser.warnOnQuit", false);
|
|
||||||
|
|
||||||
// Do not show datareporting policy notifications which can
|
// Do not show datareporting policy notifications which can
|
||||||
// interfere with tests
|
// interfere with tests
|
||||||
pref("datareporting.healthreport.documentServerURI", "");
|
pref("datareporting.healthreport.documentServerURI", "");
|
||||||
|
@ -178,13 +208,6 @@ pref("datareporting.healthreport.uploadEnabled", false);
|
||||||
// Automatically unload beforeunload alerts
|
// Automatically unload beforeunload alerts
|
||||||
pref("dom.disable_beforeunload", false);
|
pref("dom.disable_beforeunload", false);
|
||||||
|
|
||||||
// Disable popup-blocker
|
|
||||||
pref("dom.disable_open_during_load", false);
|
|
||||||
|
|
||||||
// Disable the ProcessHangMonitor
|
|
||||||
pref("dom.ipc.reportProcessHangs", false);
|
|
||||||
pref("hangmonitor.timeout", 0);
|
|
||||||
|
|
||||||
// Disable slow script dialogues
|
// Disable slow script dialogues
|
||||||
pref("dom.max_chrome_script_run_time", 0);
|
pref("dom.max_chrome_script_run_time", 0);
|
||||||
pref("dom.max_script_run_time", 0);
|
pref("dom.max_script_run_time", 0);
|
||||||
|
@ -210,21 +233,9 @@ pref("extensions.webservice.discoverURL", "");
|
||||||
pref("extensions.screenshots.disabled", true);
|
pref("extensions.screenshots.disabled", true);
|
||||||
pref("extensions.screenshots.upload-disabled", true);
|
pref("extensions.screenshots.upload-disabled", true);
|
||||||
|
|
||||||
// Allow the application to have focus even it runs in the background
|
|
||||||
pref("focusmanager.testmode", true);
|
|
||||||
|
|
||||||
// Disable useragent updates
|
// Disable useragent updates
|
||||||
pref("general.useragent.updates.enabled", false);
|
pref("general.useragent.updates.enabled", false);
|
||||||
|
|
||||||
// No ICC color correction.
|
|
||||||
// See https://developer.mozilla.org/en/docs/Mozilla/Firefox/Releases/3.5/ICC_color_correction_in_Firefox.
|
|
||||||
pref("gfx.color_management.mode", 0);
|
|
||||||
pref("gfx.color_management.rendering_intent", 3);
|
|
||||||
|
|
||||||
// Always use network provider for geolocation tests so we bypass the
|
|
||||||
// macOS dialog raised by the corelocation provider
|
|
||||||
pref("geo.provider.testing", true);
|
|
||||||
|
|
||||||
// Do not scan Wifi
|
// Do not scan Wifi
|
||||||
pref("geo.wifi.scan", false);
|
pref("geo.wifi.scan", false);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
REMOTE_URL="https://github.com/WebKit/WebKit.git"
|
REMOTE_URL="https://github.com/WebKit/WebKit.git"
|
||||||
BASE_BRANCH="main"
|
BASE_BRANCH="main"
|
||||||
BASE_REVISION="c888c485b787e204057b56d69536aae567ab8b3a"
|
BASE_REVISION="675d141bdcf7fa6df9bdf505d46e46fdac638452"
|
||||||
|
|
|
@ -229,6 +229,7 @@ const NSActivityOptions ActivityOptions =
|
||||||
if (!configuration) {
|
if (!configuration) {
|
||||||
configuration = [[WKWebViewConfiguration alloc] init];
|
configuration = [[WKWebViewConfiguration alloc] init];
|
||||||
configuration.websiteDataStore = [self persistentDataStore];
|
configuration.websiteDataStore = [self persistentDataStore];
|
||||||
|
configuration._controlledByAutomation = true;
|
||||||
configuration.preferences._fullScreenEnabled = YES;
|
configuration.preferences._fullScreenEnabled = YES;
|
||||||
configuration.preferences._developerExtrasEnabled = YES;
|
configuration.preferences._developerExtrasEnabled = YES;
|
||||||
configuration.preferences._mediaDevicesEnabled = YES;
|
configuration.preferences._mediaDevicesEnabled = YES;
|
||||||
|
@ -496,6 +497,12 @@ const NSActivityOptions ActivityOptions =
|
||||||
download.delegate = self;
|
download.delegate = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always automatically accept requestStorageAccess dialog.
|
||||||
|
- (void)_webView:(WKWebView *)webView requestStorageAccessPanelForDomain:(NSString *)requestingDomain underCurrentDomain:(NSString *)currentDomain completionHandler:(void (^)(BOOL result))completionHandler
|
||||||
|
{
|
||||||
|
completionHandler(true);
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark WKDownloadDelegate
|
#pragma mark WKDownloadDelegate
|
||||||
|
|
||||||
- (void)download:(WKDownload *)download decideDestinationUsingResponse:(NSURLResponse *)response suggestedFilename:(NSString *)suggestedFilename completionHandler:(void (^)(NSURL * _Nullable destination))completionHandler
|
- (void)download:(WKDownload *)download decideDestinationUsingResponse:(NSURLResponse *)response suggestedFilename:(NSString *)suggestedFilename completionHandler:(void (^)(NSURL * _Nullable destination))completionHandler
|
||||||
|
|
|
@ -67,10 +67,22 @@ static void* keyValueObservingContext = &keyValueObservingContext;
|
||||||
|
|
||||||
- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
|
- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
|
||||||
{
|
{
|
||||||
// Retain the frame size, but make sure that
|
float kWindowControlBarHeight = 35;
|
||||||
// the top of the window is always visible.
|
|
||||||
CGFloat yPos = NSHeight(self.screen.frame) - 100 - NSHeight(self.frame);
|
CGFloat screenHeight = screen.frame.size.height; // e.g. 1080
|
||||||
return NSMakeRect(frameRect.origin.x, yPos, frameRect.size.width, frameRect.size.height);
|
CGFloat windowHeight = self.frame.size.height; // e.g. 5000
|
||||||
|
CGFloat screenYOffset = screen.frame.origin.y; // screen arrangement offset
|
||||||
|
|
||||||
|
bool exceedsAtTheTop = (NSMaxY(frameRect) - screenYOffset) > screenHeight;
|
||||||
|
bool exceedsAtTheBottom = (frameRect.origin.y + windowHeight + -screenYOffset - kWindowControlBarHeight) < 0;
|
||||||
|
CGFloat newOriginY = frameRect.origin.y;
|
||||||
|
// if it exceeds the height, then we move it to the top of the screen
|
||||||
|
if (screenHeight > 0 && exceedsAtTheTop)
|
||||||
|
newOriginY = screenHeight - windowHeight - kWindowControlBarHeight + screenYOffset;
|
||||||
|
// if it exceeds the bottom, then we move it to the bottom of the screen but make sure that the control bar is still visible
|
||||||
|
else if (screenHeight > 0 && exceedsAtTheBottom)
|
||||||
|
newOriginY = -windowHeight + screenYOffset + kWindowControlBarHeight;
|
||||||
|
return NSMakeRect(frameRect.origin.x, newOriginY, frameRect.size.width, frameRect.size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -670,6 +682,12 @@ static BOOL areEssentiallyEqual(double a, double b)
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always automatically accept requestStorageAccess dialog.
|
||||||
|
- (void)_webView:(WKWebView *)webView requestStorageAccessPanelForDomain:(NSString *)requestingDomain underCurrentDomain:(NSString *)currentDomain completionHandler:(void (^)(BOOL result))completionHandler
|
||||||
|
{
|
||||||
|
completionHandler(true);
|
||||||
|
}
|
||||||
|
|
||||||
- (WKDragDestinationAction)_webView:(WKWebView *)webView dragDestinationActionMaskForDraggingInfo:(id)draggingInfo
|
- (WKDragDestinationAction)_webView:(WKWebView *)webView dragDestinationActionMaskForDraggingInfo:(id)draggingInfo
|
||||||
{
|
{
|
||||||
return WKDragDestinationActionAny;
|
return WKDragDestinationActionAny;
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <WebKit/WKCredential.h>
|
#include <WebKit/WKCredential.h>
|
||||||
#include <WebKit/WKFramePolicyListener.h>
|
#include <WebKit/WKFramePolicyListener.h>
|
||||||
#include <WebKit/WKInspector.h>
|
#include <WebKit/WKInspector.h>
|
||||||
|
#include <WebKit/WKPagePrivate.h>
|
||||||
#include <WebKit/WKProtectionSpace.h>
|
#include <WebKit/WKProtectionSpace.h>
|
||||||
#include <WebKit/WKProtectionSpaceCurl.h>
|
#include <WebKit/WKProtectionSpaceCurl.h>
|
||||||
#include <WebKit/WKWebsiteDataStoreRef.h>
|
#include <WebKit/WKWebsiteDataStoreRef.h>
|
||||||
|
@ -102,6 +103,8 @@ WebKitBrowserWindow::WebKitBrowserWindow(BrowserWindowClient& client, HWND mainW
|
||||||
policyClient.decidePolicyForResponse_deprecatedForUseWithV0 = decidePolicyForResponse;
|
policyClient.decidePolicyForResponse_deprecatedForUseWithV0 = decidePolicyForResponse;
|
||||||
policyClient.decidePolicyForNavigationAction = decidePolicyForNavigationAction;
|
policyClient.decidePolicyForNavigationAction = decidePolicyForNavigationAction;
|
||||||
WKPageSetPagePolicyClient(page, &policyClient.base);
|
WKPageSetPagePolicyClient(page, &policyClient.base);
|
||||||
|
|
||||||
|
WKPageSetControlledByAutomation(page, true);
|
||||||
resetZoom();
|
resetZoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче