Bug 1890689 accumulate input in LargerReceiverBlockSizeThanDesiredBuffering GTest...
[gecko.git] / remote / shared / Realm.sys.mjs
blob5bf4a2fa3a01bbd97bcf7e2b2253e39cc922e001
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/. */
5 const lazy = {};
6 ChromeUtils.defineESModuleGetters(lazy, {
7   addDebuggerToGlobal: "resource://gre/modules/jsdebugger.sys.mjs",
9   generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
10 });
12 ChromeUtils.defineLazyGetter(lazy, "dbg", () => {
13   // eslint-disable-next-line mozilla/reject-globalThis-modification
14   lazy.addDebuggerToGlobal(globalThis);
15   return new Debugger();
16 });
18 /**
19  * @typedef {string} RealmType
20  */
22 /**
23  * Enum of realm types.
24  *
25  * @readonly
26  * @enum {RealmType}
27  */
28 export const RealmType = {
29   AudioWorklet: "audio-worklet",
30   DedicatedWorker: "dedicated-worker",
31   PaintWorklet: "paint-worklet",
32   ServiceWorker: "service-worker",
33   SharedWorker: "shared-worker",
34   Window: "window",
35   Worker: "worker",
36   Worklet: "worklet",
39 /**
40  * Base class that wraps any kind of WebDriver BiDi realm.
41  */
42 export class Realm {
43   #handleObjectMap;
44   #id;
46   constructor() {
47     this.#id = lazy.generateUUID();
49     // Map of unique handles (UUIDs) to objects belonging to this realm.
50     this.#handleObjectMap = new Map();
51   }
53   destroy() {
54     this.#handleObjectMap = null;
55   }
57   /**
58    * Get the browsing context of the realm instance.
59    */
60   get browsingContext() {
61     return null;
62   }
64   /**
65    * Get the unique identifier of the realm instance.
66    *
67    * @returns {string} The unique identifier.
68    */
69   get id() {
70     return this.#id;
71   }
73   /**
74    * A getter to get a realm origin.
75    *
76    * It's required to be implemented in the sub class.
77    */
78   get origin() {
79     throw new Error("Not implemented");
80   }
82   /**
83    * Ensure the provided object can be used within this realm.
85    * @param {object} obj
86    *     Any non-primitive object.
88    * @returns {object}
89    *     An object usable in the current realm.
90    */
91   cloneIntoRealm(obj) {
92     return obj;
93   }
95   /**
96    * Remove the reference corresponding to the provided unique handle.
97    *
98    * @param {string} handle
99    *     The unique handle of an object reference tracked in this realm.
100    */
101   removeObjectHandle(handle) {
102     this.#handleObjectMap.delete(handle);
103   }
105   /**
106    * Get a new unique handle for the provided object, creating a strong
107    * reference on the object.
108    *
109    * @param {object} object
110    *     Any non-primitive object.
111    * @returns {string} The unique handle created for this strong reference.
112    */
113   getHandleForObject(object) {
114     const handle = lazy.generateUUID();
115     this.#handleObjectMap.set(handle, object);
116     return handle;
117   }
119   /**
120    * Get the basic realm information.
121    *
122    * @returns {BaseRealmInfo}
123    */
124   getInfo() {
125     return {
126       realm: this.#id,
127       origin: this.origin,
128     };
129   }
131   /**
132    * Retrieve the object corresponding to the provided unique handle.
133    *
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.
138    */
139   getObjectForHandle(handle) {
140     return this.#handleObjectMap.get(handle);
141   }
145  * Wrapper for Window realms including sandbox objects.
146  */
147 export class WindowRealm extends Realm {
148   #realmAutomationFeaturesEnabled;
149   #globalObject;
150   #globalObjectReference;
151   #isSandbox;
152   #sandboxName;
153   #userActivationEnabled;
154   #window;
156   static type = RealmType.Window;
158   /**
159    *
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`.
165    */
166   constructor(window, options = {}) {
167     const { sandboxName = null } = options;
169     super();
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(
176       this.#globalObject
177     );
178     this.#realmAutomationFeaturesEnabled = false;
179     this.#userActivationEnabled = false;
180   }
182   destroy() {
183     if (this.#realmAutomationFeaturesEnabled) {
184       lazy.dbg.disableAsyncStack(this.#globalObject);
185       lazy.dbg.disableUnlimitedStacksCapturing(this.#globalObject);
186       this.#realmAutomationFeaturesEnabled = false;
187     }
189     this.#globalObjectReference = null;
190     this.#globalObject = null;
191     this.#window = null;
193     super.destroy();
194   }
196   get browsingContext() {
197     return this.#window.browsingContext;
198   }
200   get globalObjectReference() {
201     return this.#globalObjectReference;
202   }
204   get isSandbox() {
205     return this.#isSandbox;
206   }
208   get origin() {
209     return this.#window.origin;
210   }
212   get userActivationEnabled() {
213     return this.#userActivationEnabled;
214   }
216   set userActivationEnabled(enable) {
217     if (enable === this.#userActivationEnabled) {
218       return;
219     }
221     const document = this.#window.document;
222     if (enable) {
223       document.notifyUserGestureActivation();
224     } else {
225       document.clearUserGestureActivation();
226     }
228     this.#userActivationEnabled = enable;
229   }
231   #createDebuggerObject(obj) {
232     return this.#globalObjectReference.makeDebuggeeValue(obj);
233   }
235   #createSandbox() {
236     const win = this.#window;
237     const opts = {
238       sameZoneAs: win,
239       sandboxPrototype: win,
240       wantComponents: false,
241       wantXrays: true,
242     };
244     return new Cu.Sandbox(win, opts);
245   }
247   #enableRealmAutomationFeatures() {
248     if (!this.#realmAutomationFeaturesEnabled) {
249       lazy.dbg.enableAsyncStack(this.#globalObject);
250       lazy.dbg.enableUnlimitedStacksCapturing(this.#globalObject);
251       this.#realmAutomationFeaturesEnabled = true;
252     }
253   }
255   /**
256    * Clone the provided object into the scope of this Realm (either a window
257    * global, or a sandbox).
258    *
259    * @param {object} obj
260    *     Any non-primitive object.
261    *
262    * @returns {object}
263    *     The cloned object.
264    */
265   cloneIntoRealm(obj) {
266     return Cu.cloneInto(obj, this.#globalObject, { cloneFunctions: true });
267   }
269   /**
270    * Evaluates a provided expression in the context of the current realm.
271    *
272    * @param {string} expression
273    *     The expression to evaluate.
274    *
275    * @returns {object}
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".
281    */
282   executeInGlobal(expression) {
283     this.#enableRealmAutomationFeatures();
284     return this.#globalObjectReference.executeInGlobal(expression, {
285       url: this.#window.document.baseURI,
286     });
287   }
289   /**
290    * Call a function in the context of the current realm.
291    *
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.
298    *
299    * @returns {object}
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".
305    */
306   executeInGlobalWithBindings(
307     functionDeclaration,
308     functionArguments,
309     thisParameter
310   ) {
311     this.#enableRealmAutomationFeatures();
312     const expression = `(${functionDeclaration}).apply(__bidi_this, __bidi_args)`;
314     const args = this.cloneIntoRealm([]);
315     for (const arg of functionArguments) {
316       args.push(arg);
317     }
319     return this.#globalObjectReference.executeInGlobalWithBindings(
320       expression,
321       {
322         __bidi_args: this.#createDebuggerObject(args),
323         __bidi_this: this.#createDebuggerObject(thisParameter),
324       },
325       {
326         url: this.#window.document.baseURI,
327       }
328     );
329   }
331   /**
332    * Get the realm information.
333    *
334    * @returns {object}
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.
340    */
341   getInfo() {
342     const baseInfo = super.getInfo();
343     const info = {
344       ...baseInfo,
345       context: this.#window.browsingContext,
346       type: WindowRealm.type,
347     };
349     if (this.#isSandbox) {
350       info.sandbox = this.#sandboxName;
351     }
353     return info;
354   }
356   /**
357    * Log an error caused by a script evaluation.
358    *
359    * @param {string} message
360    *     The error message.
361    * @param {Stack} stack
362    *     The JavaScript stack trace.
363    */
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(
371       message,
372       this.#window.document.baseURI,
373       sourceLine,
374       line,
375       column,
376       Ci.nsIScriptError.errorFlag,
377       "content javascript",
378       this.#window.windowGlobalChild.innerWindowId
379     );
380     Services.console.logMessage(scriptError);
381   }