tor-browser

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

window.js (10771B)


      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 var { loader, require } = ChromeUtils.importESModule(
      8  "resource://devtools/shared/loader/Loader.sys.mjs"
      9 );
     10 
     11 var { useDistinctSystemPrincipalLoader, releaseDistinctSystemPrincipalLoader } =
     12  ChromeUtils.importESModule(
     13    "resource://devtools/shared/loader/DistinctSystemPrincipalLoader.sys.mjs",
     14    { global: "shared" }
     15  );
     16 
     17 // Require this module to setup core modules
     18 loader.require("resource://devtools/client/framework/devtools-browser.js");
     19 
     20 var { gDevTools } = require("resource://devtools/client/framework/devtools.js");
     21 var { Toolbox } = require("resource://devtools/client/framework/toolbox.js");
     22 var {
     23  DevToolsClient,
     24 } = require("resource://devtools/client/devtools-client.js");
     25 var { PrefsHelper } = require("resource://devtools/client/shared/prefs.js");
     26 const KeyShortcuts = require("resource://devtools/client/shared/key-shortcuts.js");
     27 const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
     28 const L10N = new LocalizationHelper(
     29  "devtools/client/locales/toolbox.properties"
     30 );
     31 
     32 const lazy = {};
     33 ChromeUtils.defineESModuleGetters(lazy, {
     34  BrowserToolboxLauncher:
     35    "resource://devtools/client/framework/browser-toolbox/Launcher.sys.mjs",
     36 });
     37 
     38 const {
     39  CommandsFactory,
     40 } = require("resource://devtools/shared/commands/commands-factory.js");
     41 
     42 // Timeout to wait before we assume that a connect() timed out without an error.
     43 // In milliseconds. (With the Debugger pane open, this has been reported to last
     44 // more than 10 seconds!)
     45 const STATUS_REVEAL_TIME = 15000;
     46 
     47 /**
     48 * Shortcuts for accessing various debugger preferences.
     49 */
     50 var Prefs = new PrefsHelper("devtools.debugger", {
     51  chromeDebuggingHost: ["Char", "chrome-debugging-host"],
     52  chromeDebuggingWebSocket: ["Bool", "chrome-debugging-websocket"],
     53 });
     54 
     55 var gCommands, gToolbox, gShortcuts;
     56 
     57 function appendStatusMessage(msg) {
     58  const statusMessage = document.getElementById("status-message");
     59  statusMessage.textContent += msg + "\n";
     60  if (msg.stack) {
     61    statusMessage.textContent += msg.stack + "\n";
     62  }
     63 }
     64 
     65 function toggleStatusMessage(visible = true) {
     66  document.getElementById("status-message-container").hidden = !visible;
     67 }
     68 
     69 function revealStatusMessage() {
     70  toggleStatusMessage(true);
     71 }
     72 
     73 function hideStatusMessage() {
     74  toggleStatusMessage(false);
     75 }
     76 
     77 var connect = async function () {
     78  // Initiate the connection
     79 
     80  // MOZ_BROWSER_TOOLBOX_FORCE_MULTIPROCESS is set by the target Firefox instance
     81  // before opening the Browser Toolbox, it's set to "1" if multiprocess mode should
     82  // be forced (for example, when running mochitest with `--jsdebugger`).
     83  if (Services.env.get("MOZ_BROWSER_TOOLBOX_FORCE_MULTIPROCESS") === "1") {
     84    Services.prefs.setCharPref("devtools.browsertoolbox.scope", "everything");
     85  }
     86 
     87  const port = Services.env.get("MOZ_BROWSER_TOOLBOX_PORT");
     88 
     89  // A port needs to be passed in from the environment, for instance:
     90  //    MOZ_BROWSER_TOOLBOX_PORT=6080 ./mach run -chrome \
     91  //      chrome://devtools/content/framework/browser-toolbox/window.html
     92  if (!port) {
     93    throw new Error(
     94      "Must pass a port in an env variable with MOZ_BROWSER_TOOLBOX_PORT"
     95    );
     96  }
     97 
     98  const host = Prefs.chromeDebuggingHost;
     99  const webSocket = Prefs.chromeDebuggingWebSocket;
    100  appendStatusMessage(`Connecting to ${host}:${port}, ws: ${webSocket}`);
    101  const transport = await DevToolsClient.socketConnect({
    102    host,
    103    port,
    104    webSocket,
    105  });
    106  const client = new DevToolsClient(transport);
    107  appendStatusMessage("Start protocol client for connection");
    108  await client.connect();
    109 
    110  appendStatusMessage("Get root form for toolbox");
    111  gCommands = await CommandsFactory.forMainProcess({ client });
    112 
    113  // Bug 1794607: for some unexpected reason, closing the DevToolsClient
    114  // when the commands is destroyed by the toolbox would introduce leaks
    115  // when running the browser-toolbox mochitests.
    116  gCommands.shouldCloseClient = false;
    117 
    118  await openToolbox(gCommands);
    119 };
    120 
    121 // Certain options should be toggled since we can assume chrome debugging here
    122 function setPrefDefaults() {
    123  Services.prefs.setBoolPref("devtools.inspector.showUserAgentStyles", true);
    124  Services.prefs.setBoolPref(
    125    "devtools.inspector.showAllAnonymousContent",
    126    true
    127  );
    128  Services.prefs.setBoolPref("browser.dom.window.dump.enabled", true);
    129  Services.prefs.setBoolPref("devtools.console.stdout.chrome", true);
    130  Services.prefs.setBoolPref(
    131    "devtools.command-button-noautohide.enabled",
    132    true
    133  );
    134 
    135  // Bug 1773226: Try to avoid session restore to reopen a transient browser window
    136  // if we ever opened a URL from the browser toolbox. (but it doesn't seem to be enough)
    137  Services.prefs.setBoolPref("browser.sessionstore.resume_from_crash", false);
    138 
    139  // Disable Safe mode as the browser toolbox is often closed brutaly by subprocess
    140  // and the safe mode kicks in when reopening it
    141  Services.prefs.setIntPref("toolkit.startup.max_resumed_crashes", -1);
    142 }
    143 
    144 window.addEventListener(
    145  "load",
    146  async function () {
    147    gShortcuts = new KeyShortcuts({ window });
    148    gShortcuts.on("CmdOrCtrl+W", onCloseCommand);
    149    gShortcuts.on("CmdOrCtrl+Alt+Shift+I", onDebugBrowserToolbox);
    150    gShortcuts.on("CmdOrCtrl+Alt+R", onReloadBrowser);
    151 
    152    const statusMessageContainer = document.getElementById(
    153      "status-message-title"
    154    );
    155    statusMessageContainer.textContent = L10N.getStr(
    156      "browserToolbox.statusMessage"
    157    );
    158 
    159    setPrefDefaults();
    160 
    161    // Reveal status message if connecting is slow or if an error occurs.
    162    const delayedStatusReveal = setTimeout(
    163      revealStatusMessage,
    164      STATUS_REVEAL_TIME
    165    );
    166    try {
    167      await connect();
    168      clearTimeout(delayedStatusReveal);
    169      hideStatusMessage();
    170    } catch (e) {
    171      clearTimeout(delayedStatusReveal);
    172      appendStatusMessage(e);
    173      revealStatusMessage();
    174      console.error(e);
    175    }
    176  },
    177  { once: true }
    178 );
    179 
    180 function onCloseCommand() {
    181  window.close();
    182 }
    183 
    184 /**
    185 * Open a Browser toolbox debugging the current browser toolbox
    186 *
    187 * This helps debugging the browser toolbox code, especially the code
    188 * running in the parent process. i.e. frontend code.
    189 */
    190 function onDebugBrowserToolbox() {
    191  lazy.BrowserToolboxLauncher.init();
    192 }
    193 
    194 /**
    195 * Replicate the local-build-only key shortcut to reload the browser
    196 */
    197 function onReloadBrowser() {
    198  gToolbox.commands.targetCommand.reloadTopLevelTarget();
    199 }
    200 
    201 async function openToolbox(commands) {
    202  const form = commands.descriptorFront._form;
    203  appendStatusMessage(
    204    `Create toolbox for target descriptor: ${JSON.stringify({ form }, null, 2)}`
    205  );
    206 
    207  // Remember the last panel that was used inside of this profile.
    208  // But if we are testing, then it should always open the debugger panel.
    209  const selectedTool = Services.prefs.getCharPref(
    210    "devtools.browsertoolbox.panel",
    211    Services.prefs.getCharPref("devtools.toolbox.selectedTool", "jsdebugger")
    212  );
    213 
    214  const toolboxOptions = { doc: document };
    215  appendStatusMessage(`Show toolbox with ${selectedTool} selected`);
    216 
    217  gToolbox = await gDevTools.showToolbox(commands, {
    218    toolId: selectedTool,
    219    hostType: Toolbox.HostType.BROWSERTOOLBOX,
    220    hostOptions: toolboxOptions,
    221  });
    222 
    223  bindToolboxHandlers();
    224 
    225  // Enable some testing features if the browser toolbox test pref is set.
    226  if (
    227    Services.prefs.getBoolPref(
    228      "devtools.browsertoolbox.enable-test-server",
    229      false
    230    )
    231  ) {
    232    // setup a server so that the test can evaluate messages in this process.
    233    installTestingServer();
    234  }
    235 
    236  await gToolbox.raise();
    237 
    238  // Warn the user if we started recording this browser toolbox via MOZ_BROWSER_TOOLBOX_PROFILER_STARTUP=1
    239  if (Services.env.get("MOZ_PROFILER_STARTUP") === "1") {
    240    const notificationBox = gToolbox.getNotificationBox();
    241    const text =
    242      "The profiler started recording this toolbox, open another browser toolbox to open the profile via the performance panel";
    243    notificationBox.appendNotification(
    244      text,
    245      null,
    246      null,
    247      notificationBox.PRIORITY_INFO_HIGH
    248    );
    249  }
    250 }
    251 
    252 let releaseTestLoader = null;
    253 function installTestingServer() {
    254  // Install a DevToolsServer in this process and inform the server of its
    255  // location. Tests operating on the browser toolbox run in the server
    256  // (the firefox parent process) and can connect to this new server using
    257  // initBrowserToolboxTask(), allowing them to evaluate scripts here.
    258 
    259  const requester = {};
    260  const testLoader = useDistinctSystemPrincipalLoader(requester);
    261  releaseTestLoader = () => releaseDistinctSystemPrincipalLoader(requester);
    262  const { DevToolsServer } = testLoader.require(
    263    "resource://devtools/server/devtools-server.js"
    264  );
    265  const { SocketListener } = testLoader.require(
    266    "resource://devtools/shared/security/socket.js"
    267  );
    268 
    269  DevToolsServer.init();
    270  DevToolsServer.registerAllActors();
    271  DevToolsServer.allowChromeProcess = true;
    272 
    273  // Force this server to be kept alive until the browser toolbox process is closed.
    274  // For some reason intermittents appears on Windows when destroying the server
    275  // once the last connection drops.
    276  DevToolsServer.keepAlive = true;
    277 
    278  // Use a fixed port which initBrowserToolboxTask can look for.
    279  const socketOptions = { portOrPath: 6001 };
    280  const listener = new SocketListener(DevToolsServer, socketOptions);
    281  listener.open();
    282 }
    283 
    284 async function bindToolboxHandlers() {
    285  gToolbox.once("destroyed", quitApp);
    286  window.addEventListener("unload", onUnload);
    287 
    288  // If the remote connection drops, firefox was closed
    289  // In such case, force closing the browser toolbox
    290  gCommands.client.once("closed", quitApp);
    291 
    292  if (Services.appinfo.OS == "Darwin") {
    293    // Badge the dock icon to differentiate this process from the main application
    294    // process.
    295    updateBadgeText(false);
    296 
    297    gToolbox.on("toolbox-paused", () => updateBadgeText(true));
    298    gToolbox.on("toolbox-resumed", () => updateBadgeText(false));
    299  }
    300 }
    301 
    302 function updateBadgeText(paused) {
    303  const dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"].getService(
    304    Ci.nsIMacDockSupport
    305  );
    306  dockSupport.badgeText = paused ? "▐▐ " : " ▶";
    307 }
    308 
    309 function onUnload() {
    310  window.removeEventListener("unload", onUnload);
    311  gToolbox.destroy();
    312  if (releaseTestLoader) {
    313    releaseTestLoader();
    314    releaseTestLoader = null;
    315  }
    316 }
    317 
    318 function quitApp() {
    319  const quit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(
    320    Ci.nsISupportsPRBool
    321  );
    322  Services.obs.notifyObservers(quit, "quit-application-requested");
    323 
    324  const shouldProceed = !quit.data;
    325  if (shouldProceed) {
    326    Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
    327  }
    328 }