tor-browser

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

root.js (10669B)


      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 "use strict";
      5 
      6 const { rootSpec } = require("resource://devtools/shared/specs/root.js");
      7 const {
      8  FrontClassWithSpec,
      9  registerFront,
     10 } = require("resource://devtools/shared/protocol.js");
     11 
     12 loader.lazyRequireGetter(
     13  this,
     14  "getFront",
     15  "resource://devtools/shared/protocol.js",
     16  true
     17 );
     18 
     19 class RootFront extends FrontClassWithSpec(rootSpec) {
     20  constructor(client, targetFront, parentFront) {
     21    super(client, targetFront, parentFront);
     22 
     23    // Cache root form as this will always be the same value.
     24    Object.defineProperty(this, "rootForm", {
     25      get() {
     26        delete this.rootForm;
     27        this.rootForm = this.getRoot();
     28        return this.rootForm;
     29      },
     30      configurable: true,
     31    });
     32 
     33    // Cache of already created global scoped fronts
     34    // [typeName:string => Front instance]
     35    this.fronts = new Map();
     36 
     37    this._client = client;
     38  }
     39 
     40  form(form) {
     41    // Root Front is a special Front. It is the only one to set its actor ID manually
     42    // out of the form object returned by RootActor.sayHello which is called when calling
     43    // DevToolsClient.connect().
     44    this.actorID = form.from;
     45 
     46    this.applicationType = form.applicationType;
     47    this.traits = form.traits;
     48  }
     49  /**
     50   * Retrieve all service worker registrations with their corresponding workers.
     51   *
     52   * @param {Array} [workerTargets] (optional)
     53   *        Array containing the result of a call to `listAllWorkerTargets`.
     54   *        (this exists to avoid duplication of calls to that method)
     55   * @return {object[]} result - An Array of Objects with the following format
     56   *         - {result[].registration} - The registration front
     57   *         - {result[].workers} Array of form-like objects for service workers
     58   */
     59  async listAllServiceWorkers(workerTargets) {
     60    const result = [];
     61    const { registrations } = await this.listServiceWorkerRegistrations();
     62    const allWorkers = workerTargets
     63      ? workerTargets
     64      : await this.listAllWorkerTargets();
     65 
     66    for (const registrationFront of registrations) {
     67      // workers from the registration, ordered from most recent to older
     68      const workers = [
     69        registrationFront.activeWorker,
     70        registrationFront.waitingWorker,
     71        registrationFront.installingWorker,
     72        registrationFront.evaluatingWorker,
     73      ]
     74        // filter out non-existing workers
     75        .filter(w => !!w)
     76        // build a worker object with its WorkerDescriptorFront
     77        .map(workerFront => {
     78          const workerDescriptorFront = allWorkers.find(
     79            targetFront => targetFront.id === workerFront.id
     80          );
     81 
     82          return {
     83            id: workerFront.id,
     84            name: workerFront.url,
     85            state: workerFront.state,
     86            stateText: workerFront.stateText,
     87            url: workerFront.url,
     88            origin: workerFront.origin,
     89            workerDescriptorFront,
     90          };
     91        });
     92 
     93      // TODO: return only the worker targets. See Bug 1620605
     94      result.push({
     95        registration: registrationFront,
     96        workers,
     97      });
     98    }
     99 
    100    return result;
    101  }
    102 
    103  /**
    104   * Retrieve all service worker registrations as well as workers from the parent and
    105   * content processes. Listing service workers involves merging information coming from
    106   * registrations and workers, this method will combine this information to present a
    107   * unified array of serviceWorkers. If you are only interested in other workers, use
    108   * listWorkers.
    109   *
    110   * @return {object}
    111   *         - {Array} service
    112   *           array of form-like objects for serviceworkers
    113   *         - {Array} shared
    114   *           Array of WorkerTargetActor forms, containing shared workers.
    115   *         - {Array} other
    116   *           Array of WorkerTargetActor forms, containing other workers.
    117   */
    118  async listAllWorkers() {
    119    const allWorkers = await this.listAllWorkerTargets();
    120    const serviceWorkers = await this.listAllServiceWorkers(allWorkers);
    121 
    122    // NOTE: listAllServiceWorkers() now returns all the workers belonging to
    123    //       a registration. To preserve the usual behavior at about:debugging,
    124    //       in which we show only the most recent one, we grab the first
    125    //       worker in the array only.
    126    const result = {
    127      service: serviceWorkers
    128        .map(({ registration, workers }) => {
    129          return workers.slice(0, 1).map(worker => {
    130            return Object.assign(worker, {
    131              registrationFront: registration,
    132              fetch: registration.fetch,
    133            });
    134          });
    135        })
    136        .flat(),
    137      shared: [],
    138      other: [],
    139    };
    140 
    141    allWorkers.forEach(front => {
    142      const worker = {
    143        id: front.id,
    144        url: front.url,
    145        origin: front.origin,
    146        name: front.url,
    147        workerDescriptorFront: front,
    148      };
    149 
    150      switch (front.type) {
    151        case Ci.nsIWorkerDebugger.TYPE_SERVICE:
    152          // do nothing, since we already fetched them in `serviceWorkers`
    153          break;
    154        case Ci.nsIWorkerDebugger.TYPE_SHARED:
    155          result.shared.push(worker);
    156          break;
    157        default:
    158          result.other.push(worker);
    159      }
    160    });
    161 
    162    return result;
    163  }
    164 
    165  /** Get the target fronts for all worker threads running in any process. */
    166  async listAllWorkerTargets() {
    167    const listParentWorkers = async () => {
    168      const { workers } = await this.listWorkers();
    169      return workers;
    170    };
    171    const listChildWorkers = async () => {
    172      const processes = await this.listProcesses();
    173      const processWorkers = await Promise.all(
    174        processes.map(async processDescriptorFront => {
    175          // Ignore parent process
    176          if (processDescriptorFront.isParentProcessDescriptor) {
    177            return [];
    178          }
    179          try {
    180            const front = await processDescriptorFront.getTarget();
    181            if (!front) {
    182              return [];
    183            }
    184            const response = await front.listWorkers();
    185            return response.workers;
    186          } catch (e) {
    187            if (e.message.includes("Connection closed")) {
    188              return [];
    189            }
    190            throw e;
    191          }
    192        })
    193      );
    194 
    195      return processWorkers.flat();
    196    };
    197 
    198    const [parentWorkers, childWorkers] = await Promise.all([
    199      listParentWorkers(),
    200      listChildWorkers(),
    201    ]);
    202 
    203    return parentWorkers.concat(childWorkers);
    204  }
    205 
    206  /**
    207   * Fetch the ProcessDescriptorFront for the main process.
    208   *
    209   * `getProcess` requests allows to fetch the descriptor for any process and
    210   * the main process is having the process ID zero.
    211   */
    212  getMainProcess() {
    213    return this.getProcess(0);
    214  }
    215 
    216  /**
    217   * Fetch the tab descriptor for the currently selected tab, or for a specific
    218   * tab given as first parameter.
    219   *
    220   * @param [optional] object filter
    221   *        A dictionary object with following optional attributes:
    222   *         - browserId: use to match any tab
    223   *         - tab: a reference to xul:tab element (used for local tab debugging)
    224   *         - isWebExtension: an optional boolean to flag TabDescriptors
    225   *        If nothing is specified, returns the actor for the currently
    226   *        selected tab.
    227   */
    228  async getTab(filter) {
    229    const packet = {};
    230    if (filter) {
    231      if (typeof filter.browserId == "number") {
    232        packet.browserId = filter.browserId;
    233      } else if ("tab" in filter) {
    234        const browser = filter.tab.linkedBrowser;
    235        packet.browserId = browser.browserId;
    236      } else {
    237        // Throw if a filter object have been passed but without
    238        // any clearly idenfified filter.
    239        throw new Error("Unsupported argument given to getTab request");
    240      }
    241    }
    242 
    243    const descriptorFront = await super.getTab(packet);
    244 
    245    // Will flag TabDescriptor used by WebExtension codebase.
    246    if (filter?.isWebExtension) {
    247      descriptorFront.setIsForWebExtension(true);
    248    }
    249 
    250    // If the tab is a local tab, forward it to the descriptor.
    251    if (filter?.tab?.tagName == "tab") {
    252      // Ignore the fake `tab` object we receive, where there is only a
    253      // `linkedBrowser` attribute, but this isn't a real <tab> element.
    254      // devtools/client/framework/test/browser_toolbox_target.js is passing such
    255      // a fake tab.
    256      descriptorFront.setLocalTab(filter.tab);
    257    }
    258 
    259    return descriptorFront;
    260  }
    261 
    262  /**
    263   * Fetch the target front for a given add-on.
    264   * This is just an helper on top of `listAddons` request.
    265   *
    266   * @param object filter
    267   *        A dictionary object with following attribute:
    268   *         - id: used to match the add-on to connect to.
    269   */
    270  async getAddon({ id }) {
    271    const addons = await this.listAddons();
    272    const webextensionDescriptorFront = addons.find(addon => addon.id === id);
    273    return webextensionDescriptorFront;
    274  }
    275 
    276  /**
    277   * Fetch the target front for a given worker.
    278   * This is just an helper on top of `listAllWorkers` request.
    279   *
    280   * @param id
    281   */
    282  async getWorker(id) {
    283    const { service, shared, other } = await this.listAllWorkers();
    284    const worker = [...service, ...shared, ...other].find(w => w.id === id);
    285    if (!worker) {
    286      return null;
    287    }
    288    return worker.workerDescriptorFront || worker.registrationFront;
    289  }
    290 
    291  /**
    292   * Test request that returns the object passed as first argument.
    293   *
    294   * `echo` is special as all the property of the given object have to be passed
    295   * on the packet object. That's not something that can be achieve by requester helper.
    296   */
    297 
    298  echo(packet) {
    299    packet.type = "echo";
    300    return this.request(packet);
    301  }
    302 
    303  /**
    304   * This function returns a protocol.js Front for any root actor.
    305   * i.e. the one directly served from RootActor.listTabs or getRoot.
    306   *
    307   * @param String typeName
    308   *        The type name used in protocol.js's spec for this actor.
    309   */
    310  async getFront(typeName) {
    311    let front = this.fronts.get(typeName);
    312    if (front) {
    313      return front;
    314    }
    315    const rootForm = await this.rootForm;
    316    front = getFront(this._client, typeName, rootForm);
    317    this.fronts.set(typeName, front);
    318    return front;
    319  }
    320 
    321  /**
    322   * This function returns true if the root actor has a registered global actor
    323   * with a given name.
    324   *
    325   * @param {string} actorName
    326   *        The name of a global actor.
    327   *
    328   * @return {boolean}
    329   */
    330  async hasActor(actorName) {
    331    const rootForm = await this.rootForm;
    332    return !!rootForm[actorName + "Actor"];
    333  }
    334 }
    335 registerFront(RootFront);