michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * Allows registering a mock XPCOM component, that temporarily replaces the michael@0: * original one when an object implementing a given ContractID is requested michael@0: * using createInstance. michael@0: * michael@0: * @param aContractID michael@0: * The ContractID of the component to replace, for example michael@0: * "@mozilla.org/filepicker;1". michael@0: * michael@0: * @param aReplacementCtor michael@0: * The constructor function for the JavaScript object that will be michael@0: * created every time createInstance is called. This object must michael@0: * implement QueryInterface and provide the XPCOM interfaces required by michael@0: * the specified ContractID (for example michael@0: * Components.interfaces.nsIFilePicker). michael@0: */ michael@0: michael@0: function MockObjectRegisterer(aContractID, aReplacementCtor) { michael@0: this._contractID = aContractID; michael@0: this._replacementCtor = aReplacementCtor; michael@0: } michael@0: michael@0: MockObjectRegisterer.prototype = { michael@0: /** michael@0: * Replaces the current factory with one that returns a new mock object. michael@0: * michael@0: * After register() has been called, it is mandatory to call unregister() to michael@0: * restore the original component. Usually, you should use a try-catch block michael@0: * to ensure that unregister() is called. michael@0: */ michael@0: register: function MOR_register() { michael@0: if (this._originalFactory) michael@0: throw new Exception("Invalid object state when calling register()"); michael@0: michael@0: // Define a factory that creates a new object using the given constructor. michael@0: var providedConstructor = this._replacementCtor; michael@0: this._mockFactory = { michael@0: createInstance: function MF_createInstance(aOuter, aIid) { michael@0: if (aOuter != null) michael@0: throw SpecialPowers.Cr.NS_ERROR_NO_AGGREGATION; michael@0: return new providedConstructor().QueryInterface(aIid); michael@0: } michael@0: }; michael@0: michael@0: var retVal = SpecialPowers.swapFactoryRegistration(this._cid, this._contractID, this._mockFactory, this._originalFactory); michael@0: if ('error' in retVal) { michael@0: throw new Exception("ERROR: " + retVal.error); michael@0: } else { michael@0: this._cid = retVal.cid; michael@0: this._originalFactory = retVal.originalFactory; michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Restores the original factory. michael@0: */ michael@0: unregister: function MOR_unregister() { michael@0: if (!this._originalFactory) michael@0: throw new Exception("Invalid object state when calling unregister()"); michael@0: michael@0: // Free references to the mock factory. michael@0: SpecialPowers.swapFactoryRegistration(this._cid, this._contractID, this._mockFactory, this._originalFactory); michael@0: michael@0: // Allow registering a mock factory again later. michael@0: this._cid = null; michael@0: this._originalFactory = null; michael@0: this._mockFactory = null; michael@0: }, michael@0: michael@0: // --- Private methods and properties --- michael@0: michael@0: /** michael@0: * The factory of the component being replaced. michael@0: */ michael@0: _originalFactory: null, michael@0: michael@0: /** michael@0: * The CID under which the mock contractID was registered. michael@0: */ michael@0: _cid: null, michael@0: michael@0: /** michael@0: * The nsIFactory that was automatically generated by this object. michael@0: */ michael@0: _mockFactory: null michael@0: }