MockRegistrar.jsm 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  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. this.EXPORTED_SYMBOLS = [
  6. "MockRegistrar",
  7. ];
  8. const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
  9. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  10. Cu.import("resource://gre/modules/Log.jsm");
  11. var logger = Log.repository.getLogger("MockRegistrar");
  12. this.MockRegistrar = Object.freeze({
  13. _registeredComponents: new Map(),
  14. _originalCIDs: new Map(),
  15. get registrar() {
  16. return Cm.QueryInterface(Ci.nsIComponentRegistrar);
  17. },
  18. /**
  19. * Register a mock to override target interfaces.
  20. * The target interface may be accessed through _genuine property of the mock.
  21. * If you register multiple mocks to the same contract ID, you have to call
  22. * unregister in reverse order. Otherwise the previous factory will not be
  23. * restored.
  24. *
  25. * @param contractID The contract ID of the interface which is overridden by
  26. the mock.
  27. * e.g. "@mozilla.org/file/directory_service;1"
  28. * @param mock An object which implements interfaces for the contract ID.
  29. * @param args An array which is passed in the constructor of mock.
  30. *
  31. * @return The CID of the mock.
  32. */
  33. register(contractID, mock, args) {
  34. let originalCID = this._originalCIDs.get(contractID);
  35. if (!originalCID) {
  36. originalCID = this.registrar.contractIDToCID(contractID);
  37. this._originalCIDs.set(contractID, originalCID);
  38. }
  39. let originalFactory = Cm.getClassObject(originalCID, Ci.nsIFactory);
  40. let factory = {
  41. createInstance(outer, iid) {
  42. if (outer) {
  43. throw Cr.NS_ERROR_NO_AGGREGATION;
  44. }
  45. let wrappedMock;
  46. if (mock.prototype && mock.prototype.constructor) {
  47. wrappedMock = Object.create(mock.prototype);
  48. mock.apply(wrappedMock, args);
  49. } else {
  50. wrappedMock = mock;
  51. }
  52. try {
  53. let genuine = originalFactory.createInstance(outer, iid);
  54. wrappedMock._genuine = genuine;
  55. } catch(ex) {
  56. logger.info("Creating original instance failed", ex);
  57. }
  58. return wrappedMock.QueryInterface(iid);
  59. },
  60. lockFactory(lock) {
  61. throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  62. },
  63. QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
  64. };
  65. this.registrar.unregisterFactory(originalCID, originalFactory);
  66. this.registrar.registerFactory(originalCID,
  67. "A Mock for " + contractID,
  68. contractID,
  69. factory);
  70. this._registeredComponents.set(originalCID, {
  71. contractID: contractID,
  72. factory: factory,
  73. originalFactory: originalFactory
  74. });
  75. return originalCID;
  76. },
  77. /**
  78. * Unregister the mock.
  79. *
  80. * @param cid The CID of the mock.
  81. */
  82. unregister(cid) {
  83. let component = this._registeredComponents.get(cid);
  84. if (!component) {
  85. return;
  86. }
  87. this.registrar.unregisterFactory(cid, component.factory);
  88. if (component.originalFactory) {
  89. this.registrar.registerFactory(cid,
  90. "",
  91. component.contractID,
  92. component.originalFactory);
  93. }
  94. this._registeredComponents.delete(cid);
  95. },
  96. /**
  97. * Unregister all registered mocks.
  98. */
  99. unregisterAll() {
  100. for (let cid of this._registeredComponents.keys()) {
  101. this.unregister(cid);
  102. }
  103. }
  104. });