tor-browser

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

content_script.sys.mjs (6506B)


      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 import { ContentProcessWatcherRegistry } from "resource://devtools/server/connectors/js-process-actor/ContentProcessWatcherRegistry.sys.mjs";
      6 
      7 import { addDebuggerToGlobal } from "resource://gre/modules/jsdebugger.sys.mjs";
      8 // This will inject `Debugger` in the global scope
      9 // eslint-disable-next-line mozilla/reject-globalThis-modification
     10 addDebuggerToGlobal(globalThis);
     11 
     12 const lazy = {};
     13 
     14 const EXTENSION_CONTENT_SYS_MJS =
     15  "resource://gre/modules/ExtensionContent.sys.mjs";
     16 
     17 // Do not import Targets/index.js to prevent having to load DevTools module loader.
     18 const CONTENT_SCRIPT = "content_script";
     19 
     20 ChromeUtils.defineESModuleGetters(
     21  lazy,
     22  {
     23    // ExtensionContent.sys.mjs is a singleton and must be loaded through the
     24    // main loader. Note that the user of lazy.ExtensionContent elsewhere in
     25    // this file (at webextensionsContentScriptGlobals) looks up the module
     26    // via Cu.isESModuleLoaded, which also uses the main loader as desired.
     27    ExtensionContent: EXTENSION_CONTENT_SYS_MJS,
     28  },
     29  { global: "shared" }
     30 );
     31 
     32 let gDbg = null;
     33 
     34 function watch() {
     35  // Listen for new globals via Spidermonkey Debugger API
     36  // in order to catch new Content Script sandboxes created later on
     37  gDbg = new Debugger();
     38  gDbg.onNewGlobalObject = onNewGlobal;
     39 
     40  // Also listen to this event emitted by ExtensionContent to know
     41  // when some Content Script sandboxes are destroyed.
     42  // It happens when a page navigates/reload and also on add-on disabling.
     43  Services.obs.addObserver(observe, "content-script-destroyed");
     44 }
     45 
     46 function unwatch() {
     47  gDbg.onNewGlobalObject = undefined;
     48  gDbg = null;
     49 
     50  Services.obs.removeObserver(observe, "content-script-destroyed");
     51 }
     52 
     53 /**
     54 * Called whenever a new global is instantiated in the current process
     55 *
     56 * @param {Debugger.Object} global
     57 */
     58 function onNewGlobal(global) {
     59  // Content scripts are only using Sandboxes as global.
     60  if (global.class != "Sandbox") {
     61    return;
     62  }
     63 
     64  // WebExtension codebase will flag its sandboxes with a browser-id attribute
     65  // in order to inform which tab they are injected against.
     66  const contentScriptSandbox = global.unsafeDereference();
     67  const metadata = Cu.getSandboxMetadata(contentScriptSandbox);
     68  if (!metadata) {
     69    return;
     70  }
     71  const sandboxBrowserId = metadata["browser-id"];
     72  if (!sandboxBrowserId) {
     73    return;
     74  }
     75 
     76  for (const watcherDataObject of ContentProcessWatcherRegistry.getAllWatchersDataObjects(
     77    CONTENT_SCRIPT
     78  )) {
     79    const { sessionContext } = watcherDataObject;
     80    const { browserId, type } = sessionContext;
     81    // Accept all the content scripts if we are debugging everything (session context type == all)
     82    if (type != "all" && browserId != sandboxBrowserId) {
     83      continue;
     84    }
     85    createContentScriptTargetActor(watcherDataObject, {
     86      sessionContext,
     87      contentScriptSandbox,
     88    });
     89  }
     90 }
     91 
     92 /**
     93 * Listen to an Observer Service notification emitted by ExtensionContent
     94 * when any content script sandbox is destroyed.
     95 */
     96 function observe(sandbox, topic) {
     97  if (topic != "content-script-destroyed") {
     98    return;
     99  }
    100 
    101  for (const watcherDataObject of ContentProcessWatcherRegistry.getAllWatchersDataObjects(
    102    CONTENT_SCRIPT
    103  )) {
    104    const targetActor = watcherDataObject.actors.find(
    105      actor => actor.contentScriptSandbox === sandbox
    106    );
    107    if (!targetActor) {
    108      continue;
    109    }
    110 
    111    ContentProcessWatcherRegistry.destroyTargetActor(
    112      watcherDataObject,
    113      targetActor,
    114      {}
    115    );
    116  }
    117 }
    118 
    119 function createTargetsForWatcher(watcherDataObject, _isProcessActorStartup) {
    120  // Ignore this process if there is no extension activity in it
    121  if (!Cu.isESModuleLoaded(EXTENSION_CONTENT_SYS_MJS)) {
    122    return;
    123  }
    124 
    125  const { sessionContext } = watcherDataObject;
    126  // Only try spawning Content Script targets when debugging tabs or everything.
    127  if (
    128    sessionContext.type != "browser-element" &&
    129    sessionContext.type != "all"
    130  ) {
    131    return;
    132  }
    133  const { browserId, type } = sessionContext;
    134 
    135  const sandboxes = lazy.ExtensionContent.getAllContentScriptGlobals();
    136  for (const contentScriptSandbox of sandboxes) {
    137    if (!contentScriptSandbox || Cu.isDeadWrapper(contentScriptSandbox)) {
    138      // Bug 1979448: Skip invalid or dead sandboxes.
    139      continue;
    140    }
    141 
    142    const metadata = Cu.getSandboxMetadata(contentScriptSandbox);
    143    // Accept all the content scripts if we are debugging everything (session context type == all)
    144    //
    145    // Ignore sandboxes without metadata which are related to the hack
    146    // of bug 1214658, which spawns content script sandboxes in order
    147    // to expose chrome/browser API to iframes loading extension documents.
    148    if (type != "all" && (!metadata || metadata["browser-id"] != browserId)) {
    149      continue;
    150    }
    151    createContentScriptTargetActor(watcherDataObject, {
    152      sessionContext,
    153      contentScriptSandbox,
    154    });
    155  }
    156 }
    157 
    158 function destroyTargetsForWatcher(watcherDataObject, options) {
    159  // Unregister and destroy the existing target actors for this target type
    160  const actorsToDestroy = watcherDataObject.actors.filter(
    161    actor => actor.targetType == CONTENT_SCRIPT
    162  );
    163  watcherDataObject.actors = watcherDataObject.actors.filter(
    164    actor => actor.targetType != CONTENT_SCRIPT
    165  );
    166 
    167  for (const actor of actorsToDestroy) {
    168    ContentProcessWatcherRegistry.destroyTargetActor(
    169      watcherDataObject,
    170      actor,
    171      options
    172    );
    173  }
    174 }
    175 
    176 /**
    177 * Instantiate a ContentScript target actor for a given content script sandbox
    178 * and a given watcher actor.
    179 *
    180 * @param {object} watcherDataObject
    181 * @param {Sandbox} sandbox
    182 */
    183 function createContentScriptTargetActor(watcherDataObject, sandbox) {
    184  const { connection, loader } =
    185    ContentProcessWatcherRegistry.getOrCreateConnectionForWatcher(
    186      watcherDataObject.watcherActorID,
    187      false
    188    );
    189 
    190  const { WebExtensionContentScriptTargetActor } = loader.require(
    191    "devtools/server/actors/targets/content-script"
    192  );
    193 
    194  // Create the actual target actor.
    195  const targetActor = new WebExtensionContentScriptTargetActor(
    196    connection,
    197    sandbox
    198  );
    199 
    200  ContentProcessWatcherRegistry.onNewTargetActor(
    201    watcherDataObject,
    202    targetActor,
    203    false
    204  );
    205 }
    206 
    207 export const ContentScriptTargetWatcher = {
    208  watch,
    209  unwatch,
    210  createTargetsForWatcher,
    211  destroyTargetsForWatcher,
    212 };