No bug - tagging b4d3227540c9ebc43d64aac6168fdca7019c22d8 with FIREFOX_BETA_126_BASE...
[gecko.git] / testing / modules / MockRegistrar.sys.mjs
blob95dd070a2fb8fc99fa8bc6f9c5ce210b1a969d70
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/. */
5 const Cm = Components.manager;
7 export var MockRegistrar = Object.freeze({
8   _registeredComponents: new Map(),
9   _originalCIDs: new Map(),
10   get registrar() {
11     return Cm.QueryInterface(Ci.nsIComponentRegistrar);
12   },
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     let originalCID;
31     let originalFactory;
32     try {
33       originalCID = this._originalCIDs.get(contractID);
34       if (!originalCID) {
35         originalCID = this.registrar.contractIDToCID(contractID);
36         this._originalCIDs.set(contractID, originalCID);
37       }
39       originalFactory = Cm.getClassObject(originalCID, Ci.nsIFactory);
40     } catch (e) {
41       // There's no original factory. Ignore and just register the new
42       // one.
43     }
45     let cid = Services.uuid.generateUUID();
47     let factory = {
48       createInstance(iid) {
49         let wrappedMock;
50         if (mock.prototype && mock.prototype.constructor) {
51           wrappedMock = Object.create(mock.prototype);
52           mock.apply(wrappedMock, args);
53         } else if (typeof mock == "function") {
54           wrappedMock = mock();
55         } else {
56           wrappedMock = mock;
57         }
59         if (originalFactory) {
60           try {
61             let genuine = originalFactory.createInstance(iid);
62             wrappedMock._genuine = genuine;
63           } catch (ex) {
64             console.error(
65               "MockRegistrar: Creating original instance failed",
66               ex
67             );
68           }
69         }
71         return wrappedMock.QueryInterface(iid);
72       },
73       QueryInterface: ChromeUtils.generateQI(["nsIFactory"]),
74     };
76     this.registrar.registerFactory(
77       cid,
78       "A Mock for " + contractID,
79       contractID,
80       factory
81     );
83     this._registeredComponents.set(cid, {
84       contractID,
85       factory,
86       originalCID,
87     });
89     return cid;
90   },
92   /**
93    * Unregister the mock.
94    *
95    * @param cid The CID of the mock.
96    */
97   unregister(cid) {
98     let component = this._registeredComponents.get(cid);
99     if (!component) {
100       return;
101     }
103     this.registrar.unregisterFactory(cid, component.factory);
104     if (component.originalCID) {
105       // Passing `null` for the factory re-maps the contract ID to the
106       // entry for its original CID.
107       this.registrar.registerFactory(
108         component.originalCID,
109         "",
110         component.contractID,
111         null
112       );
113     }
115     this._registeredComponents.delete(cid);
116   },
118   /**
119    * Unregister all registered mocks.
120    */
121   unregisterAll() {
122     for (let cid of this._registeredComponents.keys()) {
123       this.unregister(cid);
124     }
125   },