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
Changed: pavel.feldman@gmail.com Mon 10 May 2021 09:56:40 PM PDT
1259
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);
}
async addBinding(name, script) {
await this._channel.connect('').send('addBinding', { name, script }).catch(e => void e);
async addBinding(worldName, name, script) {
await this._channel.connect('').send('addBinding', { worldName, name, script }).catch(e => void e);
}
async applyContextSetting(name, value) {
@ -717,9 +717,9 @@ class BrowserContext {
await Promise.all(Array.from(this.pages).map(page => page.addScriptToEvaluateOnNewDocument(script)));
}
async addBinding(name, script) {
this.bindings.push({ name, script });
await Promise.all(Array.from(this.pages).map(page => page.addBinding(name, script)));
async addBinding(worldName, name, script) {
this.bindings.push({ worldName, name, script });
await Promise.all(Array.from(this.pages).map(page => page.addBinding(worldName, name, script)));
}
async applySetting(name, value) {

View File

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

View File

@ -104,7 +104,6 @@ class PageAgent {
helper.addObserver(this._onDocumentOpenLoad.bind(this), 'juggler-document-open-loaded'),
helper.addEventListener(this._messageManager, 'error', this._onError.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, 'framedetached', this._onFrameDetached.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.onExecutionContextCreated(this._onExecutionContextCreated.bind(this)),
this._runtime.events.onExecutionContextDestroyed(this._onExecutionContextDestroyed.bind(this)),
this._runtime.events.onBindingCalled(this._onBindingCalled.bind(this)),
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),
adoptNode: this._adoptNode.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', {
executionContextId: frame.executionContext().id(),
executionContextId,
name,
payload
});

View File

@ -74,6 +74,7 @@ class Runtime {
onErrorFromWorker: createEvent(),
onExecutionContextCreated: createEvent(),
onExecutionContextDestroyed: createEvent(),
onBindingCalled: createEvent(),
};
}
@ -166,6 +167,10 @@ class Runtime {
_registerConsoleObserver(Services) {
const consoleObserver = ({wrappedJSObject}, topic, data) => {
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;
return domWindow && domWindow.windowGlobalChild.innerWindowId === wrappedJSObject.innerID;
});
@ -311,6 +316,10 @@ class ExecutionContext {
return this._auxData;
}
_isIsolatedWorldContext() {
return !!this._auxData.name;
}
async evaluateScript(script, exceptionDetails = {}) {
const userInputHelper = this._domWindow ? this._domWindow.windowUtils.setHandlingUserInput(true) : null;
if (this._domWindow && this._domWindow.document)
@ -365,6 +374,23 @@ class ExecutionContext {
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) {
if (!this._remoteObjects.has(objectId))
return;

View File

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

View File

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

View File

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