tor-browser

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

MockRegistrar.sys.mjs (4666B)


      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 const Cm = Components.manager;
      6 
      7 export var MockRegistrar = Object.freeze({
      8  _registeredComponents: new Map(),
      9  _originalCIDs: new Map(),
     10  get registrar() {
     11    return Cm.QueryInterface(Ci.nsIComponentRegistrar);
     12  },
     13 
     14  /**
     15   * Register a mock to override target interfaces.
     16   * The target interface may be accessed through _genuine property of the mock.
     17   * If you register multiple mocks to the same contract ID, you have to call
     18   * unregister in reverse order. Otherwise the previous factory will not be
     19   * restored.
     20   *
     21   * @param contractID The contract ID of the interface which is overridden by
     22                       the mock.
     23   *                   e.g. "@mozilla.org/file/directory_service;1"
     24   * @param mock       An object which implements interfaces for the contract ID.
     25   * @param args       An array which is passed in the constructor of mock.
     26   *
     27   * @return           The CID of the mock.
     28   */
     29  register(contractID, mock, args) {
     30    return this.registerEx(
     31      contractID,
     32      { shouldCreateInstance: true },
     33      mock,
     34      args
     35    );
     36  },
     37 
     38  /**
     39   * Register a mock to override target interfaces.
     40   * If shouldCreateInstance is true then the target interface may be accessed
     41   * through _genuine property of the mock.
     42   * If you register multiple mocks to the same contract ID, you have to call
     43   * unregister in reverse order. Otherwise the previous factory will not be
     44   * restored.
     45   *
     46   * @param contractID The contract ID of the interface which is overridden by
     47                       the mock.
     48   *                   e.g. "@mozilla.org/file/directory_service;1"
     49   * @param options    Options object with any of the following optional
     50   *                   parameters:
     51   *                   * shouldCreateInstance: Adds the _genuine property to
     52   *                     the mock.
     53   * @param mock       An object which implements interfaces for the contract ID.
     54   * @param args       An array which is passed in the constructor of mock.
     55   *
     56   * @return           The CID of the mock.
     57   */
     58  registerEx(contractID, options, mock, args) {
     59    let originalCID;
     60    let originalFactory;
     61    try {
     62      originalCID = this._originalCIDs.get(contractID);
     63      if (!originalCID) {
     64        originalCID = this.registrar.contractIDToCID(contractID);
     65        this._originalCIDs.set(contractID, originalCID);
     66      }
     67 
     68      originalFactory = Cm.getClassObject(originalCID, Ci.nsIFactory);
     69    } catch (e) {
     70      // There's no original factory. Ignore and just register the new
     71      // one.
     72    }
     73 
     74    let cid = Services.uuid.generateUUID();
     75 
     76    let factory = {
     77      createInstance(iid) {
     78        let wrappedMock;
     79        if (mock.prototype && mock.prototype.constructor) {
     80          wrappedMock = Object.create(mock.prototype);
     81          mock.apply(wrappedMock, args);
     82        } else if (typeof mock == "function") {
     83          wrappedMock = mock();
     84        } else {
     85          wrappedMock = mock;
     86        }
     87 
     88        if (originalFactory && options.shouldCreateInstance) {
     89          try {
     90            let genuine = originalFactory.createInstance(iid);
     91            wrappedMock._genuine = genuine;
     92          } catch (ex) {
     93            console.error(
     94              "MockRegistrar: Creating original instance failed",
     95              ex
     96            );
     97          }
     98        }
     99 
    100        return wrappedMock.QueryInterface(iid);
    101      },
    102      QueryInterface: ChromeUtils.generateQI(["nsIFactory"]),
    103    };
    104 
    105    this.registrar.registerFactory(
    106      cid,
    107      "A Mock for " + contractID,
    108      contractID,
    109      factory
    110    );
    111 
    112    this._registeredComponents.set(cid, {
    113      contractID,
    114      factory,
    115      originalCID,
    116    });
    117 
    118    return cid;
    119  },
    120 
    121  /**
    122   * Unregister the mock.
    123   *
    124   * @param cid The CID of the mock.
    125   */
    126  unregister(cid) {
    127    let component = this._registeredComponents.get(cid);
    128    if (!component) {
    129      return;
    130    }
    131 
    132    this.registrar.unregisterFactory(cid, component.factory);
    133    if (component.originalCID) {
    134      // Passing `null` for the factory re-maps the contract ID to the
    135      // entry for its original CID.
    136      this.registrar.registerFactory(
    137        component.originalCID,
    138        "",
    139        component.contractID,
    140        null
    141      );
    142    }
    143 
    144    this._registeredComponents.delete(cid);
    145  },
    146 
    147  /**
    148   * Unregister all registered mocks.
    149   */
    150  unregisterAll() {
    151    for (let cid of this._registeredComponents.keys()) {
    152      this.unregister(cid);
    153    }
    154  },
    155 });