tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Realm.sys.mjs (9661B)


      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/. */
      4 
      5 const lazy = {};
      6 ChromeUtils.defineESModuleGetters(lazy, {
      7  addDebuggerToGlobal: "resource://gre/modules/jsdebugger.sys.mjs",
      8 
      9  generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
     10 });
     11 
     12 ChromeUtils.defineLazyGetter(lazy, "dbg", () => {
     13  // eslint-disable-next-line mozilla/reject-globalThis-modification
     14  lazy.addDebuggerToGlobal(globalThis);
     15  return new Debugger();
     16 });
     17 
     18 /**
     19 * @typedef {string} RealmType
     20 */
     21 
     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",
     37 };
     38 
     39 /**
     40 * Base class that wraps any kind of WebDriver BiDi realm.
     41 */
     42 export class Realm {
     43  #handleObjectMap;
     44  #id;
     45 
     46  constructor() {
     47    this.#id = lazy.generateUUID();
     48 
     49    // Map of unique handles (UUIDs) to objects belonging to this realm.
     50    this.#handleObjectMap = new Map();
     51  }
     52 
     53  destroy() {
     54    this.#handleObjectMap = null;
     55  }
     56 
     57  /**
     58   * Get the browsing context of the realm instance.
     59   */
     60  get browsingContext() {
     61    return null;
     62  }
     63 
     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  }
     72 
     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  }
     81 
     82  /**
     83   * Ensure the provided object can be used within this realm.
     84 
     85   * @param {object} obj
     86   *     Any non-primitive object.
     87 
     88   * @returns {object}
     89   *     An object usable in the current realm.
     90   */
     91  cloneIntoRealm(obj) {
     92    return obj;
     93  }
     94 
     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  }
    104 
    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  }
    118 
    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  }
    130 
    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  }
    142 }
    143 
    144 /**
    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;
    155 
    156  static type = RealmType.Window;
    157 
    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;
    168 
    169    super();
    170 
    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  }
    181 
    182  destroy() {
    183    if (this.#realmAutomationFeaturesEnabled) {
    184      lazy.dbg.disableAsyncStack(this.#globalObject);
    185      lazy.dbg.disableUnlimitedStacksCapturing(this.#globalObject);
    186      this.#realmAutomationFeaturesEnabled = false;
    187    }
    188 
    189    this.#globalObjectReference = null;
    190    this.#globalObject = null;
    191    this.#window = null;
    192 
    193    super.destroy();
    194  }
    195 
    196  get browsingContext() {
    197    return this.#window.browsingContext;
    198  }
    199 
    200  get globalObject() {
    201    return this.#globalObject;
    202  }
    203 
    204  get globalObjectReference() {
    205    return this.#globalObjectReference;
    206  }
    207 
    208  get isSandbox() {
    209    return this.#isSandbox;
    210  }
    211 
    212  get origin() {
    213    return this.#window.origin;
    214  }
    215 
    216  get userActivationEnabled() {
    217    return this.#userActivationEnabled;
    218  }
    219 
    220  set userActivationEnabled(enable) {
    221    if (enable === this.#userActivationEnabled) {
    222      return;
    223    }
    224 
    225    const document = this.#window.document;
    226    if (enable) {
    227      document.notifyUserGestureActivation();
    228    } else {
    229      document.clearUserGestureActivation();
    230    }
    231 
    232    this.#userActivationEnabled = enable;
    233  }
    234 
    235  #createDebuggerObject(obj) {
    236    return this.#globalObjectReference.makeDebuggeeValue(obj);
    237  }
    238 
    239  #createSandbox() {
    240    const win = this.#window;
    241    const opts = {
    242      sameZoneAs: win,
    243      sandboxPrototype: win,
    244      wantComponents: false,
    245      wantXrays: true,
    246    };
    247 
    248    return new Cu.Sandbox(win, opts);
    249  }
    250 
    251  #enableRealmAutomationFeatures() {
    252    if (!this.#realmAutomationFeaturesEnabled) {
    253      lazy.dbg.enableAsyncStack(this.#globalObject);
    254      lazy.dbg.enableUnlimitedStacksCapturing(this.#globalObject);
    255      this.#realmAutomationFeaturesEnabled = true;
    256    }
    257  }
    258 
    259  /**
    260   * Clone the provided object into the scope of this Realm (either a window
    261   * global, or a sandbox).
    262   *
    263   * @param {object} obj
    264   *     Any non-primitive object.
    265   *
    266   * @returns {object}
    267   *     The cloned object.
    268   */
    269  cloneIntoRealm(obj) {
    270    return Cu.cloneInto(obj, this.#globalObject, { cloneFunctions: true });
    271  }
    272 
    273  /**
    274   * Evaluates a provided expression in the context of the current realm.
    275   *
    276   * @param {string} expression
    277   *     The expression to evaluate.
    278   *
    279   * @returns {object}
    280   *     - evaluationStatus {EvaluationStatus} One of "normal", "throw".
    281   *     - exceptionDetails {ExceptionDetails=} the details of the exception if
    282   *       the evaluation status was "throw".
    283   *     - result {RemoteValue=} the result of the evaluation serialized as a
    284   *       RemoteValue if the evaluation status was "normal".
    285   */
    286  executeInGlobal(expression) {
    287    this.#enableRealmAutomationFeatures();
    288    return this.#globalObjectReference.executeInGlobal(expression, {
    289      bypassCSP: true,
    290      url: this.#window.document.baseURI,
    291    });
    292  }
    293 
    294  /**
    295   * Call a function in the context of the current realm.
    296   *
    297   * @param {string} functionDeclaration
    298   *     The body of the function to call.
    299   * @param {Array<object>} functionArguments
    300   *     The arguments to pass to the function call.
    301   * @param {object} thisParameter
    302   *     The value of the `this` keyword for the function call.
    303   *
    304   * @returns {object}
    305   *     - evaluationStatus {EvaluationStatus} One of "normal", "throw".
    306   *     - exceptionDetails {ExceptionDetails=} the details of the exception if
    307   *       the evaluation status was "throw".
    308   *     - result {RemoteValue=} the result of the evaluation serialized as a
    309   *       RemoteValue if the evaluation status was "normal".
    310   */
    311  executeInGlobalWithBindings(
    312    functionDeclaration,
    313    functionArguments,
    314    thisParameter
    315  ) {
    316    this.#enableRealmAutomationFeatures();
    317    const expression = `(${functionDeclaration}).apply(__bidi_this, __bidi_args)`;
    318 
    319    const args = this.cloneIntoRealm([]);
    320    for (const arg of functionArguments) {
    321      args.push(arg);
    322    }
    323 
    324    return this.#globalObjectReference.executeInGlobalWithBindings(
    325      expression,
    326      {
    327        __bidi_args: this.#createDebuggerObject(args),
    328        __bidi_this: this.#createDebuggerObject(thisParameter),
    329      },
    330      {
    331        bypassCSP: true,
    332        url: this.#window.document.baseURI,
    333      }
    334    );
    335  }
    336 
    337  /**
    338   * Get the realm information.
    339   *
    340   * @returns {object}
    341   *     - context {BrowsingContext} The browsing context, associated with the realm.
    342   *     - id {string} The realm unique identifier.
    343   *     - origin {string} The serialization of an origin.
    344   *     - sandbox {string=} The name of the sandbox.
    345   *     - type {RealmType.Window} The window realm type.
    346   */
    347  getInfo() {
    348    const baseInfo = super.getInfo();
    349    const info = {
    350      ...baseInfo,
    351      context: this.#window.browsingContext,
    352      type: WindowRealm.type,
    353    };
    354 
    355    if (this.#isSandbox) {
    356      info.sandbox = this.#sandboxName;
    357    }
    358 
    359    return info;
    360  }
    361 
    362  /**
    363   * Log an error caused by a script evaluation.
    364   *
    365   * @param {string} message
    366   *     The error message.
    367   * @param {Stack} stack
    368   *     The JavaScript stack trace.
    369   */
    370  reportError(message, stack) {
    371    const { column, line } = stack;
    372 
    373    const scriptErrorClass = Cc["@mozilla.org/scripterror;1"];
    374    const scriptError = scriptErrorClass.createInstance(Ci.nsIScriptError);
    375 
    376    scriptError.initWithWindowID(
    377      message,
    378      this.#window.document.baseURI,
    379      line,
    380      column,
    381      Ci.nsIScriptError.errorFlag,
    382      "content javascript",
    383      this.#window.windowGlobalChild.innerWindowId
    384    );
    385    Services.console.logMessage(scriptError);
    386  }
    387 }