1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 ChromeUtils.defineESModuleGetters(lazy, {
7 addDebuggerToGlobal: "resource://gre/modules/jsdebugger.sys.mjs",
9 generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
12 ChromeUtils.defineLazyGetter(lazy, "dbg", () => {
13 // eslint-disable-next-line mozilla/reject-globalThis-modification
14 lazy.addDebuggerToGlobal(globalThis);
15 return new Debugger();
19 * @typedef {string} RealmType
23 * Enum of realm types.
28 export const RealmType = {
29 AudioWorklet: "audio-worklet",
30 DedicatedWorker: "dedicated-worker",
31 PaintWorklet: "paint-worklet",
32 ServiceWorker: "service-worker",
33 SharedWorker: "shared-worker",
40 * Base class that wraps any kind of WebDriver BiDi realm.
47 this.#id = lazy.generateUUID();
49 // Map of unique handles (UUIDs) to objects belonging to this realm.
50 this.#handleObjectMap = new Map();
54 this.#handleObjectMap = null;
58 * Get the browsing context of the realm instance.
60 get browsingContext() {
65 * Get the unique identifier of the realm instance.
67 * @returns {string} The unique identifier.
74 * A getter to get a realm origin.
76 * It's required to be implemented in the sub class.
79 throw new Error("Not implemented");
83 * Ensure the provided object can be used within this realm.
86 * Any non-primitive object.
89 * An object usable in the current realm.
96 * Remove the reference corresponding to the provided unique handle.
98 * @param {string} handle
99 * The unique handle of an object reference tracked in this realm.
101 removeObjectHandle(handle) {
102 this.#handleObjectMap.delete(handle);
106 * Get a new unique handle for the provided object, creating a strong
107 * reference on the object.
109 * @param {object} object
110 * Any non-primitive object.
111 * @returns {string} The unique handle created for this strong reference.
113 getHandleForObject(object) {
114 const handle = lazy.generateUUID();
115 this.#handleObjectMap.set(handle, object);
120 * Get the basic realm information.
122 * @returns {BaseRealmInfo}
132 * Retrieve the object corresponding to the provided unique handle.
134 * @param {string} handle
135 * The unique handle of an object reference tracked in this realm.
136 * @returns {object} object
137 * Any non-primitive object.
139 getObjectForHandle(handle) {
140 return this.#handleObjectMap.get(handle);
145 * Wrapper for Window realms including sandbox objects.
147 export class WindowRealm extends Realm {
148 #realmAutomationFeaturesEnabled;
150 #globalObjectReference;
153 #userActivationEnabled;
156 static type = RealmType.Window;
160 * @param {Window} window
161 * The window global to wrap.
162 * @param {object} options
163 * @param {string=} options.sandboxName
164 * Name of the sandbox to create if specified. Defaults to `null`.
166 constructor(window, options = {}) {
167 const { sandboxName = null } = options;
171 this.#isSandbox = sandboxName !== null;
172 this.#sandboxName = sandboxName;
173 this.#window = window;
174 this.#globalObject = this.#isSandbox ? this.#createSandbox() : this.#window;
175 this.#globalObjectReference = lazy.dbg.makeGlobalObjectReference(
178 this.#realmAutomationFeaturesEnabled = false;
179 this.#userActivationEnabled = false;
183 if (this.#realmAutomationFeaturesEnabled) {
184 lazy.dbg.disableAsyncStack(this.#globalObject);
185 lazy.dbg.disableUnlimitedStacksCapturing(this.#globalObject);
186 this.#realmAutomationFeaturesEnabled = false;
189 this.#globalObjectReference = null;
190 this.#globalObject = null;
196 get browsingContext() {
197 return this.#window.browsingContext;
200 get globalObjectReference() {
201 return this.#globalObjectReference;
205 return this.#isSandbox;
209 return this.#window.origin;
212 get userActivationEnabled() {
213 return this.#userActivationEnabled;
216 set userActivationEnabled(enable) {
217 if (enable === this.#userActivationEnabled) {
221 const document = this.#window.document;
223 document.notifyUserGestureActivation();
225 document.clearUserGestureActivation();
228 this.#userActivationEnabled = enable;
231 #createDebuggerObject(obj) {
232 return this.#globalObjectReference.makeDebuggeeValue(obj);
236 const win = this.#window;
239 sandboxPrototype: win,
240 wantComponents: false,
244 return new Cu.Sandbox(win, opts);
247 #enableRealmAutomationFeatures() {
248 if (!this.#realmAutomationFeaturesEnabled) {
249 lazy.dbg.enableAsyncStack(this.#globalObject);
250 lazy.dbg.enableUnlimitedStacksCapturing(this.#globalObject);
251 this.#realmAutomationFeaturesEnabled = true;
256 * Clone the provided object into the scope of this Realm (either a window
257 * global, or a sandbox).
259 * @param {object} obj
260 * Any non-primitive object.
265 cloneIntoRealm(obj) {
266 return Cu.cloneInto(obj, this.#globalObject, { cloneFunctions: true });
270 * Evaluates a provided expression in the context of the current realm.
272 * @param {string} expression
273 * The expression to evaluate.
276 * - evaluationStatus {EvaluationStatus} One of "normal", "throw".
277 * - exceptionDetails {ExceptionDetails=} the details of the exception if
278 * the evaluation status was "throw".
279 * - result {RemoteValue=} the result of the evaluation serialized as a
280 * RemoteValue if the evaluation status was "normal".
282 executeInGlobal(expression) {
283 this.#enableRealmAutomationFeatures();
284 return this.#globalObjectReference.executeInGlobal(expression, {
285 url: this.#window.document.baseURI,
290 * Call a function in the context of the current realm.
292 * @param {string} functionDeclaration
293 * The body of the function to call.
294 * @param {Array<object>} functionArguments
295 * The arguments to pass to the function call.
296 * @param {object} thisParameter
297 * The value of the `this` keyword for the function call.
300 * - evaluationStatus {EvaluationStatus} One of "normal", "throw".
301 * - exceptionDetails {ExceptionDetails=} the details of the exception if
302 * the evaluation status was "throw".
303 * - result {RemoteValue=} the result of the evaluation serialized as a
304 * RemoteValue if the evaluation status was "normal".
306 executeInGlobalWithBindings(
311 this.#enableRealmAutomationFeatures();
312 const expression = `(${functionDeclaration}).apply(__bidi_this, __bidi_args)`;
314 const args = this.cloneIntoRealm([]);
315 for (const arg of functionArguments) {
319 return this.#globalObjectReference.executeInGlobalWithBindings(
322 __bidi_args: this.#createDebuggerObject(args),
323 __bidi_this: this.#createDebuggerObject(thisParameter),
326 url: this.#window.document.baseURI,
332 * Get the realm information.
335 * - context {BrowsingContext} The browsing context, associated with the realm.
336 * - id {string} The realm unique identifier.
337 * - origin {string} The serialization of an origin.
338 * - sandbox {string=} The name of the sandbox.
339 * - type {RealmType.Window} The window realm type.
342 const baseInfo = super.getInfo();
345 context: this.#window.browsingContext,
346 type: WindowRealm.type,
349 if (this.#isSandbox) {
350 info.sandbox = this.#sandboxName;
357 * Log an error caused by a script evaluation.
359 * @param {string} message
361 * @param {Stack} stack
362 * The JavaScript stack trace.
364 reportError(message, stack) {
365 const { column, line, source: sourceLine } = stack;
367 const scriptErrorClass = Cc["@mozilla.org/scripterror;1"];
368 const scriptError = scriptErrorClass.createInstance(Ci.nsIScriptError);
370 scriptError.initWithWindowID(
372 this.#window.document.baseURI,
376 Ci.nsIScriptError.errorFlag,
377 "content javascript",
378 this.#window.windowGlobalChild.innerWindowId
380 Services.console.logMessage(scriptError);