tor-browser

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

Loader.sys.mjs (7060B)


      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 /**
      6 * Manages the base loader (base-loader.sys.mjs) instance used to load the developer tools.
      7 */
      8 
      9 import {
     10  Loader,
     11  Require,
     12  resolveURI,
     13  unload,
     14 } from "resource://devtools/shared/loader/base-loader.sys.mjs";
     15 import { requireRawId } from "resource://devtools/shared/loader/loader-plugin-raw.sys.mjs";
     16 
     17 export const DEFAULT_SANDBOX_NAME = "DevTools (Module loader)";
     18 
     19 var gNextLoaderID = 0;
     20 
     21 /**
     22 * The main devtools API. The standard instance of this loader is exported as
     23 * |loader| below, but if a fresh copy of the loader is needed, then a new
     24 * one can also be created.
     25 *
     26 
     27 */
     28 export class DevToolsLoader {
     29  /**
     30   * The two following boolean flags are used to control the sandboxes into
     31   * which the modules are loaded.
     32   *
     33   * @param {object} options
     34   * @param {boolean} options.freshCompartment
     35   *        If true, the modules will be forced to be loaded in a distinct
     36   *        compartment. It is typically used to load the modules in a distinct
     37   *        system compartment, different from the main one, which is shared by
     38   *        all ESMs, XPCOMs and modules loaded with this flag set to true.
     39   *        We use this in order to debug modules loaded in this shared system
     40   *        compartment. The debugger actor has to be running in a distinct
     41   *        compartment than the context it is debugging.
     42   * @param {boolean} options.useDevToolsLoaderGlobal
     43   *        If true, the loader will reuse the current global to load other
     44   *        modules instead of creating a sandbox with custom options. Cannot be
     45   *        used with freshCompartment.
     46   */
     47 
     48  constructor({
     49    freshCompartment = false,
     50    useDevToolsLoaderGlobal = false,
     51  } = {}) {
     52    if (useDevToolsLoaderGlobal && freshCompartment) {
     53      throw new Error(
     54        "Loader cannot use freshCompartment if useDevToolsLoaderGlobal is true"
     55      );
     56    }
     57 
     58    const paths = {
     59      // This resource:// URI is only registered when running DAMP tests.
     60      // This is done by: testing/talos/talos/tests/devtools/addon/api.js
     61      "damp-test": "resource://damp-test/content",
     62      // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
     63      devtools: "resource://devtools",
     64      // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
     65      // Allow access to xpcshell test items from the loader.
     66      "xpcshell-test": "resource://test",
     67 
     68      // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
     69      // Allow access to locale data using paths closer to what is
     70      // used in the source tree.
     71      "devtools/client/locales": "chrome://devtools/locale",
     72      "devtools/shared/locales": "chrome://devtools-shared/locale",
     73      "devtools/startup/locales": "chrome://devtools-startup/locale",
     74      "toolkit/locales": "chrome://global/locale",
     75    };
     76 
     77    // In case the Loader ESM is loaded in DevTools global,
     78    // also reuse this global for all CommonJS modules.
     79    const sharedGlobal =
     80      useDevToolsLoaderGlobal ||
     81      // eslint-disable-next-line mozilla/reject-globalThis-modification
     82      Cu.getRealmLocation(globalThis) == "DevTools global"
     83        ? Cu.getGlobalForObject({})
     84        : undefined;
     85    this.loader = new Loader({
     86      paths,
     87      sharedGlobal,
     88      freshCompartment,
     89      sandboxName: useDevToolsLoaderGlobal
     90        ? "DevTools (Server Module Loader)"
     91        : DEFAULT_SANDBOX_NAME,
     92      // Make sure `define` function exists. JSON Viewer needs modules in AMD
     93      // format, as it currently uses RequireJS from a content document and
     94      // can't access our usual loaders. So, any modules shared with the JSON
     95      // Viewer should include a define wrapper:
     96      //
     97      //   // Make this available to both AMD and CJS environments
     98      //   define(function(require, exports, module) {
     99      //     ... code ...
    100      //   });
    101      //
    102      // Bug 1248830 will work out a better plan here for our content module
    103      // loading needs, especially as we head towards devtools.html.
    104      supportAMDModules: true,
    105      requireHook: (id, require) => {
    106        if (id.startsWith("raw!") || id.startsWith("theme-loader!")) {
    107          return requireRawId(id, require);
    108        }
    109        return require(id);
    110      },
    111    });
    112 
    113    this.require = Require(this.loader, { id: "devtools" });
    114 
    115    // Various globals are available from ESM, but not from sandboxes,
    116    // inject them into the globals list.
    117    // Changes here should be mirrored to devtools/.eslintrc.
    118    const injectedGlobals = {
    119      BrowsingContext,
    120      CanonicalBrowsingContext,
    121      ChromeWorker,
    122      console,
    123      DebuggerNotificationObserver,
    124      DOMPoint,
    125      DOMQuad,
    126      DOMRect,
    127      fetch,
    128      Glean,
    129      HeapSnapshot,
    130      IOUtils,
    131      L10nRegistry,
    132      Localization,
    133      NamedNodeMap,
    134      NodeFilter,
    135      PathUtils,
    136      Services,
    137      StructuredCloneHolder,
    138      WebExtensionPolicy,
    139      WebSocket,
    140      WindowGlobalChild,
    141      WindowGlobalParent,
    142    };
    143    for (const name in injectedGlobals) {
    144      this.loader.globals[name] = injectedGlobals[name];
    145    }
    146 
    147    // Fetch custom pseudo modules and globals
    148    const { modules, globals } = this.require(
    149      "resource://devtools/shared/loader/builtin-modules.js"
    150    );
    151 
    152    // Register custom pseudo modules to the current loader instance
    153    for (const id in modules) {
    154      const uri = resolveURI(id, this.loader.mapping);
    155      this.loader.modules[uri] = {
    156        get exports() {
    157          return modules[id];
    158        },
    159      };
    160    }
    161 
    162    // Register custom globals to the current loader instance
    163    Object.defineProperties(
    164      this.loader.sharedGlobal,
    165      Object.getOwnPropertyDescriptors(globals)
    166    );
    167 
    168    // Define the loader id for these two usecases:
    169    // * access via the ESM (this.id)
    170    // let { loader } = ChromeUtils.importESModule("resource://devtools/shared/loader/Loader.sys.mjs");
    171    // loader.id
    172    this.id = gNextLoaderID++;
    173    // * access via module's `loader` global
    174    // loader.id
    175    globals.loader.id = this.id;
    176 
    177    // Expose lazy helpers on `loader`
    178    // ie. when you use it like that from a ESM:
    179    // let { loader } = ChromeUtils.importESModule("resource://devtools/shared/loader/Loader.sys.mjs");
    180    // loader.lazyGetter(...);
    181    this.lazyGetter = globals.loader.lazyGetter;
    182    this.lazyServiceGetter = globals.loader.lazyServiceGetter;
    183    this.lazyRequireGetter = globals.loader.lazyRequireGetter;
    184  }
    185  destroy(reason = "shutdown") {
    186    unload(this.loader, reason);
    187    delete this.loader;
    188  }
    189 
    190  /**
    191   * Return true if |id| refers to something requiring help from a
    192   * loader plugin.
    193   */
    194  isLoaderPluginId(id) {
    195    return id.startsWith("raw!");
    196  }
    197 }
    198 
    199 // Export the standard instance of DevToolsLoader used by the tools.
    200 export var loader = new DevToolsLoader();
    201 
    202 export var require = loader.require;