mirror of
https://forge.fsky.io/oneflux/omegafox.git
synced 2026-02-10 11:02:03 -08:00
feat: Main world JS evaluation
Experimental support to execute in the main world. Usage: `page.evaluate("mw:<script>")`
Has only been implemented to pass JSON serializable objects to/from the main world (Isolated worlds are still the default, and should be used unless necessary).
This commit is contained in:
parent
3e524aa2ea
commit
4305385f0b
4 changed files with 105 additions and 1 deletions
|
|
@ -517,6 +517,11 @@ class Frame {
|
||||||
frameId: this.id(),
|
frameId: this.id(),
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
|
// Camoufox: Create a main world for the isolated context
|
||||||
|
if (ChromeUtils.camouGetBool('allowMainWorld', false)) {
|
||||||
|
const mainWorld = this._runtime.createMW(this.domWindow(), this.domWindow());
|
||||||
|
world.mainEquivalent = mainWorld;
|
||||||
|
}
|
||||||
this._worldNameToContext.set(name, world);
|
this._worldNameToContext.set(name, world);
|
||||||
return world;
|
return world;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,36 @@ class Runtime {
|
||||||
const executionContext = this.findExecutionContext(executionContextId);
|
const executionContext = this.findExecutionContext(executionContextId);
|
||||||
if (!executionContext)
|
if (!executionContext)
|
||||||
throw new Error('Failed to find execution context with id = ' + executionContextId);
|
throw new Error('Failed to find execution context with id = ' + executionContextId);
|
||||||
|
|
||||||
|
// Hijack the utilityScript.evaluate function to evaluate in the main world
|
||||||
|
if (
|
||||||
|
ChromeUtils.camouGetBool('allowMainWorld', false) &&
|
||||||
|
functionDeclaration.includes('utilityScript.evaluate') &&
|
||||||
|
args.length >= 4 &&
|
||||||
|
args[3].value &&
|
||||||
|
typeof args[3].value === 'string' &&
|
||||||
|
args[3].value.startsWith('mw:')) {
|
||||||
|
ChromeUtils.camouDebug(`Evaluating in main world: ${args[3].value}`);
|
||||||
|
const mainWorldScript = args[3].value.substring(3);
|
||||||
|
|
||||||
|
// Get the main world execution context
|
||||||
|
const mainContext = executionContext.mainEquivalent;
|
||||||
|
if (!mainContext) {
|
||||||
|
throw new Error(`Main world injection is not enabled.`);
|
||||||
|
}
|
||||||
|
// Extract arguments for the main world function
|
||||||
|
const functionArgs = args[5]?.value?.a || [];
|
||||||
|
try {
|
||||||
|
const exceptionDetails = {};
|
||||||
|
const result = mainContext.executeInGlobal(mainWorldScript, functionArgs, exceptionDetails);
|
||||||
|
if (!result)
|
||||||
|
return {exceptionDetails};
|
||||||
|
return {result};
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const exceptionDetails = {};
|
const exceptionDetails = {};
|
||||||
let result = await executionContext.evaluateFunction(functionDeclaration, args, exceptionDetails);
|
let result = await executionContext.evaluateFunction(functionDeclaration, args, exceptionDetails);
|
||||||
if (!result)
|
if (!result)
|
||||||
|
|
@ -106,7 +136,7 @@ class Runtime {
|
||||||
if (returnByValue)
|
if (returnByValue)
|
||||||
result = executionContext.ensureSerializedToValue(result);
|
result = executionContext.ensureSerializedToValue(result);
|
||||||
return {result};
|
return {result};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getObjectProperties({executionContextId, objectId}) {
|
async getObjectProperties({executionContextId, objectId}) {
|
||||||
const executionContext = this.findExecutionContext(executionContextId);
|
const executionContext = this.findExecutionContext(executionContextId);
|
||||||
|
|
@ -282,6 +312,11 @@ class Runtime {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createMW(domWindow, contextGlobal) {
|
||||||
|
const context = new MainWorldContext(this, domWindow, contextGlobal);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
findExecutionContext(executionContextId) {
|
findExecutionContext(executionContextId) {
|
||||||
const executionContext = this._executionContexts.get(executionContextId);
|
const executionContext = this._executionContexts.get(executionContextId);
|
||||||
if (!executionContext)
|
if (!executionContext)
|
||||||
|
|
@ -306,6 +341,68 @@ class Runtime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MainWorldContext {
|
||||||
|
constructor(runtime, domWindow, contextGlobal) {
|
||||||
|
this._runtime = runtime;
|
||||||
|
this._domWindow = domWindow;
|
||||||
|
this._contextGlobal = contextGlobal;
|
||||||
|
this._debuggee = runtime._debugger.addDebuggee(contextGlobal);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getResult(completionValue, exceptionDetails = {}) {
|
||||||
|
if (!completionValue) {
|
||||||
|
exceptionDetails.text = "Evaluation terminated";
|
||||||
|
return {success: false, obj: null};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completionValue.throw) {
|
||||||
|
const result = this._debuggee.executeInGlobalWithBindings(`
|
||||||
|
(function(error) {
|
||||||
|
try {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
return error.toString();
|
||||||
|
}
|
||||||
|
return String(error);
|
||||||
|
} catch(e) {
|
||||||
|
return "Unknown error occurred";
|
||||||
|
}
|
||||||
|
})(e)
|
||||||
|
`, { e: completionValue.throw });
|
||||||
|
|
||||||
|
exceptionDetails.text = result.return || "Unknown error";
|
||||||
|
return {success: false, obj: null};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {success: true, obj: completionValue.return};
|
||||||
|
}
|
||||||
|
|
||||||
|
executeInGlobal(script, args = [], exceptionDetails = {}) {
|
||||||
|
try {
|
||||||
|
const wrappedScript = `
|
||||||
|
(() => {
|
||||||
|
const result = (${script});
|
||||||
|
return typeof result === 'function'
|
||||||
|
? result(${args.map(arg => JSON.stringify(arg)).join(', ')})
|
||||||
|
: result;
|
||||||
|
})()
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = this._debuggee.executeInGlobal(wrappedScript);
|
||||||
|
|
||||||
|
let {success, obj} = this._getResult(result, exceptionDetails);
|
||||||
|
if (!success) {
|
||||||
|
return {exceptionDetails};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {value: obj};
|
||||||
|
} catch (e) {
|
||||||
|
exceptionDetails.text = e.message;
|
||||||
|
exceptionDetails.stack = e.stack;
|
||||||
|
return {exceptionDetails};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ExecutionContext {
|
class ExecutionContext {
|
||||||
constructor(runtime, domWindow, contextGlobal, auxData) {
|
constructor(runtime, domWindow, contextGlobal, auxData) {
|
||||||
this._runtime = runtime;
|
this._runtime = runtime;
|
||||||
|
|
|
||||||
|
|
@ -288,6 +288,7 @@
|
||||||
"humanize:minTime": "double[>=0]",
|
"humanize:minTime": "double[>=0]",
|
||||||
"showcursor": "bool",
|
"showcursor": "bool",
|
||||||
|
|
||||||
|
"allowMainWorld": "bool",
|
||||||
"memorysaver": "bool",
|
"memorysaver": "bool",
|
||||||
"addons": "array[str]",
|
"addons": "array[str]",
|
||||||
"debug": "bool"
|
"debug": "bool"
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@
|
||||||
{ "property": "mediaDevices:webcams", "type": "uint" },
|
{ "property": "mediaDevices:webcams", "type": "uint" },
|
||||||
{ "property": "mediaDevices:speakers", "type": "uint" },
|
{ "property": "mediaDevices:speakers", "type": "uint" },
|
||||||
{ "property": "mediaDevices:enabled", "type": "bool" },
|
{ "property": "mediaDevices:enabled", "type": "bool" },
|
||||||
|
{ "property": "allowMainWorld", "type": "bool" },
|
||||||
{ "property": "memorysaver", "type": "bool" },
|
{ "property": "memorysaver", "type": "bool" },
|
||||||
{ "property": "addons", "type": "array" },
|
{ "property": "addons", "type": "array" },
|
||||||
{ "property": "debug", "type": "bool" }
|
{ "property": "debug", "type": "bool" }
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue