mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-11 16:32:59 +00:00
e1ad6294bf
Afaict the source lines are not exposed anywhere in the UI. Differential Revision: https://phabricator.services.mozilla.com/D218115
382 lines
9.3 KiB
JavaScript
382 lines
9.3 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
const lazy = {};
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
addDebuggerToGlobal: "resource://gre/modules/jsdebugger.sys.mjs",
|
|
|
|
generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
|
|
});
|
|
|
|
ChromeUtils.defineLazyGetter(lazy, "dbg", () => {
|
|
// eslint-disable-next-line mozilla/reject-globalThis-modification
|
|
lazy.addDebuggerToGlobal(globalThis);
|
|
return new Debugger();
|
|
});
|
|
|
|
/**
|
|
* @typedef {string} RealmType
|
|
*/
|
|
|
|
/**
|
|
* Enum of realm types.
|
|
*
|
|
* @readonly
|
|
* @enum {RealmType}
|
|
*/
|
|
export const RealmType = {
|
|
AudioWorklet: "audio-worklet",
|
|
DedicatedWorker: "dedicated-worker",
|
|
PaintWorklet: "paint-worklet",
|
|
ServiceWorker: "service-worker",
|
|
SharedWorker: "shared-worker",
|
|
Window: "window",
|
|
Worker: "worker",
|
|
Worklet: "worklet",
|
|
};
|
|
|
|
/**
|
|
* Base class that wraps any kind of WebDriver BiDi realm.
|
|
*/
|
|
export class Realm {
|
|
#handleObjectMap;
|
|
#id;
|
|
|
|
constructor() {
|
|
this.#id = lazy.generateUUID();
|
|
|
|
// Map of unique handles (UUIDs) to objects belonging to this realm.
|
|
this.#handleObjectMap = new Map();
|
|
}
|
|
|
|
destroy() {
|
|
this.#handleObjectMap = null;
|
|
}
|
|
|
|
/**
|
|
* Get the browsing context of the realm instance.
|
|
*/
|
|
get browsingContext() {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the unique identifier of the realm instance.
|
|
*
|
|
* @returns {string} The unique identifier.
|
|
*/
|
|
get id() {
|
|
return this.#id;
|
|
}
|
|
|
|
/**
|
|
* A getter to get a realm origin.
|
|
*
|
|
* It's required to be implemented in the sub class.
|
|
*/
|
|
get origin() {
|
|
throw new Error("Not implemented");
|
|
}
|
|
|
|
/**
|
|
* Ensure the provided object can be used within this realm.
|
|
|
|
* @param {object} obj
|
|
* Any non-primitive object.
|
|
|
|
* @returns {object}
|
|
* An object usable in the current realm.
|
|
*/
|
|
cloneIntoRealm(obj) {
|
|
return obj;
|
|
}
|
|
|
|
/**
|
|
* Remove the reference corresponding to the provided unique handle.
|
|
*
|
|
* @param {string} handle
|
|
* The unique handle of an object reference tracked in this realm.
|
|
*/
|
|
removeObjectHandle(handle) {
|
|
this.#handleObjectMap.delete(handle);
|
|
}
|
|
|
|
/**
|
|
* Get a new unique handle for the provided object, creating a strong
|
|
* reference on the object.
|
|
*
|
|
* @param {object} object
|
|
* Any non-primitive object.
|
|
* @returns {string} The unique handle created for this strong reference.
|
|
*/
|
|
getHandleForObject(object) {
|
|
const handle = lazy.generateUUID();
|
|
this.#handleObjectMap.set(handle, object);
|
|
return handle;
|
|
}
|
|
|
|
/**
|
|
* Get the basic realm information.
|
|
*
|
|
* @returns {BaseRealmInfo}
|
|
*/
|
|
getInfo() {
|
|
return {
|
|
realm: this.#id,
|
|
origin: this.origin,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Retrieve the object corresponding to the provided unique handle.
|
|
*
|
|
* @param {string} handle
|
|
* The unique handle of an object reference tracked in this realm.
|
|
* @returns {object} object
|
|
* Any non-primitive object.
|
|
*/
|
|
getObjectForHandle(handle) {
|
|
return this.#handleObjectMap.get(handle);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wrapper for Window realms including sandbox objects.
|
|
*/
|
|
export class WindowRealm extends Realm {
|
|
#realmAutomationFeaturesEnabled;
|
|
#globalObject;
|
|
#globalObjectReference;
|
|
#isSandbox;
|
|
#sandboxName;
|
|
#userActivationEnabled;
|
|
#window;
|
|
|
|
static type = RealmType.Window;
|
|
|
|
/**
|
|
*
|
|
* @param {Window} window
|
|
* The window global to wrap.
|
|
* @param {object} options
|
|
* @param {string=} options.sandboxName
|
|
* Name of the sandbox to create if specified. Defaults to `null`.
|
|
*/
|
|
constructor(window, options = {}) {
|
|
const { sandboxName = null } = options;
|
|
|
|
super();
|
|
|
|
this.#isSandbox = sandboxName !== null;
|
|
this.#sandboxName = sandboxName;
|
|
this.#window = window;
|
|
this.#globalObject = this.#isSandbox ? this.#createSandbox() : this.#window;
|
|
this.#globalObjectReference = lazy.dbg.makeGlobalObjectReference(
|
|
this.#globalObject
|
|
);
|
|
this.#realmAutomationFeaturesEnabled = false;
|
|
this.#userActivationEnabled = false;
|
|
}
|
|
|
|
destroy() {
|
|
if (this.#realmAutomationFeaturesEnabled) {
|
|
lazy.dbg.disableAsyncStack(this.#globalObject);
|
|
lazy.dbg.disableUnlimitedStacksCapturing(this.#globalObject);
|
|
this.#realmAutomationFeaturesEnabled = false;
|
|
}
|
|
|
|
this.#globalObjectReference = null;
|
|
this.#globalObject = null;
|
|
this.#window = null;
|
|
|
|
super.destroy();
|
|
}
|
|
|
|
get browsingContext() {
|
|
return this.#window.browsingContext;
|
|
}
|
|
|
|
get globalObjectReference() {
|
|
return this.#globalObjectReference;
|
|
}
|
|
|
|
get isSandbox() {
|
|
return this.#isSandbox;
|
|
}
|
|
|
|
get origin() {
|
|
return this.#window.origin;
|
|
}
|
|
|
|
get userActivationEnabled() {
|
|
return this.#userActivationEnabled;
|
|
}
|
|
|
|
set userActivationEnabled(enable) {
|
|
if (enable === this.#userActivationEnabled) {
|
|
return;
|
|
}
|
|
|
|
const document = this.#window.document;
|
|
if (enable) {
|
|
document.notifyUserGestureActivation();
|
|
} else {
|
|
document.clearUserGestureActivation();
|
|
}
|
|
|
|
this.#userActivationEnabled = enable;
|
|
}
|
|
|
|
#createDebuggerObject(obj) {
|
|
return this.#globalObjectReference.makeDebuggeeValue(obj);
|
|
}
|
|
|
|
#createSandbox() {
|
|
const win = this.#window;
|
|
const opts = {
|
|
sameZoneAs: win,
|
|
sandboxPrototype: win,
|
|
wantComponents: false,
|
|
wantXrays: true,
|
|
};
|
|
|
|
return new Cu.Sandbox(win, opts);
|
|
}
|
|
|
|
#enableRealmAutomationFeatures() {
|
|
if (!this.#realmAutomationFeaturesEnabled) {
|
|
lazy.dbg.enableAsyncStack(this.#globalObject);
|
|
lazy.dbg.enableUnlimitedStacksCapturing(this.#globalObject);
|
|
this.#realmAutomationFeaturesEnabled = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clone the provided object into the scope of this Realm (either a window
|
|
* global, or a sandbox).
|
|
*
|
|
* @param {object} obj
|
|
* Any non-primitive object.
|
|
*
|
|
* @returns {object}
|
|
* The cloned object.
|
|
*/
|
|
cloneIntoRealm(obj) {
|
|
return Cu.cloneInto(obj, this.#globalObject, { cloneFunctions: true });
|
|
}
|
|
|
|
/**
|
|
* Evaluates a provided expression in the context of the current realm.
|
|
*
|
|
* @param {string} expression
|
|
* The expression to evaluate.
|
|
*
|
|
* @returns {object}
|
|
* - evaluationStatus {EvaluationStatus} One of "normal", "throw".
|
|
* - exceptionDetails {ExceptionDetails=} the details of the exception if
|
|
* the evaluation status was "throw".
|
|
* - result {RemoteValue=} the result of the evaluation serialized as a
|
|
* RemoteValue if the evaluation status was "normal".
|
|
*/
|
|
executeInGlobal(expression) {
|
|
this.#enableRealmAutomationFeatures();
|
|
return this.#globalObjectReference.executeInGlobal(expression, {
|
|
url: this.#window.document.baseURI,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Call a function in the context of the current realm.
|
|
*
|
|
* @param {string} functionDeclaration
|
|
* The body of the function to call.
|
|
* @param {Array<object>} functionArguments
|
|
* The arguments to pass to the function call.
|
|
* @param {object} thisParameter
|
|
* The value of the `this` keyword for the function call.
|
|
*
|
|
* @returns {object}
|
|
* - evaluationStatus {EvaluationStatus} One of "normal", "throw".
|
|
* - exceptionDetails {ExceptionDetails=} the details of the exception if
|
|
* the evaluation status was "throw".
|
|
* - result {RemoteValue=} the result of the evaluation serialized as a
|
|
* RemoteValue if the evaluation status was "normal".
|
|
*/
|
|
executeInGlobalWithBindings(
|
|
functionDeclaration,
|
|
functionArguments,
|
|
thisParameter
|
|
) {
|
|
this.#enableRealmAutomationFeatures();
|
|
const expression = `(${functionDeclaration}).apply(__bidi_this, __bidi_args)`;
|
|
|
|
const args = this.cloneIntoRealm([]);
|
|
for (const arg of functionArguments) {
|
|
args.push(arg);
|
|
}
|
|
|
|
return this.#globalObjectReference.executeInGlobalWithBindings(
|
|
expression,
|
|
{
|
|
__bidi_args: this.#createDebuggerObject(args),
|
|
__bidi_this: this.#createDebuggerObject(thisParameter),
|
|
},
|
|
{
|
|
url: this.#window.document.baseURI,
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the realm information.
|
|
*
|
|
* @returns {object}
|
|
* - context {BrowsingContext} The browsing context, associated with the realm.
|
|
* - id {string} The realm unique identifier.
|
|
* - origin {string} The serialization of an origin.
|
|
* - sandbox {string=} The name of the sandbox.
|
|
* - type {RealmType.Window} The window realm type.
|
|
*/
|
|
getInfo() {
|
|
const baseInfo = super.getInfo();
|
|
const info = {
|
|
...baseInfo,
|
|
context: this.#window.browsingContext,
|
|
type: WindowRealm.type,
|
|
};
|
|
|
|
if (this.#isSandbox) {
|
|
info.sandbox = this.#sandboxName;
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
/**
|
|
* Log an error caused by a script evaluation.
|
|
*
|
|
* @param {string} message
|
|
* The error message.
|
|
* @param {Stack} stack
|
|
* The JavaScript stack trace.
|
|
*/
|
|
reportError(message, stack) {
|
|
const { column, line } = stack;
|
|
|
|
const scriptErrorClass = Cc["@mozilla.org/scripterror;1"];
|
|
const scriptError = scriptErrorClass.createInstance(Ci.nsIScriptError);
|
|
|
|
scriptError.initWithWindowID(
|
|
message,
|
|
this.#window.document.baseURI,
|
|
line,
|
|
column,
|
|
Ci.nsIScriptError.errorFlag,
|
|
"content javascript",
|
|
this.#window.windowGlobalChild.innerWindowId
|
|
);
|
|
Services.console.logMessage(scriptError);
|
|
}
|
|
}
|