diff options
Diffstat (limited to 'remote/webdriver-bidi/Realm.sys.mjs')
-rw-r--r-- | remote/webdriver-bidi/Realm.sys.mjs | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/remote/webdriver-bidi/Realm.sys.mjs b/remote/webdriver-bidi/Realm.sys.mjs new file mode 100644 index 0000000000..e4924cecdd --- /dev/null +++ b/remote/webdriver-bidi/Realm.sys.mjs @@ -0,0 +1,306 @@ +/* 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/. */ + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + addDebuggerToGlobal: "resource://gre/modules/jsdebugger.sys.mjs", +}); + +XPCOMUtils.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", +}; + +function getUUID() { + return Services.uuid + .generateUUID() + .toString() + .slice(1, -1); +} + +/** + * Base class that wraps any kind of WebDriver BiDi realm. + */ +export class Realm { + #handleObjectMap; + #id; + + constructor() { + this.#id = getUUID(); + + // Map of unique handles (UUIDs) to objects belonging to this realm. + this.#handleObjectMap = new Map(); + } + + destroy() { + this.#handleObjectMap = null; + } + + /** + * Get the unique identifier of the realm instance. + * + * @return {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} object + * Any non-primitive object. + + * @return {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. + * @return {string} The unique handle created for this strong reference. + */ + getHandleForObject(object) { + const handle = getUUID(); + this.#handleObjectMap.set(handle, object); + return handle; + } + + /** + * Get the basic realm information. + * + * @return {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. + * @return {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 { + #globalObject; + #globalObjectReference; + #sandboxName; + #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.#sandboxName = sandboxName; + this.#window = window; + this.#globalObject = + sandboxName === null ? this.#window : this.#createSandbox(); + this.#globalObjectReference = lazy.dbg.makeGlobalObjectReference( + this.#globalObject + ); + + lazy.dbg.enableAsyncStack(this.#globalObject); + } + + destroy() { + lazy.dbg.disableAsyncStack(this.#globalObject); + + this.#globalObjectReference = null; + this.#globalObject = null; + this.#window = null; + + super.destroy(); + } + + get globalObjectReference() { + return this.#globalObjectReference; + } + + get origin() { + return this.#window.origin; + } + + #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); + } + + /** + * Clone the provided object into the scope of this Realm (either a window + * global, or a sandbox). + * + * @param {Object} obj + * Any non-primitive object. + * + * @return {Object} + * The cloned object. + */ + cloneIntoRealm(obj) { + return Cu.cloneInto(obj, this.#globalObject); + } + + /** + * Evaluates a provided expression in the context of the current realm. + * + * @param {string} expression + * The expression to evaluate. + * + * @return {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) { + 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. + * + * @return {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 + ) { + 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. + * + * @return {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|null} The name of the sandbox. + * - type {RealmType.Window} The window realm type. + */ + getInfo() { + const baseInfo = super.getInfo(); + return { + ...baseInfo, + context: this.#window.browsingContext, + sandbox: this.#sandboxName, + type: WindowRealm.type, + }; + } +} |