browser(firefox): bindings in isolated worlds (#6493)

This commit is contained in:
Yury Semikhatsky 2021-05-11 16:27:39 +00:00 committed by GitHub
parent d243ae7ede
commit f8039bed10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 76 additions and 41 deletions

View File

@ -1,2 +1,2 @@
1258 1259
Changed: pavel.feldman@gmail.com Mon 10 May 2021 09:56:40 PM PDT Changed: yurys@chromium.org Tue 11 May 2021 09:09:59 AM PDT

View File

@ -479,8 +479,8 @@ class PageTarget {
await this._channel.connect('').send('addScriptToEvaluateOnNewDocument', script).catch(e => void e); await this._channel.connect('').send('addScriptToEvaluateOnNewDocument', script).catch(e => void e);
} }
async addBinding(name, script) { async addBinding(worldName, name, script) {
await this._channel.connect('').send('addBinding', { name, script }).catch(e => void e); await this._channel.connect('').send('addBinding', { worldName, name, script }).catch(e => void e);
} }
async applyContextSetting(name, value) { async applyContextSetting(name, value) {
@ -717,9 +717,9 @@ class BrowserContext {
await Promise.all(Array.from(this.pages).map(page => page.addScriptToEvaluateOnNewDocument(script))); await Promise.all(Array.from(this.pages).map(page => page.addScriptToEvaluateOnNewDocument(script)));
} }
async addBinding(name, script) { async addBinding(worldName, name, script) {
this.bindings.push({ name, script }); this.bindings.push({ worldName, name, script });
await Promise.all(Array.from(this.pages).map(page => page.addBinding(name, script))); await Promise.all(Array.from(this.pages).map(page => page.addBinding(worldName, name, script)));
} }
async applySetting(name, value) { async applySetting(name, value) {

View File

@ -148,10 +148,10 @@ class FrameTree {
return true; return true;
} }
addBinding(name, script) { addBinding(worldName, name, script) {
this._bindings.set(name, script); this._bindings.set(worldName + ':' + name, {worldName, name, script});
for (const frame of this.frames()) for (const frame of this.frames())
frame._addBinding(name, script); frame._addBinding(worldName, name, script);
} }
frameForDocShell(docShell) { frameForDocShell(docShell) {
@ -297,7 +297,6 @@ class FrameTree {
} }
FrameTree.Events = { FrameTree.Events = {
BindingCalled: 'bindingcalled',
FrameAttached: 'frameattached', FrameAttached: 'frameattached',
FrameDetached: 'framedetached', FrameDetached: 'framedetached',
WorkerCreated: 'workercreated', WorkerCreated: 'workercreated',
@ -461,18 +460,18 @@ class Frame {
this._executionContext = null; this._executionContext = null;
} }
_addBinding(name, script) { _getOrCreateIsolatedContext(worldName) {
Cu.exportFunction((...args) => { for (let context of this._isolatedWorlds.values()) {
this._frameTree.emit(FrameTree.Events.BindingCalled, { if (context.auxData().name === worldName)
frame: this, return context;
name, }
payload: args[0] return this.createIsolatedWorld(worldName);
}); }
}, this.domWindow(), {
defineAs: name, _addBinding(worldName, name, script) {
}); const executionContext = worldName ? this._getOrCreateIsolatedContext(worldName) : this._executionContext;
if (this._executionContext) if (executionContext)
this._evaluateScriptSafely(this._executionContext, script); executionContext.addBinding(name, script);
} }
_evaluateScriptSafely(executionContext, script) { _evaluateScriptSafely(executionContext, script) {
@ -494,20 +493,25 @@ class Frame {
if (this._executionContext) if (this._executionContext)
this._runtime.destroyExecutionContext(this._executionContext); this._runtime.destroyExecutionContext(this._executionContext);
for (const world of this._isolatedWorlds.values())
this._runtime.destroyExecutionContext(world);
this._isolatedWorlds.clear();
this._executionContext = this._runtime.createExecutionContext(this.domWindow(), this.domWindow(), { this._executionContext = this._runtime.createExecutionContext(this.domWindow(), this.domWindow(), {
frameId: this._frameId, frameId: this._frameId,
name: '', name: '',
}); });
for (const [name, script] of this._frameTree._bindings) for (const {script, worldName} of this._frameTree._isolatedWorlds.values())
this._addBinding(name, script); this.createIsolatedWorld(worldName);
// Add bindings before evaluating scripts.
for (const [id, {worldName, name, script}] of this._frameTree._bindings)
this._addBinding(worldName, name, script);
for (const script of this._frameTree._scriptsToEvaluateOnNewDocument.values()) for (const script of this._frameTree._scriptsToEvaluateOnNewDocument.values())
this._evaluateScriptSafely(this._executionContext, script); this._evaluateScriptSafely(this._executionContext, script);
for (const world of this._isolatedWorlds.values())
this._runtime.destroyExecutionContext(world);
this._isolatedWorlds.clear();
for (const {script, worldName} of this._frameTree._isolatedWorlds.values()) { for (const {script, worldName} of this._frameTree._isolatedWorlds.values()) {
const context = worldName ? this.createIsolatedWorld(worldName) : this.executionContext(); const context = worldName ? this._getOrCreateIsolatedContext(worldName) : this.executionContext();
this._evaluateScriptSafely(context, script); this._evaluateScriptSafely(context, script);
} }
} }

View File

@ -104,7 +104,6 @@ class PageAgent {
helper.addObserver(this._onDocumentOpenLoad.bind(this), 'juggler-document-open-loaded'), helper.addObserver(this._onDocumentOpenLoad.bind(this), 'juggler-document-open-loaded'),
helper.addEventListener(this._messageManager, 'error', this._onError.bind(this)), helper.addEventListener(this._messageManager, 'error', this._onError.bind(this)),
helper.on(this._frameTree, 'load', this._onLoad.bind(this)), helper.on(this._frameTree, 'load', this._onLoad.bind(this)),
helper.on(this._frameTree, 'bindingcalled', this._onBindingCalled.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,8 +132,9 @@ class PageAgent {
this._runtime.events.onConsoleMessage(msg => this._browserPage.emit('runtimeConsole', msg)), this._runtime.events.onConsoleMessage(msg => this._browserPage.emit('runtimeConsole', msg)),
this._runtime.events.onExecutionContextCreated(this._onExecutionContextCreated.bind(this)), this._runtime.events.onExecutionContextCreated(this._onExecutionContextCreated.bind(this)),
this._runtime.events.onExecutionContextDestroyed(this._onExecutionContextDestroyed.bind(this)), this._runtime.events.onExecutionContextDestroyed(this._onExecutionContextDestroyed.bind(this)),
this._runtime.events.onBindingCalled(this._onBindingCalled.bind(this)),
browserChannel.register('page', { browserChannel.register('page', {
addBinding: ({ name, script }) => this._frameTree.addBinding(name, script), addBinding: ({ worldName, name, script }) => this._frameTree.addBinding(worldName, name, script),
addScriptToEvaluateOnNewDocument: ({script, worldName}) => this._frameTree.addScriptToEvaluateOnNewDocument(script, worldName), addScriptToEvaluateOnNewDocument: ({script, worldName}) => this._frameTree.addScriptToEvaluateOnNewDocument(script, worldName),
adoptNode: this._adoptNode.bind(this), adoptNode: this._adoptNode.bind(this),
crash: this._crash.bind(this), crash: this._crash.bind(this),
@ -355,9 +355,9 @@ class PageAgent {
}); });
} }
_onBindingCalled({frame, name, payload}) { _onBindingCalled({executionContextId, name, payload}) {
this._browserPage.emit('pageBindingCalled', { this._browserPage.emit('pageBindingCalled', {
executionContextId: frame.executionContext().id(), executionContextId,
name, name,
payload payload
}); });

View File

@ -74,6 +74,7 @@ class Runtime {
onErrorFromWorker: createEvent(), onErrorFromWorker: createEvent(),
onExecutionContextCreated: createEvent(), onExecutionContextCreated: createEvent(),
onExecutionContextDestroyed: createEvent(), onExecutionContextDestroyed: createEvent(),
onBindingCalled: createEvent(),
}; };
} }
@ -166,6 +167,10 @@ class Runtime {
_registerConsoleObserver(Services) { _registerConsoleObserver(Services) {
const consoleObserver = ({wrappedJSObject}, topic, data) => { const consoleObserver = ({wrappedJSObject}, topic, data) => {
const executionContext = Array.from(this._executionContexts.values()).find(context => { const executionContext = Array.from(this._executionContexts.values()).find(context => {
// There is no easy way to determine isolated world context and we normally don't write
// objects to console from utility worlds so we always return main world context here.
if (context._isIsolatedWorldContext())
return false;
const domWindow = context._domWindow; const domWindow = context._domWindow;
return domWindow && domWindow.windowGlobalChild.innerWindowId === wrappedJSObject.innerID; return domWindow && domWindow.windowGlobalChild.innerWindowId === wrappedJSObject.innerID;
}); });
@ -311,6 +316,10 @@ class ExecutionContext {
return this._auxData; return this._auxData;
} }
_isIsolatedWorldContext() {
return !!this._auxData.name;
}
async evaluateScript(script, exceptionDetails = {}) { async evaluateScript(script, exceptionDetails = {}) {
const userInputHelper = this._domWindow ? this._domWindow.windowUtils.setHandlingUserInput(true) : null; const userInputHelper = this._domWindow ? this._domWindow.windowUtils.setHandlingUserInput(true) : null;
if (this._domWindow && this._domWindow.document) if (this._domWindow && this._domWindow.document)
@ -365,6 +374,23 @@ class ExecutionContext {
return this._createRemoteObject(obj); return this._createRemoteObject(obj);
} }
addBinding(name, script) {
Cu.exportFunction((...args) => {
emitEvent(this._runtime.events.onBindingCalled, {
executionContextId: this._id,
name,
payload: args[0],
});
}, this._contextGlobal, {
defineAs: name,
});
try {
this._global.executeInGlobal(script);
} catch (e) {
dump(`ERROR: ${e.message}\n${e.stack}\n`);
}
}
unsafeObject(objectId) { unsafeObject(objectId) {
if (!this._remoteObjects.has(objectId)) if (!this._remoteObjects.has(objectId))
return; return;

View File

@ -91,8 +91,8 @@ function initialize() {
} }
for (const script of scriptsToEvaluateOnNewDocument) for (const script of scriptsToEvaluateOnNewDocument)
frameTree.addScriptToEvaluateOnNewDocument(script); frameTree.addScriptToEvaluateOnNewDocument(script);
for (const { name, script } of bindings) for (const { worldName, name, script } of bindings)
frameTree.addBinding(name, script); frameTree.addBinding(worldName, name, script);
pageAgent = new PageAgent(messageManager, channel, frameTree); pageAgent = new PageAgent(messageManager, channel, frameTree);
@ -101,8 +101,8 @@ function initialize() {
frameTree.addScriptToEvaluateOnNewDocument(script); frameTree.addScriptToEvaluateOnNewDocument(script);
}, },
addBinding({name, script}) { addBinding({worldName, name, script}) {
frameTree.addBinding(name, script); frameTree.addBinding(worldName, name, script);
}, },
applyContextSetting({name, value}) { applyContextSetting({name, value}) {

View File

@ -237,8 +237,8 @@ class BrowserHandler {
await this._targetRegistry.browserContextForId(browserContextId).addScriptToEvaluateOnNewDocument(script); await this._targetRegistry.browserContextForId(browserContextId).addScriptToEvaluateOnNewDocument(script);
} }
async ['Browser.addBinding']({browserContextId, name, script}) { async ['Browser.addBinding']({browserContextId, worldName, name, script}) {
await this._targetRegistry.browserContextForId(browserContextId).addBinding(name, script); await this._targetRegistry.browserContextForId(browserContextId).addBinding(worldName, name, script);
} }
['Browser.setCookies']({browserContextId, cookies}) { ['Browser.setCookies']({browserContextId, cookies}) {

View File

@ -373,6 +373,7 @@ const Browser = {
'addBinding': { 'addBinding': {
params: { params: {
browserContextId: t.Optional(t.String), browserContextId: t.Optional(t.String),
worldName: t.Optional(t.String),
name: t.String, name: t.String,
script: t.String, script: t.String,
}, },
@ -523,7 +524,10 @@ const Runtime = {
events: { events: {
'executionContextCreated': { 'executionContextCreated': {
executionContextId: t.String, executionContextId: t.String,
auxData: t.Any, auxData: {
frameId: t.Optional(t.String),
name: t.Optional(t.String),
},
}, },
'executionContextDestroyed': { 'executionContextDestroyed': {
executionContextId: t.String, executionContextId: t.String,
@ -718,6 +722,7 @@ const Page = {
}, },
'addBinding': { 'addBinding': {
params: { params: {
worldName: t.Optional(t.String),
name: t.String, name: t.String,
script: t.String, script: t.String,
}, },