mirror of
https://forge.fsky.io/oneflux/omegafox.git
synced 2026-02-10 06:22: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(),
|
||||
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);
|
||||
return world;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,6 +99,36 @@ class Runtime {
|
|||
const executionContext = this.findExecutionContext(executionContextId);
|
||||
if (!executionContext)
|
||||
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 = {};
|
||||
let result = await executionContext.evaluateFunction(functionDeclaration, args, exceptionDetails);
|
||||
if (!result)
|
||||
|
|
@ -106,7 +136,7 @@ class Runtime {
|
|||
if (returnByValue)
|
||||
result = executionContext.ensureSerializedToValue(result);
|
||||
return {result};
|
||||
}
|
||||
}
|
||||
|
||||
async getObjectProperties({executionContextId, objectId}) {
|
||||
const executionContext = this.findExecutionContext(executionContextId);
|
||||
|
|
@ -282,6 +312,11 @@ class Runtime {
|
|||
return context;
|
||||
}
|
||||
|
||||
createMW(domWindow, contextGlobal) {
|
||||
const context = new MainWorldContext(this, domWindow, contextGlobal);
|
||||
return context;
|
||||
}
|
||||
|
||||
findExecutionContext(executionContextId) {
|
||||
const executionContext = this._executionContexts.get(executionContextId);
|
||||
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 {
|
||||
constructor(runtime, domWindow, contextGlobal, auxData) {
|
||||
this._runtime = runtime;
|
||||
|
|
|
|||
|
|
@ -288,6 +288,7 @@
|
|||
"humanize:minTime": "double[>=0]",
|
||||
"showcursor": "bool",
|
||||
|
||||
"allowMainWorld": "bool",
|
||||
"memorysaver": "bool",
|
||||
"addons": "array[str]",
|
||||
"debug": "bool"
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@
|
|||
{ "property": "mediaDevices:webcams", "type": "uint" },
|
||||
{ "property": "mediaDevices:speakers", "type": "uint" },
|
||||
{ "property": "mediaDevices:enabled", "type": "bool" },
|
||||
{ "property": "allowMainWorld", "type": "bool" },
|
||||
{ "property": "memorysaver", "type": "bool" },
|
||||
{ "property": "addons", "type": "array" },
|
||||
{ "property": "debug", "type": "bool" }
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue