browser(firefox): implement Browser.addBinding (#1477)

This commit is contained in:
Pavel Feldman 2020-03-22 21:10:00 -07:00 коммит произвёл GitHub
Родитель c68cee9fb7
Коммит 049fdf708c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 92 добавлений и 46 удалений

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

@ -1 +1 @@
1049
1050

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

@ -718,10 +718,10 @@ index 5de630a1db847a09651b310928bb7bc4d4f66f29..0268bc2bdfb3bfda2ef6e01a5dd24209
nsCOMPtr<nsIPrincipal> principal =
diff --git a/juggler/BrowserContextManager.js b/juggler/BrowserContextManager.js
new file mode 100644
index 0000000000000000000000000000000000000000..3365aa618718308ffdf05d9f196d792fc7b58677
index 0000000000000000000000000000000000000000..bd57d338c279f5ab31102e6644f43e133b7f4e25
--- /dev/null
+++ b/juggler/BrowserContextManager.js
@@ -0,0 +1,229 @@
@@ -0,0 +1,235 @@
+"use strict";
+
+const {ContextualIdentityService} = ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm");
@ -807,6 +807,7 @@ index 0000000000000000000000000000000000000000..3365aa618718308ffdf05d9f196d792f
+ this._manager._userContextIdToBrowserContext.set(this.userContextId, this);
+ this.options = options || {};
+ this.options.scriptsToEvaluateOnNewDocument = [];
+ this.options.bindings = [];
+ this.pages = new Set();
+ }
+
@ -824,6 +825,11 @@ index 0000000000000000000000000000000000000000..3365aa618718308ffdf05d9f196d792f
+ await Promise.all(Array.from(this.pages).map(page => page.addScriptToEvaluateOnNewDocument(script)));
+ }
+
+ async addBinding(name, script) {
+ this.options.bindings.push({ name, script });
+ await Promise.all(Array.from(this.pages).map(page => page.addBinding(name, script)));
+ }
+
+ async setGeolocationOverride(geolocation) {
+ this.options.geolocation = geolocation;
+ await Promise.all(Array.from(this.pages).map(page => page.setGeolocationOverride(geolocation)));
@ -1929,10 +1935,10 @@ index 0000000000000000000000000000000000000000..ba34976ad05e7f5f1a99777f76ac08b1
+this.SimpleChannel = SimpleChannel;
diff --git a/juggler/TargetRegistry.js b/juggler/TargetRegistry.js
new file mode 100644
index 0000000000000000000000000000000000000000..75ea79a8fa493f0d8f2f88244aaf397af17833d4
index 0000000000000000000000000000000000000000..e624e3c21a20dd324e0d135598e2a2402c8b62bf
--- /dev/null
+++ b/juggler/TargetRegistry.js
@@ -0,0 +1,273 @@
@@ -0,0 +1,277 @@
+const {EventEmitter} = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
+const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
@ -2169,6 +2175,10 @@ index 0000000000000000000000000000000000000000..75ea79a8fa493f0d8f2f88244aaf397a
+ await this._channel.connect('').send('addScriptToEvaluateOnNewDocument', script).catch(e => void e);
+ }
+
+ async addBinding(name, script) {
+ await this._channel.connect('').send('addBinding', { name, script }).catch(e => void e);
+ }
+
+ async setGeolocationOverride(geolocation) {
+ await this._channel.connect('').send('setGeolocationOverride', geolocation).catch(e => void e);
+ }
@ -2354,10 +2364,10 @@ index 0000000000000000000000000000000000000000..268fbc361d8053182bb6c27f626e853d
+
diff --git a/juggler/content/FrameTree.js b/juggler/content/FrameTree.js
new file mode 100644
index 0000000000000000000000000000000000000000..13c3cd817b369ed012326b97d03ba6e123b84740
index 0000000000000000000000000000000000000000..679b5851c427064c636bd1f7793358cc45b0de67
--- /dev/null
+++ b/juggler/content/FrameTree.js
@@ -0,0 +1,376 @@
@@ -0,0 +1,411 @@
+"use strict";
+const Ci = Components.interfaces;
+const Cr = Components.results;
@ -2378,6 +2388,7 @@ index 0000000000000000000000000000000000000000..13c3cd817b369ed012326b97d03ba6e1
+ this._browsingContextGroup.__jugglerFrameTrees = new Set();
+ this._browsingContextGroup.__jugglerFrameTrees.add(this);
+
+ this._bindings = new Map();
+ this._workers = new Map();
+ this._docShellToFrame = new Map();
+ this._frameIdToFrame = new Map();
@ -2408,6 +2419,7 @@ index 0000000000000000000000000000000000000000..13c3cd817b369ed012326b97d03ba6e1
+ this._eventListeners = [
+ helper.addObserver(subject => this._onDocShellCreated(subject.QueryInterface(Ci.nsIDocShell)), 'webnavigation-create'),
+ helper.addObserver(subject => this._onDocShellDestroyed(subject.QueryInterface(Ci.nsIDocShell)), 'webnavigation-destroy'),
+ helper.addObserver(window => this._onDOMWindowCreated(window), 'content-document-global-created'),
+ helper.addProgressListener(webProgress, this, flags),
+ ];
+ }
@ -2471,6 +2483,25 @@ index 0000000000000000000000000000000000000000..13c3cd817b369ed012326b97d03ba6e1
+ return this._scriptsToEvaluateOnNewDocument;
+ }
+
+ addBinding(name, script) {
+ this._bindings.set(name, script);
+ for (const frame of this.frames())
+ this._addBindingToFrame(frame, name, script);
+ }
+
+ _addBindingToFrame(frame, name, script) {
+ Cu.exportFunction((...args) => {
+ this.emit(FrameTree.Events.BindingCalled, {
+ frame,
+ name,
+ payload: args[0]
+ });
+ }, frame.domWindow(), {
+ defineAs: name,
+ });
+ frame.domWindow().eval(script);
+ }
+
+ frameForDocShell(docShell) {
+ return this._docShellToFrame.get(docShell) || null;
+ }
@ -2578,6 +2609,8 @@ index 0000000000000000000000000000000000000000..13c3cd817b369ed012326b97d03ba6e1
+ const frame = new Frame(this, docShell, parentFrame);
+ this._docShellToFrame.set(docShell, frame);
+ this._frameIdToFrame.set(frame.id(), frame);
+ for (const [name, script] of this._bindings)
+ this._addBindingToFrame(frame, name, script);
+ this.emit(FrameTree.Events.FrameAttached, frame);
+ return frame;
+ }
@ -2588,6 +2621,16 @@ index 0000000000000000000000000000000000000000..13c3cd817b369ed012326b97d03ba6e1
+ this._detachFrame(frame);
+ }
+
+ _onDOMWindowCreated(window) {
+ const docShell = window.docShell;
+ const frame = this.frameForDocShell(docShell);
+ if (!frame)
+ return;
+ for (const [name, script] of this._bindings)
+ this._addBindingToFrame(frame, name, script);
+ this.emit(FrameTree.Events.GlobalObjectCreated, { frame, window });
+ }
+
+ _detachFrame(frame) {
+ // Detach all children first
+ for (const subframe of frame._children)
@ -2602,8 +2645,10 @@ index 0000000000000000000000000000000000000000..13c3cd817b369ed012326b97d03ba6e1
+}
+
+FrameTree.Events = {
+ BindingCalled: 'bindingcalled',
+ FrameAttached: 'frameattached',
+ FrameDetached: 'framedetached',
+ GlobalObjectCreated: 'globalobjectcreated',
+ WorkerCreated: 'workercreated',
+ WorkerDestroyed: 'workerdestroyed',
+ NavigationStarted: 'navigationstarted',
@ -2804,10 +2849,10 @@ index 0000000000000000000000000000000000000000..be70ea364f9534bb3b344f64970366c3
+
diff --git a/juggler/content/PageAgent.js b/juggler/content/PageAgent.js
new file mode 100644
index 0000000000000000000000000000000000000000..3dd06f27d071a1ca70609d63a12b1ff690d99371
index 0000000000000000000000000000000000000000..6a001d9f51c819edd3981e090172ac87d6f85840
--- /dev/null
+++ b/juggler/content/PageAgent.js
@@ -0,0 +1,938 @@
@@ -0,0 +1,921 @@
+"use strict";
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const Ci = Components.interfaces;
@ -2873,8 +2918,6 @@ index 0000000000000000000000000000000000000000..3dd06f27d071a1ca70609d63a12b1ff6
+ name: '',
+ });
+
+ for (const bindingName of this._agent._bindingsToAdd.values())
+ this.exposeFunction(bindingName);
+ for (const script of this._agent._frameTree.scriptsToEvaluateOnNewDocument()) {
+ // TODO: this should actually be handled in FrameTree, but first we have to move
+ // execution contexts there.
@ -2896,18 +2939,6 @@ index 0000000000000000000000000000000000000000..3dd06f27d071a1ca70609d63a12b1ff6
+ }
+ }
+
+ exposeFunction(name) {
+ Cu.exportFunction((...args) => {
+ this._agent._browserPage.emit('pageBindingCalled', {
+ executionContextId: this.mainContext.id(),
+ name,
+ payload: args[0]
+ });
+ }, this._frame.domWindow(), {
+ defineAs: name,
+ });
+ }
+
+ createIsolatedWorld(name) {
+ const principal = [this._frame.domWindow()]; // extended principal
+ const sandbox = Cu.Sandbox(principal, {
@ -2954,11 +2985,10 @@ index 0000000000000000000000000000000000000000..3dd06f27d071a1ca70609d63a12b1ff6
+ this._frameData = new Map();
+ this._workerData = new Map();
+ this._scriptsToEvaluateOnNewDocument = new Map();
+ this._bindingsToAdd = new Set();
+
+ this._eventListeners = [
+ browserChannel.register(sessionId + 'page', {
+ addBinding: this._addBinding.bind(this),
+ addBinding: ({ name, script }) => this._frameTree.addBinding(name, script),
+ addScriptToEvaluateOnNewDocument: this._addScriptToEvaluateOnNewDocument.bind(this),
+ adoptNode: this._adoptNode.bind(this),
+ awaitViewportDimensions: this._awaitViewportDimensions.bind(this),
@ -3080,13 +3110,14 @@ index 0000000000000000000000000000000000000000..3dd06f27d071a1ca70609d63a12b1ff6
+ helper.addObserver(this._linkClicked.bind(this, false), 'juggler-link-click'),
+ helper.addObserver(this._linkClicked.bind(this, true), 'juggler-link-click-sync'),
+ helper.addObserver(this._filePickerShown.bind(this), 'juggler-file-picker-shown'),
+ helper.addObserver(this._onDOMWindowCreated.bind(this), 'content-document-global-created'),
+ helper.addEventListener(this._messageManager, 'DOMContentLoaded', this._onDOMContentLoaded.bind(this)),
+ helper.addEventListener(this._messageManager, 'pageshow', this._onLoad.bind(this)),
+ helper.addObserver(this._onDocumentOpenLoad.bind(this), 'juggler-document-open-loaded'),
+ helper.addEventListener(this._messageManager, 'error', this._onError.bind(this)),
+ helper.on(this._frameTree, 'bindingcalled', this._onBindingCalled.bind(this)),
+ helper.on(this._frameTree, 'frameattached', this._onFrameAttached.bind(this)),
+ helper.on(this._frameTree, 'framedetached', this._onFrameDetached.bind(this)),
+ helper.on(this._frameTree, 'globalobjectcreated', this._onGlobalObjectCreated.bind(this)),
+ helper.on(this._frameTree, 'navigationstarted', this._onNavigationStarted.bind(this)),
+ helper.on(this._frameTree, 'navigationcommitted', this._onNavigationCommitted.bind(this)),
+ helper.on(this._frameTree, 'navigationaborted', this._onNavigationAborted.bind(this)),
@ -3244,11 +3275,7 @@ index 0000000000000000000000000000000000000000..3dd06f27d071a1ca70609d63a12b1ff6
+ });
+ }
+
+ _onDOMWindowCreated(window) {
+ const docShell = window.docShell;
+ const frame = this._frameTree.frameForDocShell(docShell);
+ if (!frame)
+ return;
+ _onGlobalObjectCreated({ frame }) {
+ this._frameData.get(frame).reset();
+ }
+
@ -3267,6 +3294,15 @@ index 0000000000000000000000000000000000000000..3dd06f27d071a1ca70609d63a12b1ff6
+ });
+ }
+
+ _onBindingCalled({frame, name, payload}) {
+ const frameData = this._frameData.get(frame);
+ this._browserPage.emit('pageBindingCalled', {
+ executionContextId: frameData.mainContext.id(),
+ name,
+ payload
+ });
+ }
+
+ dispose() {
+ for (const workerData of this._workerData.values())
+ workerData.dispose();
@ -3334,14 +3370,6 @@ index 0000000000000000000000000000000000000000..3dd06f27d071a1ca70609d63a12b1ff6
+ return {navigationId: frame.pendingNavigationId(), navigationURL: frame.pendingNavigationURL()};
+ }
+
+ _addBinding({name}) {
+ if (this._bindingsToAdd.has(name))
+ throw new Error(`Binding with name ${name} already exists`);
+ this._bindingsToAdd.add(name);
+ for (const frameData of this._frameData.values())
+ frameData.exposeFunction(name);
+ }
+
+ async _adoptNode({frameId, objectId, executionContextId}) {
+ const frame = this._frameTree.frame(frameId);
+ if (!frame)
@ -4511,10 +4539,10 @@ index 0000000000000000000000000000000000000000..3a386425d3796d0a6786dea193b3402d
+
diff --git a/juggler/content/main.js b/juggler/content/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..56472e8515cb84d66bd0ec89e5ca1984bb360f5a
index 0000000000000000000000000000000000000000..9bb5c2bff8eb3e350203b56a3445e9b200747f8b
--- /dev/null
+++ b/juggler/content/main.js
@@ -0,0 +1,172 @@
@@ -0,0 +1,178 @@
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
+const {FrameTree} = ChromeUtils.import('chrome://juggler/content/content/FrameTree.js');
@ -4590,7 +4618,7 @@ index 0000000000000000000000000000000000000000..56472e8515cb84d66bd0ec89e5ca1984
+ response = { sessionIds: [], browserContextOptions: {}, waitForInitialNavigation: false };
+
+ const { sessionIds, browserContextOptions, waitForInitialNavigation } = response;
+ const { userAgent, bypassCSP, javaScriptDisabled, viewport, scriptsToEvaluateOnNewDocument, locale, geolocation, onlineOverride } = browserContextOptions;
+ const { userAgent, bypassCSP, javaScriptDisabled, viewport, scriptsToEvaluateOnNewDocument, bindings, locale, geolocation, onlineOverride } = browserContextOptions;
+
+ if (userAgent !== undefined)
+ docShell.customUserAgent = userAgent;
@ -4614,6 +4642,8 @@ index 0000000000000000000000000000000000000000..56472e8515cb84d66bd0ec89e5ca1984
+ frameTree = new FrameTree(docShell, waitForInitialNavigation);
+ for (const script of scriptsToEvaluateOnNewDocument || [])
+ frameTree.addScriptToEvaluateOnNewDocument(script);
+ for (const { name, script } of bindings || [])
+ frameTree.addBinding(name, script);
+ networkMonitor = new NetworkMonitor(docShell, frameTree);
+
+ const channel = SimpleChannel.createForMessageManager('content::page', messageManager);
@ -4634,6 +4664,10 @@ index 0000000000000000000000000000000000000000..56472e8515cb84d66bd0ec89e5ca1984
+ frameTree.addScriptToEvaluateOnNewDocument(script);
+ },
+
+ addBinding(name, script) {
+ frameTree.addBinding(name, script);
+ },
+
+ setGeolocationOverride(geolocation) {
+ setGeolocationOverrideInDocShell(geolocation);
+ },
@ -4768,10 +4802,10 @@ index 0000000000000000000000000000000000000000..2f2b7ca247f6b6dff396fb4b644654de
+this.AccessibilityHandler = AccessibilityHandler;
diff --git a/juggler/protocol/BrowserHandler.js b/juggler/protocol/BrowserHandler.js
new file mode 100644
index 0000000000000000000000000000000000000000..b3a7b47765d69f9ba48c74e23aaae4e2a40498e5
index 0000000000000000000000000000000000000000..e225fc81c62bbfac4d071ab1a9d83a754dda46bb
--- /dev/null
+++ b/juggler/protocol/BrowserHandler.js
@@ -0,0 +1,174 @@
@@ -0,0 +1,178 @@
+"use strict";
+
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
@ -4920,6 +4954,10 @@ index 0000000000000000000000000000000000000000..b3a7b47765d69f9ba48c74e23aaae4e2
+ await this._contextManager.browserContextForId(browserContextId).addScriptToEvaluateOnNewDocument(script);
+ }
+
+ async addBinding({browserContextId, name, script}) {
+ await this._contextManager.browserContextForId(browserContextId).addBinding(name, script);
+ }
+
+ setCookies({browserContextId, cookies}) {
+ this._contextManager.browserContextForId(browserContextId).setCookies(cookies);
+ }
@ -5817,10 +5855,10 @@ index 0000000000000000000000000000000000000000..78b6601b91d0b7fcda61114e6846aa07
+this.EXPORTED_SYMBOLS = ['t', 'checkScheme'];
diff --git a/juggler/protocol/Protocol.js b/juggler/protocol/Protocol.js
new file mode 100644
index 0000000000000000000000000000000000000000..390e68d71c0034748ae90132d9d1defaa67de772
index 0000000000000000000000000000000000000000..67df4d5592d66e0db3c7c120ad12f9b360b9c45d
--- /dev/null
+++ b/juggler/protocol/Protocol.js
@@ -0,0 +1,770 @@
@@ -0,0 +1,778 @@
+const {t, checkScheme} = ChromeUtils.import('chrome://juggler/content/protocol/PrimitiveTypes.js');
+
+// Protocol-specific types.
@ -6096,6 +6134,13 @@ index 0000000000000000000000000000000000000000..390e68d71c0034748ae90132d9d1defa
+ script: t.String,
+ }
+ },
+ 'addBinding': {
+ params: {
+ browserContextId: t.Optional(t.String),
+ name: t.String,
+ script: t.String,
+ },
+ },
+ 'grantPermissions': {
+ params: {
+ origin: t.String,
@ -6382,6 +6427,7 @@ index 0000000000000000000000000000000000000000..390e68d71c0034748ae90132d9d1defa
+ 'addBinding': {
+ params: {
+ name: t.String,
+ script: t.String,
+ },
+ },
+ 'setViewportSize': {