chore: update browser patches as of Dec 13, 2022 (#20297)

This commit is contained in:
Andrey Lushnikov 2023-01-23 11:29:48 -08:00 коммит произвёл GitHub
Родитель ba0189f8d7
Коммит 6c5317bd31
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
27 изменённых файлов: 2520 добавлений и 2047 удалений

Просмотреть файл

@ -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();
} }

Разница между файлами не показана из-за своего большого размера Загрузить разницу