browser(firefox): remove multisession logic (#4039)
This patch: 1. Changes `SimpleChannel` to buffer messages to the namespace that hasn't been registered yet. This allows us to create `SimpleChannel` per target on the browser side right away. 2. Removes multisession support. Now there's only one `PageAgent` in the content process, which talks to a single `PageHandler` on the browser side. Both ends can be created as-soon-as-needed; thanks to `SimpleChannel` bufferring, no messages will be lost and all messages will be delivered in proper order. (This is currently the reason why build 1178 flakes on windows). 3. Straightens up the target reporting. Targets are reported as soon as they appear on the browser side. **NOTE:** this doesn't yet remove sessions from protocol. References #3995
This commit is contained in:
parent
5e42029fce
commit
2c11b10598
|
|
@ -1,2 +1,2 @@
|
||||||
1178
|
1179
|
||||||
Changed: lushnikov@chromium.org Wed Sep 30 23:36:27 PDT 2020
|
Changed: lushnikov@chromium.org Fri Oct 2 03:14:15 PDT 2020
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ class SimpleChannel {
|
||||||
this._connectorId = 0;
|
this._connectorId = 0;
|
||||||
this._pendingMessages = new Map();
|
this._pendingMessages = new Map();
|
||||||
this._handlers = new Map();
|
this._handlers = new Map();
|
||||||
|
this._bufferedRequests = [];
|
||||||
this.transport = {
|
this.transport = {
|
||||||
sendMessage: null,
|
sendMessage: null,
|
||||||
dispose: null,
|
dispose: null,
|
||||||
|
|
@ -70,6 +71,14 @@ class SimpleChannel {
|
||||||
if (this._handlers.has(namespace))
|
if (this._handlers.has(namespace))
|
||||||
throw new Error('ERROR: double-register for namespace ' + namespace);
|
throw new Error('ERROR: double-register for namespace ' + namespace);
|
||||||
this._handlers.set(namespace, handler);
|
this._handlers.set(namespace, handler);
|
||||||
|
// Try to re-deliver all pending messages.
|
||||||
|
Promise.resolve().then(() => {
|
||||||
|
const bufferedRequests = this._bufferedRequests;
|
||||||
|
this._bufferedRequests = [];
|
||||||
|
for (const data of bufferedRequests) {
|
||||||
|
this._onMessage(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
return () => this.unregister(namespace);
|
return () => this.unregister(namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,7 +116,7 @@ class SimpleChannel {
|
||||||
const namespace = data.namespace;
|
const namespace = data.namespace;
|
||||||
const handler = this._handlers.get(namespace);
|
const handler = this._handlers.get(namespace);
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
this.transport.sendMessage({responseId: data.requestId, error: `error in channel "${this._name}": No handler for namespace "${namespace}"`});
|
this._bufferedRequests.push(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const method = handler[data.methodName];
|
const method = handler[data.methodName];
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm");
|
const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm");
|
||||||
const {ContextualIdentityService} = ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm");
|
const {ContextualIdentityService} = ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm");
|
||||||
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
|
const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm');
|
||||||
const {PageHandler} = ChromeUtils.import("chrome://juggler/content/protocol/PageHandler.js");
|
|
||||||
const {NetworkHandler} = ChromeUtils.import("chrome://juggler/content/protocol/NetworkHandler.js");
|
|
||||||
const {RuntimeHandler} = ChromeUtils.import("chrome://juggler/content/protocol/RuntimeHandler.js");
|
|
||||||
const {AccessibilityHandler} = ChromeUtils.import("chrome://juggler/content/protocol/AccessibilityHandler.js");
|
|
||||||
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||||
|
|
||||||
const helper = new Helper();
|
const helper = new Helper();
|
||||||
|
|
@ -145,15 +141,10 @@ class TargetRegistry {
|
||||||
if (!target)
|
if (!target)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const sessions = [];
|
|
||||||
const readyData = { sessions, target };
|
|
||||||
target.markAsReported();
|
|
||||||
this.emit(TargetRegistry.Events.TargetCreated, readyData);
|
|
||||||
return {
|
return {
|
||||||
scriptsToEvaluateOnNewDocument: target.browserContext().scriptsToEvaluateOnNewDocument,
|
scriptsToEvaluateOnNewDocument: target.browserContext().scriptsToEvaluateOnNewDocument,
|
||||||
bindings: target.browserContext().bindings,
|
bindings: target.browserContext().bindings,
|
||||||
settings: target.browserContext().settings,
|
settings: target.browserContext().settings,
|
||||||
sessionIds: sessions.map(session => session.sessionId()),
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -162,7 +153,7 @@ class TargetRegistry {
|
||||||
const tab = event.target;
|
const tab = event.target;
|
||||||
const userContextId = tab.userContextId;
|
const userContextId = tab.userContextId;
|
||||||
const browserContext = this._userContextIdToBrowserContext.get(userContextId);
|
const browserContext = this._userContextIdToBrowserContext.get(userContextId);
|
||||||
const hasExplicitSize = (appWindow.chromeFlags & Ci.nsIWebBrowserChrome.JUGGLER_WINDOW_EXPLICIT_SIZE) !== 0;
|
const hasExplicitSize = appWindow && (appWindow.chromeFlags & Ci.nsIWebBrowserChrome.JUGGLER_WINDOW_EXPLICIT_SIZE) !== 0;
|
||||||
const openerContext = tab.linkedBrowser.browsingContext.opener;
|
const openerContext = tab.linkedBrowser.browsingContext.opener;
|
||||||
let openerTarget;
|
let openerTarget;
|
||||||
if (openerContext) {
|
if (openerContext) {
|
||||||
|
|
@ -191,9 +182,14 @@ class TargetRegistry {
|
||||||
const domWindowTabListeners = new Map();
|
const domWindowTabListeners = new Map();
|
||||||
|
|
||||||
const onOpenWindow = async (appWindow) => {
|
const onOpenWindow = async (appWindow) => {
|
||||||
if (!(appWindow instanceof Ci.nsIAppWindow))
|
|
||||||
return;
|
let domWindow;
|
||||||
const domWindow = appWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
|
if (appWindow instanceof Ci.nsIAppWindow) {
|
||||||
|
domWindow = appWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
|
||||||
|
} else {
|
||||||
|
domWindow = appWindow;
|
||||||
|
appWindow = null;
|
||||||
|
}
|
||||||
if (!(domWindow instanceof Ci.nsIDOMChromeWindow))
|
if (!(domWindow instanceof Ci.nsIDOMChromeWindow))
|
||||||
return;
|
return;
|
||||||
// In persistent mode, window might be opened long ago and might be
|
// In persistent mode, window might be opened long ago and might be
|
||||||
|
|
@ -317,12 +313,11 @@ class TargetRegistry {
|
||||||
if (await target.hasFailedToOverrideTimezone())
|
if (await target.hasFailedToOverrideTimezone())
|
||||||
throw new Error('Failed to override timezone');
|
throw new Error('Failed to override timezone');
|
||||||
}
|
}
|
||||||
await target.reportedPromise();
|
|
||||||
return target.id();
|
return target.id();
|
||||||
}
|
}
|
||||||
|
|
||||||
reportedTargets() {
|
targets() {
|
||||||
return Array.from(this._browserToTarget.values()).filter(pageTarget => pageTarget._isReported);
|
return Array.from(this._browserToTarget.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
targetForBrowser(browser) {
|
targetForBrowser(browser) {
|
||||||
|
|
@ -364,24 +359,12 @@ class PageTarget {
|
||||||
helper.addProgressListener(tab.linkedBrowser, navigationListener, Ci.nsIWebProgress.NOTIFY_LOCATION),
|
helper.addProgressListener(tab.linkedBrowser, navigationListener, Ci.nsIWebProgress.NOTIFY_LOCATION),
|
||||||
];
|
];
|
||||||
|
|
||||||
this._isReported = false;
|
|
||||||
this._reportedPromise = new Promise(resolve => {
|
|
||||||
this._reportedCallback = resolve;
|
|
||||||
});
|
|
||||||
|
|
||||||
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._browserBrowsingContextToTarget.set(this._linkedBrowser.browsingContext, this);
|
||||||
}
|
|
||||||
|
|
||||||
reportedPromise() {
|
this._registry.emit(TargetRegistry.Events.TargetCreated, this);
|
||||||
return this._reportedPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
markAsReported() {
|
|
||||||
this._isReported = true;
|
|
||||||
this._reportedCallback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async windowReady() {
|
async windowReady() {
|
||||||
|
|
@ -411,7 +394,7 @@ class PageTarget {
|
||||||
// Otherwise, explicitly set page viewport prevales over browser context
|
// Otherwise, explicitly set page viewport prevales over browser context
|
||||||
// default viewport.
|
// default viewport.
|
||||||
const viewportSize = this._viewportSize || this._browserContext.defaultViewportSize;
|
const viewportSize = this._viewportSize || this._browserContext.defaultViewportSize;
|
||||||
const actualSize = setViewportSizeForBrowser(viewportSize, this._linkedBrowser, this._window);
|
const actualSize = await setViewportSizeForBrowser(viewportSize, this._linkedBrowser, this._window);
|
||||||
await this._channel.connect('').send('awaitViewportDimensions', {
|
await this._channel.connect('').send('awaitViewportDimensions', {
|
||||||
width: actualSize.width,
|
width: actualSize.width,
|
||||||
height: actualSize.height
|
height: actualSize.height
|
||||||
|
|
@ -423,30 +406,14 @@ class PageTarget {
|
||||||
await this.updateViewportSize();
|
await this.updateViewportSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
connectSession(session) {
|
|
||||||
this._channel.connect('').send('attach', { sessionId: session.sessionId() });
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectSession(session) {
|
|
||||||
if (!this._disposed)
|
|
||||||
this._channel.connect('').emit('detach', { sessionId: session.sessionId() });
|
|
||||||
}
|
|
||||||
|
|
||||||
async close(runBeforeUnload = false) {
|
async close(runBeforeUnload = false) {
|
||||||
await this._gBrowser.removeTab(this._tab, {
|
await this._gBrowser.removeTab(this._tab, {
|
||||||
skipPermitUnload: !runBeforeUnload,
|
skipPermitUnload: !runBeforeUnload,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initSession(session) {
|
channel() {
|
||||||
const pageHandler = new PageHandler(this, session, this._channel);
|
return this._channel;
|
||||||
const networkHandler = new NetworkHandler(this, session, this._channel);
|
|
||||||
session.registerHandler('Page', pageHandler);
|
|
||||||
session.registerHandler('Network', networkHandler);
|
|
||||||
session.registerHandler('Runtime', new RuntimeHandler(session, this._channel));
|
|
||||||
session.registerHandler('Accessibility', new AccessibilityHandler(session, this._channel));
|
|
||||||
pageHandler.enable();
|
|
||||||
networkHandler.enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id() {
|
id() {
|
||||||
|
|
@ -493,8 +460,7 @@ class PageTarget {
|
||||||
this._registry._browserToTarget.delete(this._linkedBrowser);
|
this._registry._browserToTarget.delete(this._linkedBrowser);
|
||||||
this._registry._browserBrowsingContextToTarget.delete(this._linkedBrowser.browsingContext);
|
this._registry._browserBrowsingContextToTarget.delete(this._linkedBrowser.browsingContext);
|
||||||
helper.removeListeners(this._eventListeners);
|
helper.removeListeners(this._eventListeners);
|
||||||
if (this._isReported)
|
this._registry.emit(TargetRegistry.Events.TargetDestroyed, this);
|
||||||
this._registry.emit(TargetRegistry.Events.TargetDestroyed, this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -733,7 +699,8 @@ async function waitForWindowReady(window) {
|
||||||
await helper.awaitEvent(window, 'load');
|
await helper.awaitEvent(window, 'load');
|
||||||
}
|
}
|
||||||
|
|
||||||
function setViewportSizeForBrowser(viewportSize, browser, window) {
|
async function setViewportSizeForBrowser(viewportSize, browser, window) {
|
||||||
|
await waitForWindowReady(window);
|
||||||
if (viewportSize) {
|
if (viewportSize) {
|
||||||
const {width, height} = viewportSize;
|
const {width, height} = viewportSize;
|
||||||
const rect = browser.getBoundingClientRect();
|
const rect = browser.getBoundingClientRect();
|
||||||
|
|
|
||||||
|
|
@ -20,32 +20,31 @@ const obs = Cc["@mozilla.org/observer-service;1"].getService(
|
||||||
const helper = new Helper();
|
const helper = new Helper();
|
||||||
|
|
||||||
class WorkerData {
|
class WorkerData {
|
||||||
constructor(pageAgent, browserChannel, sessionId, worker) {
|
constructor(pageAgent, browserChannel, worker) {
|
||||||
this._workerRuntime = worker.channel().connect(sessionId + 'runtime');
|
this._workerRuntime = worker.channel().connect('runtime');
|
||||||
this._browserWorker = browserChannel.connect(sessionId + worker.id());
|
this._browserWorker = browserChannel.connect(worker.id());
|
||||||
this._worker = worker;
|
this._worker = worker;
|
||||||
this._sessionId = sessionId;
|
|
||||||
const emit = name => {
|
const emit = name => {
|
||||||
return (...args) => this._browserWorker.emit(name, ...args);
|
return (...args) => this._browserWorker.emit(name, ...args);
|
||||||
};
|
};
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
worker.channel().register(sessionId + 'runtime', {
|
worker.channel().register('runtime', {
|
||||||
runtimeConsole: emit('runtimeConsole'),
|
runtimeConsole: emit('runtimeConsole'),
|
||||||
runtimeExecutionContextCreated: emit('runtimeExecutionContextCreated'),
|
runtimeExecutionContextCreated: emit('runtimeExecutionContextCreated'),
|
||||||
runtimeExecutionContextDestroyed: emit('runtimeExecutionContextDestroyed'),
|
runtimeExecutionContextDestroyed: emit('runtimeExecutionContextDestroyed'),
|
||||||
}),
|
}),
|
||||||
browserChannel.register(sessionId + worker.id(), {
|
browserChannel.register(worker.id(), {
|
||||||
evaluate: (options) => this._workerRuntime.send('evaluate', options),
|
evaluate: (options) => this._workerRuntime.send('evaluate', options),
|
||||||
callFunction: (options) => this._workerRuntime.send('callFunction', options),
|
callFunction: (options) => this._workerRuntime.send('callFunction', options),
|
||||||
getObjectProperties: (options) => this._workerRuntime.send('getObjectProperties', options),
|
getObjectProperties: (options) => this._workerRuntime.send('getObjectProperties', options),
|
||||||
disposeObject: (options) =>this._workerRuntime.send('disposeObject', options),
|
disposeObject: (options) =>this._workerRuntime.send('disposeObject', options),
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
worker.channel().connect('').emit('attach', {sessionId});
|
worker.channel().connect('').emit('attach');
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this._worker.channel().connect('').emit('detach', {sessionId: this._sessionId});
|
this._worker.channel().connect('').emit('detach');
|
||||||
this._workerRuntime.dispose();
|
this._workerRuntime.dispose();
|
||||||
this._browserWorker.dispose();
|
this._browserWorker.dispose();
|
||||||
helper.removeListeners(this._eventListeners);
|
helper.removeListeners(this._eventListeners);
|
||||||
|
|
@ -112,12 +111,11 @@ class FrameData {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PageAgent {
|
class PageAgent {
|
||||||
constructor(messageManager, browserChannel, sessionId, frameTree) {
|
constructor(messageManager, browserChannel, frameTree) {
|
||||||
this._messageManager = messageManager;
|
this._messageManager = messageManager;
|
||||||
this._browserChannel = browserChannel;
|
this._browserChannel = browserChannel;
|
||||||
this._sessionId = sessionId;
|
this._browserPage = browserChannel.connect('page');
|
||||||
this._browserPage = browserChannel.connect(sessionId + 'page');
|
this._browserRuntime = browserChannel.connect('runtime');
|
||||||
this._browserRuntime = browserChannel.connect(sessionId + 'runtime');
|
|
||||||
this._frameTree = frameTree;
|
this._frameTree = frameTree;
|
||||||
this._runtime = frameTree.runtime();
|
this._runtime = frameTree.runtime();
|
||||||
|
|
||||||
|
|
@ -127,7 +125,7 @@ class PageAgent {
|
||||||
this._isolatedWorlds = new Map();
|
this._isolatedWorlds = new Map();
|
||||||
|
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
browserChannel.register(sessionId + 'page', {
|
browserChannel.register('page', {
|
||||||
addBinding: ({ name, script }) => this._frameTree.addBinding(name, script),
|
addBinding: ({ name, script }) => this._frameTree.addBinding(name, script),
|
||||||
addScriptToEvaluateOnNewDocument: this._addScriptToEvaluateOnNewDocument.bind(this),
|
addScriptToEvaluateOnNewDocument: this._addScriptToEvaluateOnNewDocument.bind(this),
|
||||||
adoptNode: this._adoptNode.bind(this),
|
adoptNode: this._adoptNode.bind(this),
|
||||||
|
|
@ -152,7 +150,7 @@ class PageAgent {
|
||||||
setFileInputFiles: this._setFileInputFiles.bind(this),
|
setFileInputFiles: this._setFileInputFiles.bind(this),
|
||||||
setInterceptFileChooserDialog: this._setInterceptFileChooserDialog.bind(this),
|
setInterceptFileChooserDialog: this._setInterceptFileChooserDialog.bind(this),
|
||||||
}),
|
}),
|
||||||
browserChannel.register(sessionId + 'runtime', {
|
browserChannel.register('runtime', {
|
||||||
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),
|
||||||
|
|
@ -302,7 +300,7 @@ class PageAgent {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onWorkerCreated(worker) {
|
_onWorkerCreated(worker) {
|
||||||
const workerData = new WorkerData(this, this._browserChannel, this._sessionId, worker);
|
const workerData = new WorkerData(this, this._browserChannel, worker);
|
||||||
this._workerData.set(worker.id(), workerData);
|
this._workerData.set(worker.id(), workerData);
|
||||||
this._browserPage.emit('pageWorkerCreated', {
|
this._browserPage.emit('pageWorkerCreated', {
|
||||||
workerId: worker.id(),
|
workerId: worker.id(),
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@
|
||||||
loadSubScript('chrome://juggler/content/content/Runtime.js');
|
loadSubScript('chrome://juggler/content/content/Runtime.js');
|
||||||
loadSubScript('chrome://juggler/content/SimpleChannel.js');
|
loadSubScript('chrome://juggler/content/SimpleChannel.js');
|
||||||
|
|
||||||
const runtimeAgents = new Map();
|
|
||||||
|
|
||||||
const channel = new SimpleChannel('worker::worker');
|
const channel = new SimpleChannel('worker::worker');
|
||||||
const eventListener = event => channel._onMessage(JSON.parse(event.data));
|
const eventListener = event => channel._onMessage(JSON.parse(event.data));
|
||||||
this.addEventListener('message', eventListener);
|
this.addEventListener('message', eventListener);
|
||||||
|
|
@ -34,11 +32,11 @@ const runtime = new Runtime(true /* isWorker */);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
class RuntimeAgent {
|
class RuntimeAgent {
|
||||||
constructor(runtime, channel, sessionId) {
|
constructor(runtime, channel) {
|
||||||
this._runtime = runtime;
|
this._runtime = runtime;
|
||||||
this._browserRuntime = channel.connect(sessionId + 'runtime');
|
this._browserRuntime = channel.connect('runtime');
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
channel.register(sessionId + 'runtime', {
|
channel.register('runtime', {
|
||||||
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),
|
||||||
|
|
@ -72,15 +70,14 @@ class RuntimeAgent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let runtimeAgent;
|
||||||
|
|
||||||
channel.register('', {
|
channel.register('', {
|
||||||
attach: ({sessionId}) => {
|
attach: () => {
|
||||||
const runtimeAgent = new RuntimeAgent(runtime, channel, sessionId);
|
runtimeAgent = new RuntimeAgent(runtime, channel);
|
||||||
runtimeAgents.set(sessionId, runtimeAgent);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
detach: ({sessionId}) => {
|
detach: () => {
|
||||||
const runtimeAgent = runtimeAgents.get(sessionId);
|
|
||||||
runtimeAgents.delete(sessionId);
|
|
||||||
runtimeAgent.dispose();
|
runtimeAgent.dispose();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -12,20 +12,7 @@ let frameTree;
|
||||||
const helper = new Helper();
|
const helper = new Helper();
|
||||||
const messageManager = this;
|
const messageManager = this;
|
||||||
|
|
||||||
const sessions = new Map();
|
let pageAgent;
|
||||||
|
|
||||||
function createContentSession(channel, sessionId) {
|
|
||||||
const pageAgent = new PageAgent(messageManager, channel, sessionId, frameTree);
|
|
||||||
sessions.set(sessionId, [pageAgent]);
|
|
||||||
pageAgent.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
function disposeContentSession(sessionId) {
|
|
||||||
const handlers = sessions.get(sessionId);
|
|
||||||
sessions.delete(sessionId);
|
|
||||||
for (const handler of handlers)
|
|
||||||
handler.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
let failedToOverrideTimezone = false;
|
let failedToOverrideTimezone = false;
|
||||||
|
|
||||||
|
|
@ -89,6 +76,8 @@ const applySetting = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const channel = SimpleChannel.createForMessageManager('content::page', messageManager);
|
||||||
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
const response = sendSyncMessage('juggler:content-ready')[0];
|
const response = sendSyncMessage('juggler:content-ready')[0];
|
||||||
// If we didn't get a response, then we don't want to do anything
|
// If we didn't get a response, then we don't want to do anything
|
||||||
|
|
@ -96,7 +85,6 @@ function initialize() {
|
||||||
if (!response)
|
if (!response)
|
||||||
return;
|
return;
|
||||||
const {
|
const {
|
||||||
sessionIds = [],
|
|
||||||
scriptsToEvaluateOnNewDocument = [],
|
scriptsToEvaluateOnNewDocument = [],
|
||||||
bindings = [],
|
bindings = [],
|
||||||
settings = {}
|
settings = {}
|
||||||
|
|
@ -114,20 +102,10 @@ function initialize() {
|
||||||
for (const { name, script } of bindings)
|
for (const { name, script } of bindings)
|
||||||
frameTree.addBinding(name, script);
|
frameTree.addBinding(name, script);
|
||||||
|
|
||||||
const channel = SimpleChannel.createForMessageManager('content::page', messageManager);
|
pageAgent = new PageAgent(messageManager, channel, frameTree);
|
||||||
|
pageAgent.enable();
|
||||||
for (const sessionId of sessionIds)
|
|
||||||
createContentSession(channel, sessionId);
|
|
||||||
|
|
||||||
channel.register('', {
|
channel.register('', {
|
||||||
attach({sessionId}) {
|
|
||||||
createContentSession(channel, sessionId);
|
|
||||||
},
|
|
||||||
|
|
||||||
detach({sessionId}) {
|
|
||||||
disposeContentSession(sessionId);
|
|
||||||
},
|
|
||||||
|
|
||||||
addScriptToEvaluateOnNewDocument(script) {
|
addScriptToEvaluateOnNewDocument(script) {
|
||||||
frameTree.addScriptToEvaluateOnNewDocument(script);
|
frameTree.addScriptToEvaluateOnNewDocument(script);
|
||||||
},
|
},
|
||||||
|
|
@ -169,12 +147,9 @@ function initialize() {
|
||||||
const gListeners = [
|
const gListeners = [
|
||||||
helper.addEventListener(messageManager, 'unload', msg => {
|
helper.addEventListener(messageManager, 'unload', msg => {
|
||||||
helper.removeListeners(gListeners);
|
helper.removeListeners(gListeners);
|
||||||
channel.dispose();
|
pageAgent.dispose();
|
||||||
|
|
||||||
for (const sessionId of sessions.keys())
|
|
||||||
disposeContentSession(sessionId);
|
|
||||||
|
|
||||||
frameTree.dispose();
|
frameTree.dispose();
|
||||||
|
channel.dispose();
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
class AccessibilityHandler {
|
class AccessibilityHandler {
|
||||||
constructor(session, contentChannel) {
|
constructor(session, contentChannel) {
|
||||||
this._contentPage = contentChannel.connect(session.sessionId() + 'page');
|
this._contentPage = contentChannel.connect('page');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFullAXTree(params) {
|
async getFullAXTree(params) {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
|
const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
|
||||||
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||||
|
const {PageHandler} = ChromeUtils.import("chrome://juggler/content/protocol/PageHandler.js");
|
||||||
|
const {NetworkHandler} = ChromeUtils.import("chrome://juggler/content/protocol/NetworkHandler.js");
|
||||||
|
const {RuntimeHandler} = ChromeUtils.import("chrome://juggler/content/protocol/RuntimeHandler.js");
|
||||||
|
const {AccessibilityHandler} = ChromeUtils.import("chrome://juggler/content/protocol/AccessibilityHandler.js");
|
||||||
|
|
||||||
const helper = new Helper();
|
const helper = new Helper();
|
||||||
|
|
||||||
|
|
@ -29,19 +33,6 @@ class BrowserHandler {
|
||||||
this._enabled = true;
|
this._enabled = true;
|
||||||
this._attachToDefaultContext = attachToDefaultContext;
|
this._attachToDefaultContext = attachToDefaultContext;
|
||||||
|
|
||||||
for (const target of this._targetRegistry.reportedTargets()) {
|
|
||||||
if (!this._shouldAttachToTarget(target))
|
|
||||||
continue;
|
|
||||||
const session = this._dispatcher.createSession();
|
|
||||||
this._attachedSessions.set(target, session);
|
|
||||||
this._session.emitEvent('Browser.attachedToTarget', {
|
|
||||||
sessionId: session.sessionId(),
|
|
||||||
targetInfo: target.info()
|
|
||||||
});
|
|
||||||
target.initSession(session);
|
|
||||||
target.connectSession(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
helper.on(this._targetRegistry, TargetRegistry.Events.TargetCreated, this._onTargetCreated.bind(this)),
|
helper.on(this._targetRegistry, TargetRegistry.Events.TargetCreated, this._onTargetCreated.bind(this)),
|
||||||
helper.on(this._targetRegistry, TargetRegistry.Events.TargetDestroyed, this._onTargetDestroyed.bind(this)),
|
helper.on(this._targetRegistry, TargetRegistry.Events.TargetDestroyed, this._onTargetDestroyed.bind(this)),
|
||||||
|
|
@ -54,6 +45,9 @@ class BrowserHandler {
|
||||||
};
|
};
|
||||||
Services.obs.addObserver(onScreencastStopped, 'juggler-screencast-stopped');
|
Services.obs.addObserver(onScreencastStopped, 'juggler-screencast-stopped');
|
||||||
this._eventListeners.push(() => Services.obs.removeObserver(onScreencastStopped, 'juggler-screencast-stopped'));
|
this._eventListeners.push(() => Services.obs.removeObserver(onScreencastStopped, 'juggler-screencast-stopped'));
|
||||||
|
|
||||||
|
for (const target of this._targetRegistry.targets())
|
||||||
|
this._onTargetCreated(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createBrowserContext({removeOnDetach}) {
|
async createBrowserContext({removeOnDetach}) {
|
||||||
|
|
@ -73,10 +67,8 @@ class BrowserHandler {
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
helper.removeListeners(this._eventListeners);
|
helper.removeListeners(this._eventListeners);
|
||||||
for (const [target, session] of this._attachedSessions) {
|
for (const [target, session] of this._attachedSessions)
|
||||||
target.disconnectSession(session);
|
|
||||||
this._dispatcher.destroySession(session);
|
this._dispatcher.destroySession(session);
|
||||||
}
|
|
||||||
this._attachedSessions.clear();
|
this._attachedSessions.clear();
|
||||||
for (const browserContextId of this._createdBrowserContextIds) {
|
for (const browserContextId of this._createdBrowserContextIds) {
|
||||||
const browserContext = this._targetRegistry.browserContextForId(browserContextId);
|
const browserContext = this._targetRegistry.browserContextForId(browserContextId);
|
||||||
|
|
@ -87,24 +79,29 @@ class BrowserHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
_shouldAttachToTarget(target) {
|
_shouldAttachToTarget(target) {
|
||||||
if (!target._browserContext)
|
|
||||||
return false;
|
|
||||||
if (this._createdBrowserContextIds.has(target._browserContext.browserContextId))
|
if (this._createdBrowserContextIds.has(target._browserContext.browserContextId))
|
||||||
return true;
|
return true;
|
||||||
return this._attachToDefaultContext && target._browserContext === this._targetRegistry.defaultContext();
|
return this._attachToDefaultContext && target._browserContext === this._targetRegistry.defaultContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onTargetCreated({sessions, target}) {
|
_onTargetCreated(target) {
|
||||||
if (!this._shouldAttachToTarget(target))
|
if (!this._shouldAttachToTarget(target))
|
||||||
return;
|
return;
|
||||||
|
const channel = target.channel();
|
||||||
const session = this._dispatcher.createSession();
|
const session = this._dispatcher.createSession();
|
||||||
this._attachedSessions.set(target, session);
|
this._attachedSessions.set(target, session);
|
||||||
|
const pageHandler = new PageHandler(target, session, channel);
|
||||||
|
const networkHandler = new NetworkHandler(target, session, channel);
|
||||||
|
session.registerHandler('Page', pageHandler);
|
||||||
|
session.registerHandler('Network', networkHandler);
|
||||||
|
session.registerHandler('Runtime', new RuntimeHandler(session, channel));
|
||||||
|
session.registerHandler('Accessibility', new AccessibilityHandler(session, channel));
|
||||||
|
pageHandler.enable();
|
||||||
|
networkHandler.enable();
|
||||||
this._session.emitEvent('Browser.attachedToTarget', {
|
this._session.emitEvent('Browser.attachedToTarget', {
|
||||||
sessionId: session.sessionId(),
|
sessionId: session.sessionId(),
|
||||||
targetInfo: target.info()
|
targetInfo: target.info()
|
||||||
});
|
});
|
||||||
target.initSession(session);
|
|
||||||
sessions.push(session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onTargetDestroyed(target) {
|
_onTargetDestroyed(target) {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ const helper = new Helper();
|
||||||
class WorkerHandler {
|
class WorkerHandler {
|
||||||
constructor(session, contentChannel, workerId) {
|
constructor(session, contentChannel, workerId) {
|
||||||
this._session = session;
|
this._session = session;
|
||||||
this._contentWorker = contentChannel.connect(session.sessionId() + workerId);
|
this._contentWorker = contentChannel.connect(workerId);
|
||||||
this._workerId = workerId;
|
this._workerId = workerId;
|
||||||
|
|
||||||
const emitWrappedProtocolEvent = eventName => {
|
const emitWrappedProtocolEvent = eventName => {
|
||||||
|
|
@ -30,7 +30,7 @@ class WorkerHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
contentChannel.register(session.sessionId() + workerId, {
|
contentChannel.register(workerId, {
|
||||||
runtimeConsole: emitWrappedProtocolEvent('Runtime.console'),
|
runtimeConsole: emitWrappedProtocolEvent('Runtime.console'),
|
||||||
runtimeExecutionContextCreated: emitWrappedProtocolEvent('Runtime.executionContextCreated'),
|
runtimeExecutionContextCreated: emitWrappedProtocolEvent('Runtime.executionContextCreated'),
|
||||||
runtimeExecutionContextDestroyed: emitWrappedProtocolEvent('Runtime.executionContextDestroyed'),
|
runtimeExecutionContextDestroyed: emitWrappedProtocolEvent('Runtime.executionContextDestroyed'),
|
||||||
|
|
@ -59,7 +59,7 @@ class PageHandler {
|
||||||
constructor(target, session, contentChannel) {
|
constructor(target, session, contentChannel) {
|
||||||
this._session = session;
|
this._session = session;
|
||||||
this._contentChannel = contentChannel;
|
this._contentChannel = contentChannel;
|
||||||
this._contentPage = contentChannel.connect(session.sessionId() + 'page');
|
this._contentPage = contentChannel.connect('page');
|
||||||
this._workers = new Map();
|
this._workers = new Map();
|
||||||
|
|
||||||
const emitProtocolEvent = eventName => {
|
const emitProtocolEvent = eventName => {
|
||||||
|
|
@ -67,7 +67,7 @@ class PageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
contentChannel.register(session.sessionId() + 'page', {
|
contentChannel.register('page', {
|
||||||
pageBindingCalled: emitProtocolEvent('Page.bindingCalled'),
|
pageBindingCalled: emitProtocolEvent('Page.bindingCalled'),
|
||||||
pageDispatchMessageFromWorker: emitProtocolEvent('Page.dispatchMessageFromWorker'),
|
pageDispatchMessageFromWorker: emitProtocolEvent('Page.dispatchMessageFromWorker'),
|
||||||
pageEventFired: emitProtocolEvent('Page.eventFired'),
|
pageEventFired: emitProtocolEvent('Page.eventFired'),
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,14 @@ const helper = new Helper();
|
||||||
|
|
||||||
class RuntimeHandler {
|
class RuntimeHandler {
|
||||||
constructor(session, contentChannel) {
|
constructor(session, contentChannel) {
|
||||||
const sessionId = session.sessionId();
|
this._contentRuntime = contentChannel.connect('runtime');
|
||||||
this._contentRuntime = contentChannel.connect(sessionId + 'runtime');
|
|
||||||
|
|
||||||
const emitProtocolEvent = eventName => {
|
const emitProtocolEvent = eventName => {
|
||||||
return (...args) => session.emitEvent(eventName, ...args);
|
return (...args) => session.emitEvent(eventName, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
contentChannel.register(sessionId + 'runtime', {
|
contentChannel.register('runtime', {
|
||||||
runtimeConsole: emitProtocolEvent('Runtime.console'),
|
runtimeConsole: emitProtocolEvent('Runtime.console'),
|
||||||
runtimeExecutionContextCreated: emitProtocolEvent('Runtime.executionContextCreated'),
|
runtimeExecutionContextCreated: emitProtocolEvent('Runtime.executionContextCreated'),
|
||||||
runtimeExecutionContextDestroyed: emitProtocolEvent('Runtime.executionContextDestroyed'),
|
runtimeExecutionContextDestroyed: emitProtocolEvent('Runtime.executionContextDestroyed'),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue