tor-browser

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

frame.js (6718B)


      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 "use strict";
      6 
      7 /* global addEventListener */
      8 
      9 /*
     10 * Frame script that listens for requests to start a `DevToolsServer` for a frame in a
     11 * content process.  Loaded into content process frames by the main process during
     12 * frame-connector.js' connectToFrame.
     13 */
     14 
     15 try {
     16  var chromeGlobal = this;
     17 
     18  // Encapsulate in its own scope to allows loading this frame script more than once.
     19  (function () {
     20    // In most cases, we are debugging a tab in content process, without chrome
     21    // privileges. But in some tests, we are attaching to privileged document.
     22    // Because the debugger can't be running in the same compartment than its debuggee,
     23    // we have to load the server in a dedicated Loader, loading modules in a distinct compartment.
     24    // That's what DistinctSystemPrincipalLoader does.
     25    let loader,
     26      customLoader = false;
     27    if (content.document.nodePrincipal.isSystemPrincipal) {
     28      const { useDistinctSystemPrincipalLoader } = ChromeUtils.importESModule(
     29        "resource://devtools/shared/loader/DistinctSystemPrincipalLoader.sys.mjs",
     30        { global: "shared" }
     31      );
     32      loader = useDistinctSystemPrincipalLoader(chromeGlobal);
     33      customLoader = true;
     34    } else {
     35      // Otherwise, use the shared loader.
     36      loader = ChromeUtils.importESModule(
     37        "resource://devtools/shared/loader/Loader.sys.mjs"
     38      );
     39    }
     40    const { require } = loader;
     41 
     42    const DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js");
     43    const {
     44      DevToolsServer,
     45    } = require("resource://devtools/server/devtools-server.js");
     46 
     47    DevToolsServer.init();
     48    // We want a special server without any root actor and only target-scoped actors.
     49    // We are going to spawn a WindowGlobalTargetActor instance in the next few lines,
     50    // it is going to act like a root actor without being one.
     51    DevToolsServer.registerActors({ target: true });
     52 
     53    const connections = new Map();
     54 
     55    const onConnect = DevToolsUtils.makeInfallible(function (msg) {
     56      const mm = msg.target;
     57      const prefix = msg.data.prefix;
     58 
     59      // If we try to create several frame targets simultaneously, the frame script will be loaded several times.
     60      // In this case a single "debug:connect" message might be received by all the already loaded frame scripts.
     61      // Check if the DevToolsServer already knows the provided connection prefix,
     62      // because it means that another framescript instance already handled this message.
     63      // Another "debug:connect" message is guaranteed to be emitted for another prefix,
     64      // so we keep the message listener and wait for this next message.
     65      if (DevToolsServer.hasConnectionForPrefix(prefix)) {
     66        return;
     67      }
     68      removeMessageListener("debug:connect", onConnect);
     69 
     70      const conn = DevToolsServer.connectToParent(prefix, mm);
     71      connections.set(prefix, conn);
     72 
     73      const {
     74        WindowGlobalTargetActor,
     75      } = require("resource://devtools/server/actors/targets/window-global.js");
     76      const {
     77        createBrowserElementSessionContext,
     78      } = require("resource://devtools/server/actors/watcher/session-context.js");
     79 
     80      const { docShell } = chromeGlobal;
     81      // For a script loaded via loadFrameScript, the global is the content
     82      // message manager.
     83      // All WindowGlobalTarget actors created via the framescript are top-level
     84      // targets. Non top-level WindowGlobalTarget actors are all created by the
     85      // DevToolsFrameChild actor.
     86      //
     87      // createBrowserElementSessionContext only reads browserId attribute
     88      const fakeBrowserElement = {
     89        browserId: docShell.browsingContext.browserId,
     90      };
     91      const actor = new WindowGlobalTargetActor(conn, {
     92        docShell,
     93        ignoreSubFrames: false,
     94        isTopLevelTarget: true,
     95        // This is only used when server target switching is off and we create
     96        // the target from TabDescriptor. So all config attributes are false.
     97        sessionContext: createBrowserElementSessionContext(
     98          fakeBrowserElement,
     99          {}
    100        ),
    101      });
    102      actor.manage(actor);
    103 
    104      sendAsyncMessage("debug:actor", { actor: actor.form(), prefix });
    105    });
    106 
    107    addMessageListener("debug:connect", onConnect);
    108 
    109    const onDisconnect = DevToolsUtils.makeInfallible(function (msg) {
    110      const prefix = msg.data.prefix;
    111      const conn = connections.get(prefix);
    112      if (!conn) {
    113        // Several copies of this frame script can be running for a single frame since it
    114        // is loaded once for each DevTools connection to the frame.  If this disconnect
    115        // request doesn't match a connection known here, ignore it.
    116        return;
    117      }
    118 
    119      removeMessageListener("debug:disconnect", onDisconnect);
    120      // Call DevToolsServerConnection.close to destroy all child actors. It should end up
    121      // calling DevToolsServerConnection.onTransportClosed that would actually cleanup all actor
    122      // pools.
    123      conn.close();
    124      connections.delete(prefix);
    125    });
    126    addMessageListener("debug:disconnect", onDisconnect);
    127 
    128    // In non-e10s mode, the "debug:disconnect" message isn't always received before the
    129    // messageManager connection goes away.  Watching for "unload" here ensures we close
    130    // any connections when the frame is unloaded.
    131    addEventListener("unload", () => {
    132      for (const conn of connections.values()) {
    133        conn.close();
    134      }
    135      connections.clear();
    136    });
    137 
    138    // Destroy the server once its last connection closes. Note that multiple frame
    139    // scripts may be running in parallel and reuse the same server.
    140    function destroyLoader() {
    141      // Only destroy the server if there is no more connections to it. It may be used
    142      // to debug another tab running in the same process.
    143      if (DevToolsServer.hasConnection() || DevToolsServer.keepAlive) {
    144        return;
    145      }
    146      DevToolsServer.off("connectionchange", destroyLoader);
    147 
    148      // When debugging chrome pages, we initialized a dedicated loader, also destroy it
    149      if (customLoader) {
    150        const { releaseDistinctSystemPrincipalLoader } =
    151          ChromeUtils.importESModule(
    152            "resource://devtools/shared/loader/DistinctSystemPrincipalLoader.sys.mjs",
    153            { global: "shared" }
    154          );
    155        releaseDistinctSystemPrincipalLoader(chromeGlobal);
    156      }
    157    }
    158    DevToolsServer.on("connectionchange", destroyLoader);
    159  })();
    160 } catch (e) {
    161  dump(`Exception in DevTools frame startup: ${e}\n`);
    162 }