Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

browser(firefox): implement Browser.addBinding #1477

Merged
merged 1 commit into from
Mar 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion browser_patches/firefox/BUILD_NUMBER
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1049
1050
136 changes: 91 additions & 45 deletions browser_patches/firefox/patches/bootstrap.diff
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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();
+ }
+
Expand All @@ -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)));
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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);
+ }
Expand Down Expand Up @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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),
+ ];
+ }
Expand Down Expand Up @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use mainContext.evaluate instead to circumvent CSP? Similar to scripts to evaluate on load.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't get ahold of it from there!

+ }
+
+ frameForDocShell(docShell) {
+ return this._docShellToFrame.get(docShell) || null;
+ }
Expand Down Expand Up @@ -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;
+ }
Expand All @@ -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)
Expand All @@ -2602,8 +2645,10 @@ index 0000000000000000000000000000000000000000..13c3cd817b369ed012326b97d03ba6e1
+}
+
+FrameTree.Events = {
+ BindingCalled: 'bindingcalled',
+ FrameAttached: 'frameattached',
+ FrameDetached: 'framedetached',
+ GlobalObjectCreated: 'globalobjectcreated',
+ WorkerCreated: 'workercreated',
+ WorkerDestroyed: 'workerdestroyed',
+ NavigationStarted: 'navigationstarted',
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -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, {
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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)),
Expand Down Expand Up @@ -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();
+ }
+
Expand All @@ -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();
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -4634,6 +4664,10 @@ index 0000000000000000000000000000000000000000..56472e8515cb84d66bd0ec89e5ca1984
+ frameTree.addScriptToEvaluateOnNewDocument(script);
+ },
+
+ addBinding(name, script) {
+ frameTree.addBinding(name, script);
+ },
+
+ setGeolocationOverride(geolocation) {
+ setGeolocationOverrideInDocShell(geolocation);
+ },
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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);
+ }
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -6382,6 +6427,7 @@ index 0000000000000000000000000000000000000000..390e68d71c0034748ae90132d9d1defa
+ 'addBinding': {
+ params: {
+ name: t.String,
+ script: t.String,
+ },
+ },
+ 'setViewportSize': {
Expand Down