Skip to content

Commit

Permalink
browser(firefox): implement Browser.addBinding
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelfeldman committed Mar 23, 2020
1 parent d0027a0 commit a54d15a
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 31 deletions.
6 changes: 6 additions & 0 deletions juggler/BrowserContextManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class BrowserContext {
this._manager._userContextIdToBrowserContext.set(this.userContextId, this);
this.options = options || {};
this.options.scriptsToEvaluateOnNewDocument = [];
this.options.bindings = [];
this.pages = new Set();
}

Expand All @@ -100,6 +101,11 @@ class BrowserContext {
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
4 changes: 4 additions & 0 deletions juggler/TargetRegistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ class PageTarget {
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
35 changes: 35 additions & 0 deletions juggler/content/FrameTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class FrameTree {
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 @@ -48,6 +49,7 @@ class FrameTree {
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 @@ -111,6 +113,25 @@ class FrameTree {
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;
}
Expand Down Expand Up @@ -218,6 +239,8 @@ class FrameTree {
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 @@ -228,6 +251,16 @@ class FrameTree {
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 @@ -242,8 +275,10 @@ class FrameTree {
}

FrameTree.Events = {
BindingCalled: 'bindingcalled',
FrameAttached: 'frameattached',
FrameDetached: 'framedetached',
GlobalObjectCreated: 'globalobjectcreated',
WorkerCreated: 'workercreated',
WorkerDestroyed: 'workerdestroyed',
NavigationStarted: 'navigationstarted',
Expand Down
43 changes: 13 additions & 30 deletions juggler/content/PageAgent.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ class FrameData {
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 @@ -86,18 +84,6 @@ class FrameData {
}
}

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 @@ -144,11 +130,10 @@ class PageAgent {
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 @@ -270,13 +255,14 @@ class PageAgent {
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 @@ -434,11 +420,7 @@ class PageAgent {
});
}

_onDOMWindowCreated(window) {
const docShell = window.docShell;
const frame = this._frameTree.frameForDocShell(docShell);
if (!frame)
return;
_onGlobalObjectCreated({ frame }) {
this._frameData.get(frame).reset();
}

Expand All @@ -457,6 +439,15 @@ class PageAgent {
});
}

_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 @@ -524,14 +515,6 @@ class PageAgent {
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
8 changes: 7 additions & 1 deletion juggler/content/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function initialize() {
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 @@ -97,6 +97,8 @@ function initialize() {
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 @@ -117,6 +119,10 @@ function initialize() {
frameTree.addScriptToEvaluateOnNewDocument(script);
},

addBinding(name, script) {
frameTree.addBinding(name, script);
},

setGeolocationOverride(geolocation) {
setGeolocationOverrideInDocShell(geolocation);
},
Expand Down
4 changes: 4 additions & 0 deletions juggler/protocol/BrowserHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ class BrowserHandler {
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
8 changes: 8 additions & 0 deletions juggler/protocol/Protocol.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,13 @@ const Browser = {
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 @@ -559,6 +566,7 @@ const Page = {
'addBinding': {
params: {
name: t.String,
script: t.String,
},
},
'setViewportSize': {
Expand Down

0 comments on commit a54d15a

Please sign in to comment.