tor-browser

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

index.js (20071B)


      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 const Targets = require("resource://devtools/server/actors/targets/index.js");
      8 
      9 const TYPES = {
     10  CONSOLE_MESSAGE: "console-message",
     11  CSS_CHANGE: "css-change",
     12  CSS_MESSAGE: "css-message",
     13  CSS_REGISTERED_PROPERTIES: "css-registered-properties",
     14  DOCUMENT_EVENT: "document-event",
     15  ERROR_MESSAGE: "error-message",
     16  LAST_PRIVATE_CONTEXT_EXIT: "last-private-context-exit",
     17  NETWORK_EVENT: "network-event",
     18  NETWORK_EVENT_STACKTRACE: "network-event-stacktrace",
     19  PLATFORM_MESSAGE: "platform-message",
     20  REFLOW: "reflow",
     21  SERVER_SENT_EVENT: "server-sent-event",
     22  SOURCE: "source",
     23  STYLESHEET: "stylesheet",
     24  THREAD_STATE: "thread-state",
     25  JSTRACER_TRACE: "jstracer-trace",
     26  JSTRACER_STATE: "jstracer-state",
     27  WEBSOCKET: "websocket",
     28  WEBTRANSPORT: "webtransport",
     29 
     30  // storage types
     31  CACHE_STORAGE: "Cache",
     32  COOKIE: "cookies",
     33  EXTENSION_STORAGE: "extension-storage",
     34  INDEXED_DB: "indexed-db",
     35  LOCAL_STORAGE: "local-storage",
     36  SESSION_STORAGE: "session-storage",
     37 
     38  // root types
     39  EXTENSIONS_BGSCRIPT_STATUS: "extensions-backgroundscript-status",
     40 };
     41 exports.TYPES = TYPES;
     42 
     43 // Helper dictionaries, which will contain data specific to each resource type.
     44 // - `path` is the absolute path to the module defining the Resource Watcher class.
     45 //
     46 // Also see the attributes added by `augmentResourceDictionary` for each type:
     47 // - `watchers` is a weak map which will store Resource Watchers
     48 //    (i.e. devtools/server/actors/resources/ class instances)
     49 //    keyed by target actor -or- watcher actor.
     50 // - `WatcherClass` is a shortcut to the Resource Watcher module.
     51 //    Each module exports a Resource Watcher class.
     52 //
     53 // These are several dictionaries, which depend how the resource watcher classes are instantiated.
     54 
     55 // Frame target resources are spawned via a BrowsingContext Target Actor.
     56 // Their watcher class receives a target actor as first argument.
     57 // They are instantiated for each observed BrowsingContext, from the content process where it runs.
     58 // They are meant to observe all resources related to a given Browsing Context.
     59 const FrameTargetResources = augmentResourceDictionary({
     60  [TYPES.CACHE_STORAGE]: {
     61    path: "devtools/server/actors/resources/storage-cache",
     62  },
     63  [TYPES.CONSOLE_MESSAGE]: {
     64    path: "devtools/server/actors/resources/console-messages",
     65  },
     66  [TYPES.CSS_CHANGE]: {
     67    path: "devtools/server/actors/resources/css-changes",
     68  },
     69  [TYPES.CSS_MESSAGE]: {
     70    path: "devtools/server/actors/resources/css-messages",
     71  },
     72  [TYPES.CSS_REGISTERED_PROPERTIES]: {
     73    path: "devtools/server/actors/resources/css-registered-properties",
     74  },
     75  [TYPES.DOCUMENT_EVENT]: {
     76    path: "devtools/server/actors/resources/document-event",
     77  },
     78  [TYPES.ERROR_MESSAGE]: {
     79    path: "devtools/server/actors/resources/error-messages",
     80  },
     81  [TYPES.JSTRACER_STATE]: {
     82    path: "devtools/server/actors/resources/jstracer-state",
     83  },
     84  [TYPES.JSTRACER_TRACE]: {
     85    path: "devtools/server/actors/resources/jstracer-trace",
     86  },
     87  [TYPES.LOCAL_STORAGE]: {
     88    path: "devtools/server/actors/resources/storage-local-storage",
     89  },
     90  [TYPES.PLATFORM_MESSAGE]: {
     91    path: "devtools/server/actors/resources/platform-messages",
     92  },
     93  [TYPES.SESSION_STORAGE]: {
     94    path: "devtools/server/actors/resources/storage-session-storage",
     95  },
     96  [TYPES.STYLESHEET]: {
     97    path: "devtools/server/actors/resources/stylesheets",
     98  },
     99  [TYPES.NETWORK_EVENT]: {
    100    path: "devtools/server/actors/resources/network-events-content",
    101  },
    102  [TYPES.NETWORK_EVENT_STACKTRACE]: {
    103    path: "devtools/server/actors/resources/network-events-stacktraces",
    104  },
    105  [TYPES.REFLOW]: {
    106    path: "devtools/server/actors/resources/reflow",
    107  },
    108  [TYPES.SOURCE]: {
    109    path: "devtools/server/actors/resources/sources",
    110  },
    111  [TYPES.THREAD_STATE]: {
    112    path: "devtools/server/actors/resources/thread-states",
    113  },
    114  [TYPES.SERVER_SENT_EVENT]: {
    115    path: "devtools/server/actors/resources/server-sent-events",
    116  },
    117  [TYPES.WEBSOCKET]: {
    118    path: "devtools/server/actors/resources/websockets",
    119  },
    120  [TYPES.WEBTRANSPORT]: {
    121    path: "devtools/server/actors/resources/webtransport",
    122  },
    123 });
    124 
    125 // Process target resources are spawned via a Process Target Actor.
    126 // Their watcher class receives a process target actor as first argument.
    127 // They are instantiated for each observed Process (parent and all content processes).
    128 // They are meant to observe all resources related to a given process.
    129 const ProcessTargetResources = augmentResourceDictionary({
    130  [TYPES.CONSOLE_MESSAGE]: {
    131    path: "devtools/server/actors/resources/console-messages",
    132  },
    133  [TYPES.JSTRACER_TRACE]: {
    134    path: "devtools/server/actors/resources/jstracer-trace",
    135  },
    136  [TYPES.JSTRACER_STATE]: {
    137    path: "devtools/server/actors/resources/jstracer-state",
    138  },
    139  [TYPES.ERROR_MESSAGE]: {
    140    path: "devtools/server/actors/resources/error-messages",
    141  },
    142  [TYPES.PLATFORM_MESSAGE]: {
    143    path: "devtools/server/actors/resources/platform-messages",
    144  },
    145  [TYPES.SOURCE]: {
    146    path: "devtools/server/actors/resources/sources",
    147  },
    148  [TYPES.THREAD_STATE]: {
    149    path: "devtools/server/actors/resources/thread-states",
    150  },
    151 });
    152 
    153 // Worker target resources are spawned via a Worker Target Actor.
    154 // Their watcher class receives a worker target actor as first argument.
    155 // They are instantiated for each observed worker, from the worker thread.
    156 // They are meant to observe all resources related to a given worker.
    157 //
    158 // We'll only support a few resource types in Workers (console-message, source,
    159 // thread state, …) as error and platform messages are not supported since we need access
    160 // to Ci, which isn't available in worker context.
    161 // Errors are emitted from the content process main thread so the user would still get them.
    162 const WorkerTargetResources = augmentResourceDictionary({
    163  [TYPES.CONSOLE_MESSAGE]: {
    164    path: "devtools/server/actors/resources/console-messages",
    165  },
    166  [TYPES.JSTRACER_TRACE]: {
    167    path: "devtools/server/actors/resources/jstracer-trace",
    168  },
    169  [TYPES.JSTRACER_STATE]: {
    170    path: "devtools/server/actors/resources/jstracer-state",
    171  },
    172  [TYPES.SOURCE]: {
    173    path: "devtools/server/actors/resources/sources",
    174  },
    175  [TYPES.THREAD_STATE]: {
    176    path: "devtools/server/actors/resources/thread-states",
    177  },
    178 });
    179 
    180 // Web Extension Content Script resources are spawn for each content script,
    181 // from the web page's main thread it relates to.
    182 //
    183 // Similarly to workers, it doesn't relate to any DOM Document,
    184 // but only a JavaScript context. So it only expose a subset of resources.
    185 const WebExtensionContentScriptTargetResources = augmentResourceDictionary({
    186  [TYPES.CONSOLE_MESSAGE]: {
    187    path: "devtools/server/actors/resources/console-messages",
    188  },
    189  [TYPES.JSTRACER_TRACE]: {
    190    path: "devtools/server/actors/resources/jstracer-trace",
    191  },
    192  [TYPES.JSTRACER_STATE]: {
    193    path: "devtools/server/actors/resources/jstracer-state",
    194  },
    195  [TYPES.SOURCE]: {
    196    path: "devtools/server/actors/resources/sources",
    197  },
    198  [TYPES.THREAD_STATE]: {
    199    path: "devtools/server/actors/resources/thread-states",
    200  },
    201 });
    202 
    203 // Parent process resources are spawned via the Watcher Actor.
    204 // Their watcher class receives the watcher actor as first argument.
    205 // They are instantiated once per watcher from the parent process.
    206 // They are meant to observe all resources related to a given context designated by the Watcher (and its sessionContext)
    207 // they should be observed from the parent process.
    208 const ParentProcessResources = augmentResourceDictionary({
    209  [TYPES.NETWORK_EVENT]: {
    210    path: "devtools/server/actors/resources/network-events",
    211  },
    212  [TYPES.COOKIE]: {
    213    path: "devtools/server/actors/resources/storage-cookie",
    214  },
    215  [TYPES.EXTENSION_STORAGE]: {
    216    path: "devtools/server/actors/resources/storage-extension",
    217  },
    218  [TYPES.INDEXED_DB]: {
    219    path: "devtools/server/actors/resources/storage-indexed-db",
    220  },
    221  [TYPES.DOCUMENT_EVENT]: {
    222    path: "devtools/server/actors/resources/parent-process-document-event",
    223  },
    224  [TYPES.LAST_PRIVATE_CONTEXT_EXIT]: {
    225    path: "devtools/server/actors/resources/last-private-context-exit",
    226  },
    227 });
    228 
    229 // Root resources are spawned via the Root Actor.
    230 // Their watcher class receives the root actor as first argument.
    231 // They are instantiated only once from the parent process.
    232 // They are meant to observe anything easily observable from the parent process
    233 // that isn't related to any particular context/target.
    234 // This is especially useful when you need to observe something without having to instantiate a Watcher actor.
    235 const RootResources = augmentResourceDictionary({
    236  [TYPES.EXTENSIONS_BGSCRIPT_STATUS]: {
    237    path: "devtools/server/actors/resources/extensions-backgroundscript-status",
    238  },
    239 });
    240 exports.RootResources = RootResources;
    241 
    242 function augmentResourceDictionary(dict) {
    243  for (const resource of Object.values(dict)) {
    244    resource.watchers = new WeakMap();
    245 
    246    loader.lazyRequireGetter(resource, "WatcherClass", resource.path);
    247  }
    248  return dict;
    249 }
    250 
    251 /**
    252 * For a given actor, return the related dictionary defined just before,
    253 * that contains info about how to listen for a given resource type, from a given actor.
    254 *
    255 * @param Actor rootOrWatcherOrTargetActor
    256 *        Either a RootActor or WatcherActor or a TargetActor which can be listening to a resource.
    257 */
    258 function getResourceTypeDictionary(rootOrWatcherOrTargetActor) {
    259  const { typeName } = rootOrWatcherOrTargetActor;
    260  if (typeName == "root") {
    261    return RootResources;
    262  }
    263  if (typeName == "watcher") {
    264    return ParentProcessResources;
    265  }
    266  const { targetType } = rootOrWatcherOrTargetActor;
    267  return getResourceTypeDictionaryForTargetType(targetType);
    268 }
    269 
    270 /**
    271 * For a targetType, return the related dictionary.
    272 *
    273 * @param String targetType
    274 *        A targetType string (See Targets.TYPES)
    275 */
    276 function getResourceTypeDictionaryForTargetType(targetType) {
    277  switch (targetType) {
    278    case Targets.TYPES.FRAME:
    279      return FrameTargetResources;
    280    case Targets.TYPES.PROCESS:
    281      return ProcessTargetResources;
    282    case Targets.TYPES.WORKER:
    283      return WorkerTargetResources;
    284    case Targets.TYPES.SERVICE_WORKER:
    285      return WorkerTargetResources;
    286    case Targets.TYPES.SHARED_WORKER:
    287      return WorkerTargetResources;
    288    case Targets.TYPES.CONTENT_SCRIPT:
    289      return WebExtensionContentScriptTargetResources;
    290    default:
    291      throw new Error(`Unsupported target actor typeName '${targetType}'`);
    292  }
    293 }
    294 
    295 /**
    296 * For a given actor, return the object stored in one of the previous dictionary
    297 * that contains info about how to listen for a given resource type, from a given actor.
    298 *
    299 * @param Actor rootOrWatcherOrTargetActor
    300 *        Either a RootActor or WatcherActor or a TargetActor which can be listening to a resource.
    301 * @param String resourceType
    302 *        The resource type to be observed.
    303 */
    304 function getResourceTypeEntry(rootOrWatcherOrTargetActor, resourceType) {
    305  const dict = getResourceTypeDictionary(rootOrWatcherOrTargetActor);
    306  if (!(resourceType in dict)) {
    307    throw new Error(
    308      `Unsupported resource type '${resourceType}' for ${rootOrWatcherOrTargetActor.typeName}`
    309    );
    310  }
    311  return dict[resourceType];
    312 }
    313 
    314 /**
    315 * Start watching for a new list of resource types.
    316 * This will also emit all already existing resources before resolving.
    317 *
    318 * @param Actor rootOrWatcherOrTargetActor
    319 *        Either a RootActor or WatcherActor or a TargetActor which can be listening to a resource:
    320 *        * RootActor will be used for resources observed from the parent process and aren't related to any particular
    321 *        context/descriptor. They can be observed right away when connecting to the RDP server
    322 *        without instantiating any actor other than the root actor.
    323 *        * WatcherActor will be used for resources listened from the parent process.
    324 *        * TargetActor will be used for resources listened from the content process.
    325 *        This actor:
    326 *          - defines what context to observe (browsing context, process, worker, ...)
    327 *            Via browsingContextID, windows, docShells attributes for the target actor.
    328 *            Via the `sessionContext` object for the watcher actor.
    329 *            (only for Watcher and Target actors. Root actor is context-less.)
    330 *          - exposes `notifyResources` method to be notified about all the resources updates
    331 *            This method will receive two arguments:
    332 *            - {String} updateType, which can be "available", "updated", or "destroyed"
    333 *            - {Array<Object>} resources, which will be the list of resource's forms
    334 *              or special update object for "updated" scenario.
    335 * @param Array<String> resourceTypes
    336 *        List of all type of resource to listen to.
    337 */
    338 async function watchResources(rootOrWatcherOrTargetActor, resourceTypes) {
    339  // If we are given a target actor, filter out the resource types supported by the target.
    340  // When using sharedData to pass types between processes, we are passing them for all target types.
    341  const { targetType } = rootOrWatcherOrTargetActor;
    342  // Only target actors usecase will have a target type.
    343  // For Root and Watcher we process the `resourceTypes` list unfiltered.
    344  if (targetType) {
    345    resourceTypes = getResourceTypesForTargetType(resourceTypes, targetType);
    346  }
    347  const promises = [];
    348  for (const resourceType of resourceTypes) {
    349    const { watchers, WatcherClass } = getResourceTypeEntry(
    350      rootOrWatcherOrTargetActor,
    351      resourceType
    352    );
    353 
    354    // Ignore resources we're already listening to
    355    if (watchers.has(rootOrWatcherOrTargetActor)) {
    356      continue;
    357    }
    358 
    359    // Don't watch for console messages from the worker target if worker messages are still
    360    // being cloned to the main process, otherwise we'll get duplicated messages in the
    361    // console output (See Bug 1778852).
    362    if (
    363      resourceType == TYPES.CONSOLE_MESSAGE &&
    364      rootOrWatcherOrTargetActor.workerConsoleApiMessagesDispatchedToMainThread
    365    ) {
    366      continue;
    367    }
    368 
    369    const watcher = new WatcherClass();
    370    promises.push(
    371      watcher.watch(rootOrWatcherOrTargetActor, {
    372        onAvailable: rootOrWatcherOrTargetActor.notifyResources.bind(
    373          rootOrWatcherOrTargetActor,
    374          "available",
    375          resourceType
    376        ),
    377        onUpdated: rootOrWatcherOrTargetActor.notifyResources.bind(
    378          rootOrWatcherOrTargetActor,
    379          "updated",
    380          resourceType
    381        ),
    382        onDestroyed: rootOrWatcherOrTargetActor.notifyResources.bind(
    383          rootOrWatcherOrTargetActor,
    384          "destroyed",
    385          resourceType
    386        ),
    387      })
    388    );
    389    watchers.set(rootOrWatcherOrTargetActor, watcher);
    390  }
    391  await Promise.all(promises);
    392 
    393  // Force sending resources to the client before we resolve.
    394  // So that resources are received by the client
    395  // before WatcherActor.watchResources resolves.
    396  // This is important when ResourceCommand.watchResources's `ignoreExistingResources` flag is set to false (default behavior).
    397  // The client code expects all resources to be emitted when this server method resolves.
    398  if (rootOrWatcherOrTargetActor.emitResources) {
    399    rootOrWatcherOrTargetActor.emitResources();
    400  }
    401 }
    402 exports.watchResources = watchResources;
    403 
    404 function getParentProcessResourceTypes(resourceTypes) {
    405  return resourceTypes.filter(resourceType => {
    406    return resourceType in ParentProcessResources;
    407  });
    408 }
    409 exports.getParentProcessResourceTypes = getParentProcessResourceTypes;
    410 
    411 function getResourceTypesForTargetType(resourceTypes, targetType) {
    412  const resourceDictionnary =
    413    getResourceTypeDictionaryForTargetType(targetType);
    414  return resourceTypes.filter(resourceType => {
    415    return resourceType in resourceDictionnary;
    416  });
    417 }
    418 exports.getResourceTypesForTargetType = getResourceTypesForTargetType;
    419 
    420 function hasResourceTypesForTargets(resourceTypes) {
    421  return resourceTypes.some(resourceType => {
    422    return resourceType in FrameTargetResources;
    423  });
    424 }
    425 exports.hasResourceTypesForTargets = hasResourceTypesForTargets;
    426 
    427 /**
    428 * Stop watching for a list of resource types.
    429 *
    430 * @param Actor rootOrWatcherOrTargetActor
    431 *        The related actor, already passed to watchResources.
    432 * @param Array<String> resourceTypes
    433 *        List of all type of resource to stop listening to.
    434 */
    435 function unwatchResources(rootOrWatcherOrTargetActor, resourceTypes) {
    436  // If we are given a target actor, filter out the resource types supported by the target.
    437  // When using sharedData to pass types between processes, we are passing them for all target types.
    438  const { targetType } = rootOrWatcherOrTargetActor;
    439  // Only target actors usecase will have a target type.
    440  // For Root and Watcher we process the `resourceTypes` list unfiltered.
    441  if (targetType) {
    442    resourceTypes = getResourceTypesForTargetType(resourceTypes, targetType);
    443  }
    444  for (const resourceType of resourceTypes) {
    445    // Pull all info about this resource type from `Resources` global object
    446    const { watchers } = getResourceTypeEntry(
    447      rootOrWatcherOrTargetActor,
    448      resourceType
    449    );
    450 
    451    const watcher = watchers.get(rootOrWatcherOrTargetActor);
    452    if (watcher) {
    453      watcher.destroy();
    454      watchers.delete(rootOrWatcherOrTargetActor);
    455    }
    456  }
    457 }
    458 exports.unwatchResources = unwatchResources;
    459 
    460 /**
    461 * Clear resources for a list of resource types.
    462 *
    463 * @param Actor rootOrWatcherOrTargetActor
    464 *        The related actor, already passed to watchResources.
    465 * @param Array<String> resourceTypes
    466 *        List of all type of resource to clear.
    467 */
    468 function clearResources(rootOrWatcherOrTargetActor, resourceTypes) {
    469  // If we are given a target actor, filter out the resource types supported by the target.
    470  // When using sharedData to pass types between processes, we are passing them for all target types.
    471  const { targetType } = rootOrWatcherOrTargetActor;
    472  // Only target actors usecase will have a target type.
    473  // For Root and Watcher we process the `resourceTypes` list unfiltered.
    474  if (targetType) {
    475    resourceTypes = getResourceTypesForTargetType(resourceTypes, targetType);
    476  }
    477  for (const resourceType of resourceTypes) {
    478    const { watchers } = getResourceTypeEntry(
    479      rootOrWatcherOrTargetActor,
    480      resourceType
    481    );
    482 
    483    const watcher = watchers.get(rootOrWatcherOrTargetActor);
    484    if (watcher && typeof watcher.clear == "function") {
    485      watcher.clear();
    486    }
    487  }
    488 }
    489 
    490 exports.clearResources = clearResources;
    491 
    492 /**
    493 * Stop watching for all watched resources on a given actor.
    494 *
    495 * @param Actor rootOrWatcherOrTargetActor
    496 *        The related actor, already passed to watchResources.
    497 */
    498 function unwatchAllResources(rootOrWatcherOrTargetActor) {
    499  for (const { watchers } of Object.values(
    500    getResourceTypeDictionary(rootOrWatcherOrTargetActor)
    501  )) {
    502    const watcher = watchers.get(rootOrWatcherOrTargetActor);
    503    if (watcher) {
    504      watcher.destroy();
    505      watchers.delete(rootOrWatcherOrTargetActor);
    506    }
    507  }
    508 }
    509 exports.unwatchAllResources = unwatchAllResources;
    510 
    511 /**
    512 * If we are watching for the given resource type,
    513 * return the current ResourceWatcher instance used by this target actor
    514 * in order to observe this resource type.
    515 *
    516 * @param Actor watcherOrTargetActor
    517 *        Either a WatcherActor or a TargetActor which can be listening to a resource.
    518 *        WatcherActor will be used for resources listened from the parent process,
    519 *        and TargetActor will be used for resources listened from the content process.
    520 * @param String resourceType
    521 *        The resource type to query
    522 * @return ResourceWatcher
    523 *         The resource watcher instance, defined in devtools/server/actors/resources/
    524 */
    525 function getResourceWatcher(watcherOrTargetActor, resourceType) {
    526  const { watchers } = getResourceTypeEntry(watcherOrTargetActor, resourceType);
    527 
    528  return watchers.get(watcherOrTargetActor);
    529 }
    530 exports.getResourceWatcher = getResourceWatcher;