author | Paolo Amadini <paolo.mozmail@amadzone.org> |
Fri, 14 Apr 2017 20:04:09 +0100 | |
changeset 562932 | 52721e85ff92ffd9f8aa0765663c3bde5b6eab47 |
parent 562931 | 98d17809392a4b5a8fd403a047c29f4eeaeb64d8 |
child 563209 | 0c842601f76df4819eb8760f5090a3db713747d0 |
push id | 54170 |
push user | paolo.mozmail@amadzone.org |
push date | Fri, 14 Apr 2017 19:20:13 +0000 |
reviewers | mak |
bugs | 1355585 |
milestone | 55.0a1 |
--- a/uriloader/exthandler/nsHandlerService-json.js +++ b/uriloader/exthandler/nsHandlerService-json.js @@ -34,42 +34,46 @@ HandlerService.prototype = { Ci.nsIHandlerService, Ci.nsIObserver ]), __store: null, get _store() { if (!this.__store) { this.__store = new JSONFile({ - path: OS.Path.join(OS.Constants.Path.profileDir, - "handlers.json"), + path: OS.Path.join(OS.Constants.Path.profileDir, "handlers.json"), dataPostProcessor: this._dataPostProcessor.bind(this), }); this.__store.ensureDataReady(); this._updateDB(); } return this.__store; }, _dataPostProcessor(data) { - return data.schemes ? data : { version: {}, mimetypes: {}, schemes: {} }; + return data.defaultHandlersVersion ? data : { + defaultHandlersVersion: {}, + mimeTypes: {}, + schemes: {}, + }; }, _updateDB() { try { - let locale = Services.locale.getAppLocaleAsLangTag(); let prefsDefaultHandlersVersion = Number(Services.prefs.getComplexValue( "gecko.handlerService.defaultHandlersVersion", Ci.nsIPrefLocalizedString).data); - let defaultHandlersVersion = this._store.data.version[locale] || 0; - if (defaultHandlersVersion < prefsDefaultHandlersVersion ) { + let defaultHandlersVersion = + this._store.data.defaultHandlersVersion[locale] || 0; + if (defaultHandlersVersion < prefsDefaultHandlersVersion) { this._injectNewDefaults(); - this._store.data.version[locale] = prefsDefaultHandlersVersion; + this._store.data.defaultHandlersVersion[locale] = + prefsDefaultHandlersVersion; } } catch (ex) { Cu.reportError(ex); } }, _injectNewDefaults() { let schemesPrefBranch = Services.prefs.getBranch("gecko.handlerService.schemes."); @@ -116,36 +120,25 @@ HandlerService.prototype = { osDefaultHandlerFound.value); } // cache the possible handlers to avoid extra xpconnect traversals. let possibleHandlers = protoInfo.possibleApplicationHandlers; for (let handlerNumber of Object.keys(schemes[scheme])) { let handlerApp = this.handlerAppFromSerializable(schemes[scheme][handlerNumber]); - if (!this._isInHandlerArray(possibleHandlers, handlerApp)) { - possibleHandlers.appendElement(handlerApp, false); - } + // If there is already a handler registered with the same template + // URL, the newly added one will be ignored when saving. + possibleHandlers.appendElement(handlerApp, false); } this.store(protoInfo); } }, - _isInHandlerArray(array, handler) { - let enumerator = array.enumerate(); - while (enumerator.hasMoreElements()) { - let handlerApp = enumerator.getNext().QueryInterface(Ci.nsIHandlerApp); - if (handlerApp.equals(handler)) { - return true; - } - } - return false; - }, - _onDBChange() { return Task.spawn(function* () { if (this.__store) { yield this.__store.finalize(); } this.__store = null; }.bind(this)).catch(Cu.reportError); }, @@ -158,119 +151,134 @@ HandlerService.prototype = { let promise = this._onDBChange(); promise.then(() => { Services.obs.notifyObservers(null, "handlersvc-json-replace-complete", null); }); }, // nsIHandlerService enumerate() { - let handlers = Cc["@mozilla.org/array;1"]. - createInstance(Ci.nsIMutableArray); - for (let type of Object.keys(this._store.data.mimetypes)) { + let handlers = Cc["@mozilla.org/array;1"] + .createInstance(Ci.nsIMutableArray); + for (let type of Object.keys(this._store.data.mimeTypes)) { let handler = gMIMEService.getFromTypeAndExtension(type, null); handlers.appendElement(handler, false); } for (let type of Object.keys(this._store.data.schemes)) { let handler = gExternalProtocolService.getProtocolHandlerInfo(type); handlers.appendElement(handler, false); } return handlers.enumerate(); }, // nsIHandlerService store(handlerInfo) { - let handlerObj = { - action: handlerInfo.preferredAction, - askBeforeHandling: handlerInfo.alwaysAskBeforeHandling, - }; + let handlerList = this._getHandlerListByHandlerInfoType(handlerInfo); + + // Retrieve an existing entry if present, instead of creating a new one, so + // that we preserve unknown properties for forward compatibility. + let storedHandlerInfo = handlerList[handlerInfo.type]; + if (!storedHandlerInfo) { + storedHandlerInfo = {}; + handlerList[handlerInfo.type] = storedHandlerInfo; + } - let preferredHandler = handlerInfo.preferredApplicationHandler; - if (preferredHandler) { - let serializable = this.handlerAppToSerializable(preferredHandler); - if (serializable) { - handlerObj.preferredHandler = serializable; + // Only a limited number of preferredAction values is allowed. + if (handlerInfo.preferredAction == Ci.nsIHandlerInfo.saveToDisk || + handlerInfo.preferredAction == Ci.nsIHandlerInfo.useSystemDefault || + handlerInfo.preferredAction == Ci.nsIHandlerInfo.handleInternally) { + storedHandlerInfo.action = handlerInfo.preferredAction; + } else { + storedHandlerInfo.action = Ci.nsIHandlerInfo.useHelperApp; + } + + if (handlerInfo.alwaysAskBeforeHandling) { + storedHandlerInfo.ask = true; + } else { + delete storedHandlerInfo.ask; + } + + // Build a list of unique nsIHandlerInfo instances to process later. + let handlers = []; + if (handlerInfo.preferredApplicationHandler) { + handlers.push(handlerInfo.preferredApplicationHandler); + } + let enumerator = handlerInfo.possibleApplicationHandlers.enumerate(); + while (enumerator.hasMoreElements()) { + let handler = enumerator.getNext().QueryInterface(Ci.nsIHandlerApp); + // If the caller stored duplicate handlers, we save them only once. + if (!handlers.some(h => h.equals(handler))) { + handlers.push(handler); } } - let apps = handlerInfo.possibleApplicationHandlers.enumerate(); - let possibleHandlers = []; - while (apps.hasMoreElements()) { - let handler = apps.getNext().QueryInterface(Ci.nsIHandlerApp); - let serializable = this.handlerAppToSerializable(handler); - if (serializable) { - possibleHandlers.push(serializable); + // If any of the nsIHandlerInfo instances cannot be serialized, it is not + // included in the final list. The first element is always the preferred + // handler, or null if there is none. + let serializableHandlers = + handlers.map(h => this.handlerAppToSerializable(h)).filter(h => h); + if (serializableHandlers.length) { + if (!handlerInfo.preferredApplicationHandler) { + serializableHandlers.unshift(null); } - } - if (possibleHandlers.length) { - handlerObj.possibleHandlers = possibleHandlers; + storedHandlerInfo.handlers = serializableHandlers; + } else { + delete storedHandlerInfo.handlers; } if (this._isMIMEInfo(handlerInfo)) { let extEnumerator = handlerInfo.getFileExtensions(); - let extensions = []; + let extensions = storedHandlerInfo.extensions || []; while (extEnumerator.hasMore()) { - let extension = extEnumerator.getNext(); + let extension = extEnumerator.getNext().toLowerCase(); + // If the caller stored duplicate extensions, we save them only once. if (!extensions.includes(extension)) { extensions.push(extension); } } if (extensions.length) { - handlerObj.fileExtensions = extensions; + storedHandlerInfo.extensions = extensions; + } else { + delete storedHandlerInfo.extensions; } } - this._getHandlerListByHandlerInfoType(handlerInfo)[handlerInfo.type] = handlerObj; + this._store.saveSoon(); }, // nsIHandlerService fillHandlerInfo(handlerInfo, overrideType) { let type = overrideType || handlerInfo.type; let storedHandlerInfo = this._getHandlerListByHandlerInfoType(handlerInfo)[type]; if (!storedHandlerInfo) { throw new Components.Exception("handlerSvc fillHandlerInfo: don't know this type", Cr.NS_ERROR_NOT_AVAILABLE); } - // logic from _retrievePreferredAction of nsHandlerService.js - if (storedHandlerInfo.action == Ci.nsIHandlerInfo.saveToDisk || - storedHandlerInfo.action == Ci.nsIHandlerInfo.useSystemDefault || - storedHandlerInfo.action == Ci.nsIHandlerInfo.handleInternally) { - handlerInfo.preferredAction = storedHandlerInfo.action; - } else { - handlerInfo.preferredAction = Ci.nsIHandlerInfo.useHelperApp; - } + handlerInfo.preferredAction = storedHandlerInfo.action; + handlerInfo.alwaysAskBeforeHandling = !!storedHandlerInfo.ask; - let preferHandler = null; - if (storedHandlerInfo.preferredHandler) { - preferHandler = this.handlerAppFromSerializable(storedHandlerInfo.preferredHandler); - } - handlerInfo.preferredApplicationHandler = preferHandler; - if (preferHandler) { - handlerInfo.possibleApplicationHandlers.appendElement(preferHandler, false); - } - - if (storedHandlerInfo.possibleHandlers) { - for (let handler of storedHandlerInfo.possibleHandlers) { - let possibleHandler = this.handlerAppFromSerializable(handler); - if (possibleHandler && (!preferHandler || - !possibleHandler.equals(preferHandler))) { - handlerInfo.possibleApplicationHandlers.appendElement(possibleHandler, false); - } + // If the first item is not null, it is also the preferred handler. Since + // we cannot modify the stored array, use a boolean to keep track of this. + let isFirstItem = true; + for (let handler of storedHandlerInfo.handlers || [null]) { + let handlerApp = this.handlerAppFromSerializable(handler || {}); + if (isFirstItem) { + isFirstItem = false; + handlerInfo.preferredApplicationHandler = handlerApp; + } + if (handlerApp) { + handlerInfo.possibleApplicationHandlers.appendElement(handlerApp, + false); } } - // We always store "askBeforeHandling" in the JSON file. Just use this value. - handlerInfo.alwaysAskBeforeHandling = storedHandlerInfo.askBeforeHandling; - - if (this._isMIMEInfo(handlerInfo)) { - if (storedHandlerInfo.fileExtensions) { - for (let extension of storedHandlerInfo.fileExtensions) { - handlerInfo.appendExtension(extension); - } + if (this._isMIMEInfo(handlerInfo) && storedHandlerInfo.extensions) { + for (let extension of storedHandlerInfo.extensions) { + handlerInfo.appendExtension(extension); } } }, /** * @param handler * A nsIHandlerApp handler app * @returns Serializable representation of a handler app object. @@ -308,47 +316,47 @@ HandlerService.prototype = { handlerAppFromSerializable(handlerObj) { let handlerApp; if ("path" in handlerObj) { try { let file = new FileUtils.File(handlerObj.path); if (!file.exists()) { return null; } - handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"]. - createInstance(Ci.nsILocalHandlerApp); + handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"] + .createInstance(Ci.nsILocalHandlerApp); handlerApp.executable = file; } catch (ex) { return null; } } else if ("uriTemplate" in handlerObj) { - handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"]. - createInstance(Ci.nsIWebHandlerApp); + handlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"] + .createInstance(Ci.nsIWebHandlerApp); handlerApp.uriTemplate = handlerObj.uriTemplate; } else if ("service" in handlerObj) { - handlerApp = Cc["@mozilla.org/uriloader/dbus-handler-app;1"]. - createInstance(Ci.nsIDBusHandlerApp); + handlerApp = Cc["@mozilla.org/uriloader/dbus-handler-app;1"] + .createInstance(Ci.nsIDBusHandlerApp); handlerApp.service = handlerObj.service; handlerApp.method = handlerObj.method; handlerApp.objectPath = handlerObj.objectPath; handlerApp.dBusInterface = handlerObj.dBusInterface; } else { return null; } handlerApp.name = handlerObj.name; return handlerApp; }, /** - * The function return a reference to the "mimetypes" or "schemes" object + * The function returns a reference to the "mimeTypes" or "schemes" object * based on which type of handlerInfo is provided. */ _getHandlerListByHandlerInfoType(handlerInfo) { - return this._isMIMEInfo(handlerInfo) ? this._store.data.mimetypes + return this._isMIMEInfo(handlerInfo) ? this._store.data.mimeTypes : this._store.data.schemes; }, /** * Determines whether an nsIHandlerInfo instance represents a MIME type. */ _isMIMEInfo(handlerInfo) { // We cannot rely only on the instanceof check because on Android both MIME @@ -367,21 +375,20 @@ HandlerService.prototype = { remove(handlerInfo) { delete this._getHandlerListByHandlerInfoType(handlerInfo)[handlerInfo.type]; this._store.saveSoon(); }, // nsIHandlerService getTypeFromExtension(fileExtension) { let extension = fileExtension.toLowerCase(); - let mimeTypes = this._store.data.mimetypes; + let mimeTypes = this._store.data.mimeTypes; for (let type of Object.keys(mimeTypes)) { - if (mimeTypes[type].fileExtensions && - mimeTypes[type].fileExtensions.includes(extension)) { - return type; + if (mimeTypes[type].extensions && + mimeTypes[type].extensions.includes(extension)) { + return type; } } return ""; }, - }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HandlerService]);
--- a/uriloader/exthandler/nsHandlerService.js +++ b/uriloader/exthandler/nsHandlerService.js @@ -704,37 +704,33 @@ HandlerService.prototype = { _storePreferredHandler: function HS__storePreferredHandler(aHandlerInfo) { var infoID = this._getInfoID(this._getClass(aHandlerInfo), aHandlerInfo.type); var handlerID = this._getPreferredHandlerID(this._getClass(aHandlerInfo), aHandlerInfo.type); var handler = aHandlerInfo.preferredApplicationHandler; - if (handler) { - // If the handlerApp is an unknown type, ignore it. - // Android default application handler is the case. - if (this._handlerAppIsUnknownType(handler)) { - return; - } + if (handler && !this._handlerAppIsUnknownType(handler)) { this._storeHandlerApp(handlerID, handler); // Make this app be the preferred app for the handler info. // // Note: nsExternalHelperAppService::FillContentHandlerProperties ignores // this setting and instead identifies the preferred app as the resource // whose URI follows the pattern urn:<class>:externalApplication:<type>. // But the old downloadactions.js code used to set this property, so just // in case there is still some code somewhere that relies on its presence, // we set it here. this._setResource(infoID, NC_PREFERRED_APP, handlerID); } else { - // There isn't a preferred handler. Remove the existing record for it, - // if any. + // There isn't a preferred handler or the handler cannot be serialized, + // for example the Android default application handler. Remove the + // existing record for it, if any. this._removeTarget(infoID, NC_PREFERRED_APP); this._removeAssertions(handlerID); } }, /** * Store the list of possible handler apps for the content type represented * by the given handler info object.
--- a/uriloader/exthandler/tests/HandlerServiceTestUtils.jsm +++ b/uriloader/exthandler/tests/HandlerServiceTestUtils.jsm @@ -90,16 +90,24 @@ this.HandlerServiceTestUtils = { */ getHandlerInfo(type) { if (type.includes("/")) { // We have to use the getFromTypeAndExtension method because we don't have // access to getMIMEInfoFromOS. This means that we have to reset the data // that may have been imported from the default nsIHandlerService instance // and is not overwritten by fillHandlerInfo later. let handlerInfo = gMIMEService.getFromTypeAndExtension(type, null); + if (AppConstants.platform == "android") { + // On Android, the first handler application is always the internal one. + while (handlerInfo.possibleApplicationHandlers.length > 1) { + handlerInfo.possibleApplicationHandlers.removeElementAt(1); + } + } else { + handlerInfo.possibleApplicationHandlers.clear(); + } handlerInfo.setFileExtensions(""); // Populate the object from the handler service instance under testing. if (this.handlerService.exists(handlerInfo)) { this.handlerService.fillHandlerInfo(handlerInfo, ""); } return handlerInfo; } @@ -168,19 +176,22 @@ this.HandlerServiceTestUtils = { /** * Checks whether an nsIHandlerInfo instance matches the provided object. */ assertHandlerInfoMatches(handlerInfo, expected) { let expectedInterface = expected.type.includes("/") ? Ci.nsIMIMEInfo : Ci.nsIHandlerInfo; Assert.ok(handlerInfo instanceof expectedInterface); Assert.equal(handlerInfo.type, expected.type); - Assert.equal(handlerInfo.preferredAction, expected.preferredAction); - Assert.equal(handlerInfo.alwaysAskBeforeHandling, - expected.alwaysAskBeforeHandling); + + if (!expected.preferredActionOSDependent) { + Assert.equal(handlerInfo.preferredAction, expected.preferredAction); + Assert.equal(handlerInfo.alwaysAskBeforeHandling, + expected.alwaysAskBeforeHandling); + } if (expectedInterface == Ci.nsIMIMEInfo) { let fileExtensionsEnumerator = handlerInfo.getFileExtensions(); for (let expectedFileExtension of expected.fileExtensions || []) { Assert.equal(fileExtensionsEnumerator.getNext(), expectedFileExtension); } Assert.ok(!fileExtensionsEnumerator.hasMore()); }
--- a/uriloader/exthandler/tests/unit/common_test_handlerService.js +++ b/uriloader/exthandler/tests/unit/common_test_handlerService.js @@ -1,538 +1,611 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/** - * This script is loaded by "test_handlerService_json.js" and "test_handlerService_rdf.js" - * to make sure there is the same behavior when using two different implementations - * of handlerService (JSON backend and RDF backend). +/* + * Loaded by "test_handlerService_json.js" and "test_handlerService_rdf.js" to + * check that the nsIHandlerService interface has the same behavior with both + * the JSON and RDF backends. */ HandlerServiceTestUtils.handlerService = gHandlerService; -const pdfHandlerInfo = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type"); -const gzipHandlerInfo = - HandlerServiceTestUtils.getHandlerInfo("application/x-gzip"); -const ircHandlerInfo = - HandlerServiceTestUtils.getHandlerInfo("irc"); +// Set up an nsIWebHandlerApp instance that can be used in multiple tests. +let webHandlerApp = Cc["@mozilla.org/uriloader/web-handler-app;1"] + .createInstance(Ci.nsIWebHandlerApp); +webHandlerApp.name = "Web Handler"; +webHandlerApp.uriTemplate = "https://www.example.com/?url=%s"; +let expectedWebHandlerApp = { + name: webHandlerApp.name, + uriTemplate: webHandlerApp.uriTemplate, +}; -let executable = Services.dirsvc.get("TmpD", Ci.nsIFile); -let localHandler = { - name: "Local Handler", - executable: executable, - interfaces: [Ci.nsIHandlerApp, Ci.nsILocalHandlerApp, Ci.nsISupports], - QueryInterface: function(iid) { - if (!this.interfaces.some( function(v) { return iid.equals(v) } )) - throw Cr.NS_ERROR_NO_INTERFACE; - return this; - } +// Set up an nsILocalHandlerApp instance that can be used in multiple tests. The +// executable should exist, but it doesn't need to point to an actual file, so +// we simply initialize it to the path of an existing directory. +let localHandlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"] + .createInstance(Ci.nsILocalHandlerApp); +localHandlerApp.name = "Local Handler"; +localHandlerApp.executable = FileUtils.getFile("TmpD", []); +let expectedLocalHandlerApp = { + name: localHandlerApp.name, + executable: localHandlerApp.executable, }; -let webHandler = { - name: "Web Handler", - uriTemplate: "https://www.webhandler.com/?url=%s", - interfaces: [Ci.nsIHandlerApp, Ci.nsIWebHandlerApp, Ci.nsISupports], - QueryInterface: function(iid) { - if (!this.interfaces.some( function(v) { return iid.equals(v) } )) - throw Cr.NS_ERROR_NO_INTERFACE; - return this; - } -}; +/** + * Returns a new nsIHandlerInfo instance initialized to known values that don't + * depend on the platform and are easier to verify later. + * + * @param type + * Because the "preferredAction" is initialized to saveToDisk, this + * should represent a MIME type rather than a protocol. + */ +function getKnownHandlerInfo(type) { + let handlerInfo = HandlerServiceTestUtils.getBlankHandlerInfo(type); + handlerInfo.preferredAction = Ci.nsIHandlerInfo.saveToDisk; + handlerInfo.alwaysAskBeforeHandling = false; + return handlerInfo; +} -let dBusHandler = { - name: "DBus Handler", - service: "DBus Service", - method: "DBus method", - objectPath: "/tmp/PATH/DBus", - dBusInterface: "DBusInterface", - interfaces: [Ci.nsIHandlerApp, Ci.nsIDBusHandlerApp, Ci.nsISupports], - QueryInterface: function(iid) { - if (!this.interfaces.some( function(v) { return iid.equals(v) } )) - throw Cr.NS_ERROR_NO_INTERFACE; - return this; - } -}; - -// Loads data from a file in a predefined format. This test verifies that: -// - The JSON format used in previous versions can be loaded -// - All the known properties specified in the JSON file are considered -add_task(function* testLoadPredefined() { - yield prepareImportDB(); - - let handlerInfos = - HandlerServiceTestUtils.getAllHandlerInfos(); +/** + * Checks that the information stored in the handler service instance under + * testing matches the test data files. + */ +function* assertAllHandlerInfosMatchTestData() { + let handlerInfos = HandlerServiceTestUtils.getAllHandlerInfos(); // It's important that the MIME types we check here do not exist at the // operating system level, otherwise the list of handlers and file extensions // will be merged. The current implementation adds each saved file extension // even if one already exists in the system, resulting in duplicate entries. HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfos.shift(), { - type: "irc", + type: "example/type.handleinternally", + preferredAction: Ci.nsIHandlerInfo.handleInternally, + alwaysAskBeforeHandling: false, + fileExtensions: [ + "example_one", + ], + }); + + HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfos.shift(), { + type: "example/type.savetodisk", + preferredAction: Ci.nsIHandlerInfo.saveToDisk, + alwaysAskBeforeHandling: true, + preferredApplicationHandler: { + name: "Example Default Handler", + uriTemplate: "https://www.example.com/?url=%s", + }, + possibleApplicationHandlers: [{ + name: "Example Default Handler", + uriTemplate: "https://www.example.com/?url=%s", + }], + fileExtensions: [ + "example_two", + "example_three", + ], + }); + + HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfos.shift(), { + type: "example/type.usehelperapp", preferredAction: Ci.nsIHandlerInfo.useHelperApp, alwaysAskBeforeHandling: true, + preferredApplicationHandler: { + name: "Example Default Handler", + uriTemplate: "https://www.example.com/?url=%s", + }, possibleApplicationHandlers: [{ - name: "Mibbit", - uriTemplate: "https://www.mibbit.com/?url=%s", + name: "Example Default Handler", + uriTemplate: "https://www.example.com/?url=%s", + },{ + name: "Example Possible Handler One", + uriTemplate: "http://www.example.com/?id=1&url=%s", + },{ + name: "Example Possible Handler Two", + uriTemplate: "http://www.example.com/?id=2&url=%s", + }], + fileExtensions: [ + "example_two", + "example_three", + ], + }); + + HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfos.shift(), { + type: "example/type.usesystemdefault", + preferredAction: Ci.nsIHandlerInfo.useSystemDefault, + alwaysAskBeforeHandling: false, + possibleApplicationHandlers: [{ + name: "Example Possible Handler", + uriTemplate: "http://www.example.com/?url=%s", + }], + }); + + HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfos.shift(), { + type: "examplescheme.usehelperapp", + preferredAction: Ci.nsIHandlerInfo.useHelperApp, + alwaysAskBeforeHandling: true, + preferredApplicationHandler: { + name: "Example Default Handler", + uriTemplate: "https://www.example.com/?url=%s", + }, + possibleApplicationHandlers: [{ + name: "Example Default Handler", + uriTemplate: "https://www.example.com/?url=%s", + },{ + name: "Example Possible Handler One", + uriTemplate: "http://www.example.com/?id=1&url=%s", + },{ + name: "Example Possible Handler Two", + uriTemplate: "http://www.example.com/?id=2&url=%s", }], }); HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfos.shift(), { - type: "ircs", - preferredAction: Ci.nsIHandlerInfo.useHelperApp, - alwaysAskBeforeHandling: true, + type: "examplescheme.usesystemdefault", + preferredAction: Ci.nsIHandlerInfo.useSystemDefault, + alwaysAskBeforeHandling: false, possibleApplicationHandlers: [{ - name: "Mibbit", - uriTemplate: "https://www.mibbit.com/?url=%s", + name: "Example Possible Handler", + uriTemplate: "http://www.example.com/?url=%s", }], }); + do_check_eq(handlerInfos.length, 0); +} + +/** + * Loads data from a file in a predefined format, verifying that the format is + * recognized and all the known properties are loaded and saved. + */ +add_task(function* test_store_fillHandlerInfo_predefined() { + // Test that the file format used in previous versions can be loaded. + yield copyTestDataToHandlerStore(); + yield assertAllHandlerInfosMatchTestData(); + + // Keep a copy of the nsIHandlerInfo instances, then delete the handler store + // and populate it with the known data. Since the handler store is empty, the + // default handlers for the current locale are also injected, so we have to + // delete them manually before adding the other nsIHandlerInfo instances. + let testHandlerInfos = HandlerServiceTestUtils.getAllHandlerInfos(); + yield deleteHandlerStore(); + for (let handlerInfo of HandlerServiceTestUtils.getAllHandlerInfos()) { + gHandlerService.remove(handlerInfo); + } + for (let handlerInfo of testHandlerInfos) { + gHandlerService.store(handlerInfo); + } + + // Test that the known data still matches after saving it and reloading. + yield unloadHandlerStore(); + yield assertAllHandlerInfosMatchTestData(); +}); + +/** + * Check that "store" is able to add new instances, that "remove" and "exists" + * work, and that "fillHandlerInfo" throws when the instance does not exist. + */ +add_task(function* test_store_remove_exists() { + // Test both MIME types and protocols. + for (let type of ["example/type.usehelperapp", + "examplescheme.usehelperapp"]) { + // Create new nsIHandlerInfo instances before loading the test data. + yield deleteHandlerStore(); + let handlerInfoPresent = HandlerServiceTestUtils.getHandlerInfo(type); + let handlerInfoAbsent = HandlerServiceTestUtils.getHandlerInfo(type + "2"); + + // Set up known properties that we can verify later. + handlerInfoAbsent.preferredAction = Ci.nsIHandlerInfo.saveToDisk; + handlerInfoAbsent.alwaysAskBeforeHandling = false; + + yield copyTestDataToHandlerStore(); + + do_check_true(gHandlerService.exists(handlerInfoPresent)); + do_check_false(gHandlerService.exists(handlerInfoAbsent)); + + gHandlerService.store(handlerInfoAbsent); + gHandlerService.remove(handlerInfoPresent); + + yield unloadHandlerStore(); + + do_check_false(gHandlerService.exists(handlerInfoPresent)); + do_check_true(gHandlerService.exists(handlerInfoAbsent)); + + Assert.throws( + () => gHandlerService.fillHandlerInfo(handlerInfoPresent, ""), + ex => ex.result == Cr.NS_ERROR_NOT_AVAILABLE); + + let actualHandlerInfo = HandlerServiceTestUtils.getHandlerInfo(type + "2"); + HandlerServiceTestUtils.assertHandlerInfoMatches(actualHandlerInfo, { + type: type + "2", + preferredAction: Ci.nsIHandlerInfo.saveToDisk, + alwaysAskBeforeHandling: false, + }); + } +}); + +/** + * Tests that it is possible to save an nsIHandlerInfo instance with a + * "preferredAction" that is alwaysAsk or has an unknown value, but the + * action always becomes useHelperApp when reloading. + */ +add_task(function* test_store_preferredAction() { + yield deleteHandlerStore(); + + let handlerInfo = getKnownHandlerInfo("example/new"); + + for (let preferredAction of [Ci.nsIHandlerInfo.alwaysAsk, 999]) { + handlerInfo.preferredAction = preferredAction; + gHandlerService.store(handlerInfo); + gHandlerService.fillHandlerInfo(handlerInfo, ""); + do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.useHelperApp); + } +}); + +/** + * Tests that it is possible to save an nsIHandlerInfo instance containing an + * nsILocalHandlerApp instance pointing to an executable that doesn't exist, but + * this entry is ignored when reloading. + */ +add_task(function* test_store_localHandlerApp_missing() { + if (!("@mozilla.org/uriloader/dbus-handler-app;1" in Cc)) { + do_print("Skipping test because it does not apply to this platform."); + return; + } + + let missingHandlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"] + .createInstance(Ci.nsILocalHandlerApp); + missingHandlerApp.name = "Non-existing Handler"; + missingHandlerApp.executable = FileUtils.getFile("TmpD", ["nonexisting"]); + + yield deleteHandlerStore(); + + let handlerInfo = getKnownHandlerInfo("example/new"); + handlerInfo.preferredApplicationHandler = missingHandlerApp; + handlerInfo.possibleApplicationHandlers.appendElement(missingHandlerApp, false); + handlerInfo.possibleApplicationHandlers.appendElement(webHandlerApp, false); + gHandlerService.store(handlerInfo); + + yield unloadHandlerStore(); + + let actualHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("example/new"); + HandlerServiceTestUtils.assertHandlerInfoMatches(actualHandlerInfo, { + type: "example/new", + preferredAction: Ci.nsIHandlerInfo.saveToDisk, + alwaysAskBeforeHandling: false, + possibleApplicationHandlers: [expectedWebHandlerApp], + }); +}); + +/** + * Test saving and reloading an instance of nsIDBusHandlerApp. + */ +add_task(function* test_store_dBusHandlerApp() { + if (!("@mozilla.org/uriloader/dbus-handler-app;1" in Cc)) { + do_print("Skipping test because it does not apply to this platform."); + return; + } + + // Set up an nsIDBusHandlerApp instance for testing. + let dBusHandlerApp = Cc["@mozilla.org/uriloader/dbus-handler-app;1"] + .createInstance(Ci.nsIDBusHandlerApp); + dBusHandlerApp.name = "DBus Handler"; + dBusHandlerApp.service = "test.method.server"; + dBusHandlerApp.method = "Method"; + dBusHandlerApp.dBusInterface = "test.method.Type"; + dBusHandlerApp.objectPath = "/test/method/Object"; + let expectedDBusHandlerApp = { + name: dBusHandlerApp.name, + service: dBusHandlerApp.service, + method: dBusHandlerApp.method, + dBusInterface: dBusHandlerApp.dBusInterface, + objectPath: dBusHandlerApp.objectPath, + }; + + yield deleteHandlerStore(); + + let handlerInfo = getKnownHandlerInfo("example/new"); + handlerInfo.preferredApplicationHandler = dBusHandlerApp; + handlerInfo.possibleApplicationHandlers.appendElement(dBusHandlerApp, false); + gHandlerService.store(handlerInfo); + + yield unloadHandlerStore(); + + let actualHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("example/new"); + HandlerServiceTestUtils.assertHandlerInfoMatches(actualHandlerInfo, { + type: "example/new", + preferredAction: Ci.nsIHandlerInfo.saveToDisk, + alwaysAskBeforeHandling: false, + preferredApplicationHandler: expectedDBusHandlerApp, + possibleApplicationHandlers: [expectedDBusHandlerApp], + }); +}); + +/** + * Tests that it is possible to save an nsIHandlerInfo instance with a + * "preferredApplicationHandler" and no "possibleApplicationHandlers", but the + * former is always included in the latter list when reloading. + */ +add_task(function* test_store_possibleApplicationHandlers_includes_preferred() { + yield deleteHandlerStore(); + + let handlerInfo = getKnownHandlerInfo("example/new"); + handlerInfo.preferredApplicationHandler = localHandlerApp; + gHandlerService.store(handlerInfo); + + yield unloadHandlerStore(); + + let actualHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("example/new"); + HandlerServiceTestUtils.assertHandlerInfoMatches(actualHandlerInfo, { + type: "example/new", + preferredAction: Ci.nsIHandlerInfo.saveToDisk, + alwaysAskBeforeHandling: false, + preferredApplicationHandler: expectedLocalHandlerApp, + possibleApplicationHandlers: [expectedLocalHandlerApp], + }); +}); + +/** + * Tests that it is possible to save an nsIHandlerInfo instance with a + * "preferredApplicationHandler" that is not the first element in + * "possibleApplicationHandlers", but the former is always included as the first + * element of the latter list when reloading. + */ +add_task(function* test_store_possibleApplicationHandlers_preferred_first() { + yield deleteHandlerStore(); + + let handlerInfo = getKnownHandlerInfo("example/new"); + handlerInfo.preferredApplicationHandler = webHandlerApp; + // The preferred handler is appended after the other one. + handlerInfo.possibleApplicationHandlers.appendElement(localHandlerApp, false); + handlerInfo.possibleApplicationHandlers.appendElement(webHandlerApp, false); + gHandlerService.store(handlerInfo); + + yield unloadHandlerStore(); + + let actualHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("example/new"); + HandlerServiceTestUtils.assertHandlerInfoMatches(actualHandlerInfo, { + type: "example/new", + preferredAction: Ci.nsIHandlerInfo.saveToDisk, + alwaysAskBeforeHandling: false, + preferredApplicationHandler: expectedWebHandlerApp, + possibleApplicationHandlers: [ + expectedWebHandlerApp, + expectedLocalHandlerApp, + ], + }); +}); + +/** + * Tests that it is possible to save an nsIHandlerInfo instance with an + * uppercase file extension, but it is converted to lowercase when reloading. + */ +add_task(function* test_store_fileExtensions_lowercase() { + yield deleteHandlerStore(); + + let handlerInfo = getKnownHandlerInfo("example/new"); + handlerInfo.appendExtension("extension_test1"); + handlerInfo.appendExtension("EXTENSION_test2"); + gHandlerService.store(handlerInfo); + + yield unloadHandlerStore(); + + let actualHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("example/new"); + HandlerServiceTestUtils.assertHandlerInfoMatches(actualHandlerInfo, { + type: "example/new", + preferredAction: Ci.nsIHandlerInfo.saveToDisk, + alwaysAskBeforeHandling: false, + fileExtensions: [ + "extension_test1", + "extension_test2", + ], + }); +}); + +/** + * Tests that duplicates added with "appendExtension" or present in + * "possibleApplicationHandlers" are removed when saving and reloading. + */ +add_task(function* test_store_no_duplicates() { + yield deleteHandlerStore(); + + let handlerInfo = getKnownHandlerInfo("example/new"); + handlerInfo.preferredApplicationHandler = webHandlerApp; + handlerInfo.possibleApplicationHandlers.appendElement(webHandlerApp, false); + handlerInfo.possibleApplicationHandlers.appendElement(localHandlerApp, false); + handlerInfo.possibleApplicationHandlers.appendElement(localHandlerApp, false); + handlerInfo.possibleApplicationHandlers.appendElement(webHandlerApp, false); + handlerInfo.appendExtension("extension_test1"); + handlerInfo.appendExtension("extension_test2"); + handlerInfo.appendExtension("extension_test1"); + handlerInfo.appendExtension("EXTENSION_test1"); + gHandlerService.store(handlerInfo); + + yield unloadHandlerStore(); + + let actualHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("example/new"); + HandlerServiceTestUtils.assertHandlerInfoMatches(actualHandlerInfo, { + type: "example/new", + preferredAction: Ci.nsIHandlerInfo.saveToDisk, + alwaysAskBeforeHandling: false, + preferredApplicationHandler: expectedWebHandlerApp, + possibleApplicationHandlers: [ + expectedWebHandlerApp, + expectedLocalHandlerApp, + ], + fileExtensions: [ + "extension_test1", + "extension_test2", + ], + }); +}); + +/** + * Tests that "store" deletes properties that have their default values from + * the data store. This is mainly relevant for the JSON back-end. + * + * File extensions are never deleted once they have been associated. + */ +add_task(function* test_store_deletes_properties_except_extensions() { + yield deleteHandlerStore(); + + // Prepare an nsIHandlerInfo instance with all the properties set to values + // that will result in deletions. The preferredAction is also set to a defined + // value so we can more easily verify it later. + let handlerInfo = + HandlerServiceTestUtils.getBlankHandlerInfo("example/type.savetodisk"); + handlerInfo.preferredAction = Ci.nsIHandlerInfo.saveToDisk; + handlerInfo.alwaysAskBeforeHandling = false; + + // All the properties for "example/type.savetodisk" are present in the test + // data, so we load the data before overwriting their values. + yield copyTestDataToHandlerStore(); + gHandlerService.store(handlerInfo); + + // Now we can reload the data and verify that no extra values have been kept. + yield unloadHandlerStore(); + let actualHandlerInfo = + HandlerServiceTestUtils.getHandlerInfo("example/type.savetodisk"); + HandlerServiceTestUtils.assertHandlerInfoMatches(actualHandlerInfo, { + type: "example/type.savetodisk", + preferredAction: Ci.nsIHandlerInfo.saveToDisk, + alwaysAskBeforeHandling: false, + fileExtensions: [ + "example_two", + "example_three" + ], + }); +}); + +/** + * Tests the "overrideType" argument of "fillHandlerInfo". + */ +add_task(function* test_fillHandlerInfo_overrideType() { + // Test both MIME types and protocols. + for (let type of ["example/type.usesystemdefault", + "examplescheme.usesystemdefault"]) { + yield deleteHandlerStore(); + + // Create new nsIHandlerInfo instances before loading the test data. + let handlerInfoAbsent = HandlerServiceTestUtils.getHandlerInfo(type + "2"); + + // Fill the nsIHandlerInfo instance using the type that actually exists. + yield copyTestDataToHandlerStore(); + gHandlerService.fillHandlerInfo(handlerInfoAbsent, type); + HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfoAbsent, { + // While the data is populated from another type, the type is unchanged. + type: type + "2", + preferredAction: Ci.nsIHandlerInfo.useSystemDefault, + alwaysAskBeforeHandling: false, + possibleApplicationHandlers: [{ + name: "Example Possible Handler", + uriTemplate: "http://www.example.com/?url=%s", + }], + }); + } +}); + +/** + * Tests "getTypeFromExtension" including unknown extensions. + */ +add_task(function* test_getTypeFromExtension() { + yield copyTestDataToHandlerStore(); + + do_check_eq(gHandlerService.getTypeFromExtension(""), ""); + do_check_eq(gHandlerService.getTypeFromExtension("example_unknown"), ""); + do_check_eq(gHandlerService.getTypeFromExtension("example_one"), + "example/type.handleinternally"); + do_check_eq(gHandlerService.getTypeFromExtension("EXAMPLE_one"), + "example/type.handleinternally"); +}); + +/** + * Checks that the information stored in the handler service instance under + * testing matches the default handlers for the English locale. + */ +function* assertAllHandlerInfosMatchDefaultHandlers() { + let handlerInfos = HandlerServiceTestUtils.getAllHandlerInfos(); + + for (let type of ["irc", "ircs"]) { + HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfos.shift(), { + type, + preferredActionOSDependent: true, + possibleApplicationHandlers: [{ + name: "Mibbit", + uriTemplate: "https://www.mibbit.com/?url=%s", + }], + }); + } + HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfos.shift(), { type: "mailto", - preferredAction: Ci.nsIHandlerInfo.useSystemDefault, - alwaysAskBeforeHandling: false, + preferredActionOSDependent: true, possibleApplicationHandlers: [{ name: "Yahoo! Mail", uriTemplate: "https://compose.mail.yahoo.com/?To=%s", },{ name: "Gmail", uriTemplate: "https://mail.google.com/mail/?extsrc=mailto&url=%s", }], }); HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfos.shift(), { - type: "nonexistent/type", - preferredAction: Ci.nsIHandlerInfo.handleInternally, - alwaysAskBeforeHandling: false, - fileExtensions: ["pdf"], - }); - - HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfos.shift(), { type: "webcal", - preferredAction: Ci.nsIHandlerInfo.useHelperApp, - alwaysAskBeforeHandling: true, - preferredApplicationHandler: { - name: "30 Boxes", - uriTemplate: "http://30boxes.com/external/widget?refer=ff&url=%s", - }, + preferredActionOSDependent: true, possibleApplicationHandlers: [{ name: "30 Boxes", - uriTemplate: "http://30boxes.com/external/widget?refer=ff&url=%s", - },{ - name: "30 Boxes", uriTemplate: "https://30boxes.com/external/widget?refer=ff&url=%s", }], }); do_check_eq(handlerInfos.length, 0); -}); - -// Verify the load mechansim of hander service by -// - Start the hander service with DB -// - Do some modifications on DB first and reload it -add_task(function* testImportAndReload() { - // I. Prepare a testing ds first and do reload for handerService - yield prepareImportDB(); - Assert.deepEqual(HandlerServiceTestUtils.getAllHandlerInfoTypes(), - ["irc", "ircs", "mailto", "nonexistent/type", "webcal"]); - - // II. do modifications first and reload the DS again - gHandlerService.store(gzipHandlerInfo); - gHandlerService.remove(pdfHandlerInfo); - yield reloadData(); - Assert.deepEqual(HandlerServiceTestUtils.getAllHandlerInfoTypes(), - ["application/x-gzip", "irc", "ircs", "mailto", "webcal"]); -}); - -// Verify reload without DB -add_task(function* testReloadWithoutDB() { - yield removeImportDB(); - // If we have a defaultHandlersVersion pref, then assume that we're in the - // firefox tree and that we'll also have default handlers. - if (Services.prefs.getPrefType("gecko.handlerService.defaultHandlersVersion")){ - Assert.deepEqual(HandlerServiceTestUtils.getAllHandlerInfoTypes(), - ["irc", "ircs", "mailto", "webcal"]); - } -}); - -// Do the test for exist() with store() and remove() -add_task(function* testExists() { - yield prepareImportDB(); - - do_check_true(gHandlerService.exists(pdfHandlerInfo)); - do_check_false(gHandlerService.exists(gzipHandlerInfo)); - - // Remove the handler of irc first - let handler = ircHandlerInfo; - gHandlerService.remove(handler); - do_check_false(gHandlerService.exists(handler)); - gHandlerService.store(handler); - do_check_true(gHandlerService.exists(handler)); -}); - -// Do the test for GetTypeFromExtension() with store(), remove() and exist() -add_task(function* testGetTypeFromExtension() { - yield prepareImportDB(); - - let type = gHandlerService.getTypeFromExtension("doc"); - do_check_eq(type, ""); - type = gHandlerService.getTypeFromExtension("pdf"); - do_check_eq(type, "nonexistent/type"); - - // Load the "pdf" extension into the nsIHandlerInfo for "nonexistent/type". - gHandlerService.fillHandlerInfo(pdfHandlerInfo, ""); - - gHandlerService.remove(pdfHandlerInfo); - do_check_false(gHandlerService.exists(pdfHandlerInfo)); - type = gHandlerService.getTypeFromExtension("pdf"); - do_check_eq(type, ""); - - gHandlerService.store(pdfHandlerInfo); - do_check_true(gHandlerService.exists(pdfHandlerInfo)); - type = gHandlerService.getTypeFromExtension("pdf"); - do_check_eq(type, "nonexistent/type"); -}); - -// Test the functionality of fillHandlerInfo : -// - All the detail of handlerinfo are filled perferectly -// - The set of possible handlers included the preferred handler -add_task(function* testStoreAndFillHandlerInfo() { - yield removeImportDB(); - - // Get a handler info for a MIME type that neither the application nor - // the OS knows about and make sure its properties are set to the proper - // default values. - let handlerInfo = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type"); - let handlerInfo2 = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type2"); - handlerInfo2.preferredAction = Ci.nsIHandlerInfo.useSystemDefault; - handlerInfo2.preferredApplicationHandler = localHandler; - handlerInfo2.alwaysAskBeforeHandling = false; - handlerInfo2.appendExtension("type2"); - gHandlerService.store(handlerInfo2); +} - gHandlerService.fillHandlerInfo(handlerInfo, "nonexistent/type2"); - HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfo, { - type: "nonexistent/type", - preferredAction: Ci.nsIHandlerInfo.useSystemDefault, - alwaysAskBeforeHandling: false, - fileExtensions: ["type2"], - preferredApplicationHandler: { - name: "Local Handler", - executable, - }, - possibleApplicationHandlers: [{ - name: "Local Handler", - executable, - }], - }); -}); - -// Test the functionality of fillHandlerInfo : -// - Check the failure case by requesting a non-existent handler type -add_task(function* testFillHandlerInfoWithError() { - yield removeImportDB(); - - let handlerInfo = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type"); - - Assert.throws( - () => gHandlerService.fillHandlerInfo(handlerInfo, "nonexistent/type2"), - ex => ex.result == Cr.NS_ERROR_NOT_AVAILABLE); -}); - -// Test the functionality of fillHandlerInfo : -// - Prefer handler is the first one of possibleHandlers and with only one instance -add_task(function* testPreferHandlerIsTheFirstOrder() { - yield removeImportDB(); - - let handlerInfo = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type"); - let handlerInfo2 = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type2"); - handlerInfo2.preferredAction = Ci.nsIHandlerInfo.useHelperApp; - handlerInfo2.preferredApplicationHandler = webHandler; - handlerInfo2.possibleApplicationHandlers.appendElement(localHandler, false); - handlerInfo2.possibleApplicationHandlers.appendElement(webHandler, false); - handlerInfo2.alwaysAskBeforeHandling = false; - gHandlerService.store(handlerInfo2); - - gHandlerService.fillHandlerInfo(handlerInfo, "nonexistent/type2"); - HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfo, { - type: "nonexistent/type", - preferredAction: Ci.nsIHandlerInfo.useHelperApp, - alwaysAskBeforeHandling: false, - preferredApplicationHandler: { - name: webHandler.name, - uriTemplate: webHandler.uriTemplate, - }, - possibleApplicationHandlers: [{ - name: webHandler.name, - uriTemplate: webHandler.uriTemplate, - },{ - name: "Local Handler", - executable, - }], - }); -}); - -// Verify the handling of app handler: web handler -add_task(function* testStoreForWebHandler() { - yield removeImportDB(); - - let handlerInfo = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type"); - let handlerInfo2 = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type2"); - handlerInfo2.preferredAction = Ci.nsIHandlerInfo.useHelperApp; - handlerInfo2.preferredApplicationHandler = webHandler; - handlerInfo2.alwaysAskBeforeHandling = false; - gHandlerService.store(handlerInfo2); - - gHandlerService.fillHandlerInfo(handlerInfo, "nonexistent/type2"); - HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfo, { - type: "nonexistent/type", - preferredAction: Ci.nsIHandlerInfo.useHelperApp, - alwaysAskBeforeHandling: false, - preferredApplicationHandler: { - name: webHandler.name, - uriTemplate: webHandler.uriTemplate, - }, - possibleApplicationHandlers: [{ - name: webHandler.name, - uriTemplate: webHandler.uriTemplate, - }], - }); -}); - -// Verify the handling of app handler: DBus handler -add_task(function* testStoreForDBusHandler() { - if (!("@mozilla.org/uriloader/dbus-handler-app;1" in Cc)) { - do_print("Skipping test because it does not apply to this platform."); +/** + * Tests the default protocol handlers imported from the locale-specific data. + */ +add_task(function* test_default_protocol_handlers() { + if (!Services.prefs.getPrefType("gecko.handlerService.defaultHandlersVersion")) { + do_print("This platform or locale does not have default handlers."); return; } - yield removeImportDB(); - - let handlerInfo = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type"); - let handlerInfo2 = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type2"); - handlerInfo2.preferredAction = Ci.nsIHandlerInfo.useHelperApp; - handlerInfo2.preferredApplicationHandler = dBusHandler; - handlerInfo2.alwaysAskBeforeHandling = false; - gHandlerService.store(handlerInfo2); + // This will inject the default protocol handlers for the current locale. + yield deleteHandlerStore(); - gHandlerService.fillHandlerInfo(handlerInfo, "nonexistent/type2"); - let expectedHandler = { - name: dBusHandler.name, - service: dBusHandler.service, - method: dBusHandler.method, - dBusInterface: dBusHandler.dBusInterface, - objectPath: dBusHandler.objectPath, - }; - HandlerServiceTestUtils.assertHandlerInfoMatches(handlerInfo, { - type: "nonexistent/type", - preferredAction: Ci.nsIHandlerInfo.useHelperApp, - alwaysAskBeforeHandling: false, - preferredApplicationHandler: expectedHandler, - possibleApplicationHandlers: [expectedHandler], - }); + yield assertAllHandlerInfosMatchDefaultHandlers(); }); -// Test the functionality of _IsInHandlerArray() by injecting default handler again -// Since we don't have defaultHandlersVersion pref on Android, skip this test. -// Also skip for applications like Thunderbird which don't have all the prefs. -add_task(function* testIsInHandlerArray() { - if (AppConstants.platform == "android") { - do_print("Skipping test because it does not apply to this platform."); - return; - } +/** + * Tests that the default protocol handlers are not imported again from the + * locale-specific data if they already exist. + */ +add_task(function* test_default_protocol_handlers_no_duplicates() { if (!Services.prefs.getPrefType("gecko.handlerService.defaultHandlersVersion")) { - do_print("Skipping test: No pref gecko.handlerService.defaultHandlersVersion."); - return; - } - - yield removeImportDB(); - - let protoInfo = HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent"); - do_check_eq(protoInfo.possibleApplicationHandlers.length, 0); - gHandlerService.fillHandlerInfo(protoInfo, "ircs"); - do_check_eq(protoInfo.possibleApplicationHandlers.length, 1); - - // Remove the handler of irc first - let ircInfo = HandlerServiceTestUtils.getHandlerInfo("irc"); - gHandlerService.remove(ircInfo); - do_check_false(gHandlerService.exists(ircInfo)); - - let origPrefs = Services.prefs.getComplexValue( - "gecko.handlerService.defaultHandlersVersion", Ci.nsIPrefLocalizedString); - - // Set preference as an arbitrarily high number to force injecting - let string = Cc["@mozilla.org/pref-localizedstring;1"] - .createInstance(Ci.nsIPrefLocalizedString); - string.data = "999"; - Services.prefs.setComplexValue("gecko.handlerService.defaultHandlersVersion", - Ci.nsIPrefLocalizedString, string); - - // do reloading - yield reloadData(); - - // check "irc" exists again to make sure that injection actually happened - do_check_true(gHandlerService.exists(ircInfo)); - - // test "ircs" has only one handler to know the _IsInHandlerArray was invoked - protoInfo = HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent"); - do_check_false(gHandlerService.exists(protoInfo)); - gHandlerService.fillHandlerInfo(protoInfo, "ircs"); - do_check_eq(protoInfo.possibleApplicationHandlers.length, 1); - - // reset the preference after the test - Services.prefs.setComplexValue("gecko.handlerService.defaultHandlersVersion", - Ci.nsIPrefLocalizedString, origPrefs); -}); - -// Test the basic functionality of FillHandlerInfo() for protocol -// Since Android use mimeInfo to deal with mimeTypes and protocol, skip this test. -// Also skip for applications like Thunderbird which don't have all the prefs. -add_task(function* testFillHandlerInfoForProtocol() { - if (AppConstants.platform == "android") { - do_print("Skipping test because it does not apply to this platform."); - return; - } - if (!Services.prefs.getPrefType("gecko.handlerService.defaultHandlersVersion")) { - do_print("Skipping test: No pref gecko.handlerService.defaultHandlersVersion."); + do_print("This platform or locale does not have default handlers."); return; } - yield removeImportDB(); - - let protoInfo = HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent"); - - let ircInfo = HandlerServiceTestUtils.getHandlerInfo("irc"); - do_check_true(gHandlerService.exists(ircInfo)); - - gHandlerService.fillHandlerInfo(protoInfo, "irc"); - HandlerServiceTestUtils.assertHandlerInfoMatches(protoInfo, { - type: "nonexistent", - preferredAction: Ci.nsIHandlerInfo.useHelperApp, - alwaysAskBeforeHandling: true, - possibleApplicationHandlers: [{ - name: "Mibbit", - uriTemplate: "https://www.mibbit.com/?url=%s", - }], - }); -}); - - -// Test the functionality of store() and fillHandlerInfo for protocol -add_task(function* testStoreForProtocol() { - yield removeImportDB(); - - let protoInfo = HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent"); - let protoInfo2 = HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent2"); - protoInfo2.preferredAction = Ci.nsIHandlerInfo.useHelperApp; - protoInfo2.alwaysAskBeforeHandling = false; - protoInfo2.preferredApplicationHandler = webHandler; - gHandlerService.store(protoInfo2); + // This will inject the default protocol handlers for the current locale. + yield deleteHandlerStore(); - yield reloadData(); - do_check_true(gHandlerService.exists(protoInfo2)); + // Remove the "irc" handler so we can verify that the injection is repeated. + let ircHandlerInfo = HandlerServiceTestUtils.getHandlerInfo("irc"); + gHandlerService.remove(ircHandlerInfo); - gHandlerService.fillHandlerInfo(protoInfo, "nonexistent2"); - HandlerServiceTestUtils.assertHandlerInfoMatches(protoInfo, { - type: "nonexistent", - preferredAction: Ci.nsIHandlerInfo.useHelperApp, - alwaysAskBeforeHandling: false, - preferredApplicationHandler: { - name: webHandler.name, - uriTemplate: webHandler.uriTemplate, - }, - possibleApplicationHandlers: [{ - name: webHandler.name, - uriTemplate: webHandler.uriTemplate, - }], - }); -}); - -// Test the functionality of fillHandlerInfo when there is no overrideType -add_task(function* testFillHandlerInfoWithoutOverrideType() { - yield removeImportDB(); - - // mimeType - let mimeInfo = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type"); - let storedHandlerInfo = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent/type"); - storedHandlerInfo.preferredAction = Ci.nsIHandlerInfo.useSystemDefault; - storedHandlerInfo.preferredApplicationHandler = webHandler; - storedHandlerInfo.alwaysAskBeforeHandling = false; - gHandlerService.store(storedHandlerInfo); + let originalDefaultHandlersVersion = Services.prefs.getComplexValue( + "gecko.handlerService.defaultHandlersVersion", Ci.nsIPrefLocalizedString); - // protocol type - let protoInfo = HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent"); - let storedProtoInfo = - HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent"); - storedProtoInfo.preferredAction = Ci.nsIHandlerInfo.useHelperApp; - storedProtoInfo.alwaysAskBeforeHandling = false; - storedProtoInfo.preferredApplicationHandler = webHandler; - gHandlerService.store(storedProtoInfo); + // Set the preference to an arbitrarily high number to force injecting again. + Services.prefs.setStringPref("gecko.handlerService.defaultHandlersVersion", + "999"); + + yield unloadHandlerStore(); - // Get handlerInfo by fillHandlerInfo without overrideType - for (let handlerInfo of [mimeInfo, protoInfo]) { - let handlerInfo2 = storedProtoInfo; - if (handlerInfo.type == "nonexistent/type") { - handlerInfo2 = storedHandlerInfo; - } - gHandlerService.fillHandlerInfo(handlerInfo, null); - do_check_eq(handlerInfo.preferredAction, handlerInfo2.preferredAction); - do_check_eq(handlerInfo.alwaysAskBeforeHandling, - handlerInfo2.alwaysAskBeforeHandling); - do_check_eq(handlerInfo.preferredApplicationHandler.name, - handlerInfo2.preferredApplicationHandler.name); - let apps = handlerInfo.possibleApplicationHandlers.enumerate(); - let app; - if (AppConstants.platform == "android") { - app = apps.getNext().QueryInterface(Ci.nsIHandlerApp); - do_check_eq(app.name, "Android chooser"); - } - app = apps.getNext().QueryInterface(Ci.nsIWebHandlerApp); - do_check_eq(app.name, webHandler.name); - do_check_eq(app.uriTemplate, webHandler.uriTemplate); - } + // Check that "irc" exists to make sure that the injection was repeated. + do_check_true(gHandlerService.exists(ircHandlerInfo)); + + // There should be no duplicate handlers in the protocols. + yield assertAllHandlerInfosMatchDefaultHandlers(); + + Services.prefs.setStringPref("gecko.handlerService.defaultHandlersVersion", + originalDefaultHandlersVersion); }); - -// Test the functionality of fillHandlerInfo() : -// - Use "nsIHandlerInfo.useHelperApp" to replace "nsIHandlerInfo.alwaysAsk" for handlerInfo.preferredAction -// - Use "nsIHandlerInfo.useHelperApp" to replace unknow action for handlerInfo.preferredAction -add_task(function* testPreferredActionHandling() { - yield removeImportDB(); - - let protoInfo = HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent"); - let protoInfo2 = HandlerServiceTestUtils.getBlankHandlerInfo("nonexistent2"); - - for (let preferredAction of [ - Ci.nsIHandlerInfo.saveToDisk, - Ci.nsIHandlerInfo.useHelperApp, - Ci.nsIHandlerInfo.handleInternally, - Ci.nsIHandlerInfo.useSystemDefault - ]) { - protoInfo2.preferredAction = preferredAction; - gHandlerService.store(protoInfo2); - gHandlerService.fillHandlerInfo(protoInfo, "nonexistent2"); - do_check_eq(protoInfo.preferredAction, preferredAction); - } - - for (let preferredAction of [ - Ci.nsIHandlerInfo.alwaysAsk, - 999 - ]) { - protoInfo2.preferredAction = preferredAction; - gHandlerService.store(protoInfo2); - gHandlerService.fillHandlerInfo(protoInfo, "nonexistent2"); - do_check_eq(protoInfo.preferredAction, Ci.nsIHandlerInfo.useHelperApp); - } -});
--- a/uriloader/exthandler/tests/unit/handlers.json +++ b/uriloader/exthandler/tests/unit/handlers.json @@ -1,1 +1,90 @@ -{"version":{"en-US":999},"mimetypes":{"nonexistent/type":{"action":3,"askBeforeHandling":false,"fileExtensions":["pdf"]}},"schemes":{"webcal":{"action":1,"askBeforeHandling":true,"preferredHandler":{"name":"30 Boxes","uriTemplate":"http://30boxes.com/external/widget?refer=ff&url=%s"},"possibleHandlers":[{"name":"30 Boxes","uriTemplate":"https://30boxes.com/external/widget?refer=ff&url=%s"}]},"ircs":{"action":1,"askBeforeHandling":true,"fileExtensions":[],"possibleHandlers":[{"name":"Mibbit","uriTemplate":"https://www.mibbit.com/?url=%s"}]},"mailto":{"action":4,"askBeforeHandling":false,"possibleHandlers":[{"name":"Yahoo! Mail","uriTemplate":"https://compose.mail.yahoo.com/?To=%s"},{"name":"Gmail","uriTemplate":"https://mail.google.com/mail/?extsrc=mailto&url=%s"}]},"irc":{"action":1,"askBeforeHandling":true,"possibleHandlers":[{"name":"Mibbit","uriTemplate":"https://www.mibbit.com/?url=%s"}]}}} +{ + "defaultHandlersVersion": { + "en-US": 999 + }, + "mimeTypes": { + "example/type.handleinternally": { + "unknownProperty": "preserved", + "action": 3, + "extensions": [ + "example_one" + ] + }, + "example/type.savetodisk": { + "action": 0, + "ask": true, + "handlers": [ + { + "name": "Example Default Handler", + "uriTemplate": "https://www.example.com/?url=%s" + } + ], + "extensions": [ + "example_two", + "example_three" + ] + }, + "example/type.usehelperapp": { + "action": 2, + "ask": true, + "handlers": [ + { + "name": "Example Default Handler", + "uriTemplate": "https://www.example.com/?url=%s" + }, + { + "name": "Example Possible Handler One", + "uriTemplate": "http://www.example.com/?id=1&url=%s" + }, + { + "name": "Example Possible Handler Two", + "uriTemplate": "http://www.example.com/?id=2&url=%s" + } + ], + "extensions": [ + "example_two", + "example_three" + ] + }, + "example/type.usesystemdefault": { + "action": 4, + "handlers": [ + null, + { + "name": "Example Possible Handler", + "uriTemplate": "http://www.example.com/?url=%s" + } + ] + } + }, + "schemes": { + "examplescheme.usehelperapp": { + "action": 2, + "ask": true, + "handlers": [ + { + "name": "Example Default Handler", + "uriTemplate": "https://www.example.com/?url=%s" + }, + { + "name": "Example Possible Handler One", + "uriTemplate": "http://www.example.com/?id=1&url=%s" + }, + { + "name": "Example Possible Handler Two", + "uriTemplate": "http://www.example.com/?id=2&url=%s" + } + ] + }, + "examplescheme.usesystemdefault": { + "action": 4, + "handlers": [ + null, + { + "name": "Example Possible Handler", + "uriTemplate": "http://www.example.com/?url=%s" + } + ] + } + } +}
--- a/uriloader/exthandler/tests/unit/head.js +++ b/uriloader/exthandler/tests/unit/head.js @@ -6,33 +6,33 @@ * Initialization for tests related to invoking external handler applications. */ "use strict"; const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; Cu.import("resource://gre/modules/AppConstants.jsm"); +Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://testing-common/HandlerServiceTestUtils.jsm", this); Cu.import("resource://testing-common/TestUtils.jsm"); HandlerServiceTestUtils.Assert = Assert; do_get_profile(); let jsonPath = OS.Path.join(OS.Constants.Path.profileDir, "handlers.json"); -let rdfFile = Services.dirsvc.get("ProfD", Ci.nsIFile); -rdfFile.append("mimeTypes.rdf") +let rdfFile = FileUtils.getFile("ProfD", ["mimeTypes.rdf"]); function deleteDatasourceFile() { if (rdfFile.exists()) { rdfFile.remove(false); } } // Delete the existing datasource file, if any, so we start from scratch.
--- a/uriloader/exthandler/tests/unit/mimeTypes-android.rdf +++ b/uriloader/exthandler/tests/unit/mimeTypes-android.rdf @@ -1,78 +1,100 @@ <?xml version="1.0"?> <RDF:RDF xmlns:NC="http://home.netscape.com/NC-rdf#" xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> - <RDF:Description RDF:about="urn:mimetype:handler:webcal" - NC:alwaysAsk="true"> - <NC:externalApplication RDF:resource="urn:mimetype:externalApplication:webcal"/> - <NC:possibleApplication RDF:resource="urn:handler:web:http://30boxes.com/external/widget?refer=ff&url=%s"/> - <NC:possibleApplication RDF:resource="urn:handler:web:https://30boxes.com/external/widget?refer=ff&url=%s"/> - </RDF:Description> - <RDF:Description RDF:about="urn:mimetype:nonexistent/type" - NC:value="nonexistent/type" - NC:fileExtensions="pdf"> - <NC:handlerProp RDF:resource="urn:mimetype:handler:nonexistent/type"/> - </RDF:Description> - <RDF:Description RDF:about="urn:mimetype:ircs" - NC:value="ircs"> - <NC:handlerProp RDF:resource="urn:mimetype:handler:ircs"/> + <RDF:Seq RDF:about="urn:mimetypes:root"> + <RDF:li RDF:resource="urn:mimetype:example/type.handleinternally"/> + <RDF:li RDF:resource="urn:mimetype:example/type.savetodisk"/> + <RDF:li RDF:resource="urn:mimetype:example/type.usehelperapp"/> + <RDF:li RDF:resource="urn:mimetype:example/type.usesystemdefault"/> + <RDF:li RDF:resource="urn:mimetype:examplescheme.usehelperapp"/> + <RDF:li RDF:resource="urn:mimetype:examplescheme.usesystemdefault"/> + </RDF:Seq> + <RDF:Description RDF:about="urn:mimetype:example/type.usehelperapp" + NC:value="example/type.usehelperapp"> + <NC:handlerProp RDF:resource="urn:mimetype:handler:example/type.usehelperapp"/> + <NC:fileExtensions>example_two</NC:fileExtensions> + <NC:fileExtensions>example_three</NC:fileExtensions> </RDF:Description> - <RDF:Description RDF:about="urn:handler:web:https://compose.mail.yahoo.com/?To=%s" - NC:prettyName="Yahoo! Mail" - NC:uriTemplate="https://compose.mail.yahoo.com/?To=%s" /> - <RDF:Description RDF:about="urn:handler:web:https://30boxes.com/external/widget?refer=ff&url=%s" - NC:prettyName="30 Boxes" - NC:uriTemplate="https://30boxes.com/external/widget?refer=ff&url=%s" /> - <RDF:Seq RDF:about="urn:mimetypes:root"> - <RDF:li RDF:resource="urn:mimetype:irc"/> - <RDF:li RDF:resource="urn:mimetype:ircs"/> - <RDF:li RDF:resource="urn:mimetype:mailto"/> - <RDF:li RDF:resource="urn:mimetype:nonexistent/type"/> - <RDF:li RDF:resource="urn:mimetype:webcal"/> - </RDF:Seq> - <RDF:Description RDF:about="urn:mimetype:handler:nonexistent/type" + <RDF:Description RDF:about="urn:handler:web:http://www.example.com/?id=2&url=%s" + NC:prettyName="Example Possible Handler Two" + NC:uriTemplate="http://www.example.com/?id=2&url=%s" /> + <RDF:Description RDF:about="urn:mimetype:handler:example/type.handleinternally" NC:handleInternal="true" NC:alwaysAsk="false" /> - <RDF:Description RDF:about="urn:handler:web:https://www.mibbit.com/?url=%s" - NC:prettyName="Mibbit" - NC:uriTemplate="https://www.mibbit.com/?url=%s" /> - <RDF:Description RDF:about="urn:mimetype:externalApplication:webcal" - NC:prettyName="30 Boxes" - NC:uriTemplate="http://30boxes.com/external/widget?refer=ff&url=%s" /> - <RDF:Description RDF:about="urn:mimetype:mailto" - NC:value="mailto"> - <NC:handlerProp RDF:resource="urn:mimetype:handler:mailto"/> + <RDF:Description RDF:about="urn:mimetype:examplescheme.usesystemdefault" + NC:value="examplescheme.usesystemdefault"> + <NC:handlerProp RDF:resource="urn:mimetype:handler:examplescheme.usesystemdefault"/> + </RDF:Description> + <RDF:Description RDF:about="urn:mimetype:externalApplication:example/type.usehelperapp" + NC:prettyName="Example Default Handler" + NC:uriTemplate="https://www.example.com/?url=%s" /> + <RDF:Description RDF:about="urn:mimetype:externalApplication:example/type.savetodisk" + NC:prettyName="Example Default Handler" + NC:uriTemplate="https://www.example.com/?url=%s" /> + <RDF:Description RDF:about="urn:mimetype:example/type.usesystemdefault" + NC:value="example/type.usesystemdefault"> + <NC:handlerProp RDF:resource="urn:mimetype:handler:example/type.usesystemdefault"/> + </RDF:Description> + <RDF:Description RDF:about="urn:mimetype:examplescheme.usehelperapp" + NC:value="examplescheme.usehelperapp"> + <NC:handlerProp RDF:resource="urn:mimetype:handler:examplescheme.usehelperapp"/> </RDF:Description> - <RDF:Description RDF:about="urn:mimetype:handler:mailto" + <RDF:Description RDF:about="urn:handler:web:http://www.example.com/?url=%s" + NC:prettyName="Example Possible Handler" + NC:uriTemplate="http://www.example.com/?url=%s" /> + <RDF:Description RDF:about="urn:mimetype:example/type.savetodisk" + NC:value="example/type.savetodisk"> + <NC:handlerProp RDF:resource="urn:mimetype:handler:example/type.savetodisk"/> + <NC:fileExtensions>example_two</NC:fileExtensions> + <NC:fileExtensions>example_three</NC:fileExtensions> + </RDF:Description> + <RDF:Description RDF:about="urn:mimetype:handler:examplescheme.usehelperapp" + NC:alwaysAsk="true"> + <NC:externalApplication RDF:resource="urn:mimetype:externalApplication:examplescheme.usehelperapp"/> + <NC:possibleApplication RDF:resource="urn:handler:web:https://www.example.com/?url=%s"/> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?id=1&url=%s"/> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?id=2&url=%s"/> + </RDF:Description> + <RDF:Description RDF:about="urn:mimetype:example/type.handleinternally" + NC:value="example/type.handleinternally" + NC:fileExtensions="example_one"> + <NC:handlerProp RDF:resource="urn:mimetype:handler:example/type.handleinternally"/> + </RDF:Description> + <RDF:Description RDF:about="urn:handler:web:http://www.example.com/?id=1&url=%s" + NC:prettyName="Example Possible Handler One" + NC:uriTemplate="http://www.example.com/?id=1&url=%s" /> + <RDF:Description RDF:about="urn:handler:web:https://www.example.com/?url=%s" + NC:prettyName="Example Default Handler" + NC:uriTemplate="https://www.example.com/?url=%s" /> + <RDF:Description RDF:about="urn:mimetype:handler:examplescheme.usesystemdefault" NC:useSystemDefault="true" NC:alwaysAsk="false"> - <NC:possibleApplication RDF:resource="urn:handler:web:https://compose.mail.yahoo.com/?To=%s"/> - <NC:possibleApplication RDF:resource="urn:handler:web:https://mail.google.com/mail/?extsrc=mailto&url=%s"/> - </RDF:Description> - <RDF:Description RDF:about="urn:mimetype:handler:ircs" - NC:alwaysAsk="true"> - <NC:possibleApplication RDF:resource="urn:handler:web:https://www.mibbit.com/?url=%s"/> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?url=%s"/> </RDF:Description> - <RDF:Description RDF:about="urn:handler:web:https://mail.google.com/mail/?extsrc=mailto&url=%s" - NC:prettyName="Gmail" - NC:uriTemplate="https://mail.google.com/mail/?extsrc=mailto&url=%s" /> - <RDF:Description RDF:about="urn:mimetype:webcal" - NC:value="webcal"> - <NC:handlerProp RDF:resource="urn:mimetype:handler:webcal"/> + <RDF:Description RDF:about="urn:mimetype:handler:example/type.usesystemdefault" + NC:useSystemDefault="true" + NC:alwaysAsk="false"> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?url=%s"/> </RDF:Description> - <RDF:Description RDF:about="urn:handler:web:http://30boxes.com/external/widget?refer=ff&url=%s" - NC:prettyName="30 Boxes" - NC:uriTemplate="http://30boxes.com/external/widget?refer=ff&url=%s" /> <RDF:Description RDF:about="urn:root" NC:en-US_defaultHandlersVersion="999" /> - <RDF:Description RDF:about="urn:mimetype:handler:irc" + <RDF:Description RDF:about="urn:mimetype:handler:example/type.usehelperapp" NC:alwaysAsk="true"> - <NC:possibleApplication RDF:resource="urn:handler:web:https://www.mibbit.com/?url=%s"/> + <NC:externalApplication RDF:resource="urn:mimetype:externalApplication:example/type.usehelperapp"/> + <NC:possibleApplication RDF:resource="urn:handler:web:https://www.example.com/?url=%s"/> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?id=1&url=%s"/> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?id=2&url=%s"/> </RDF:Description> - <RDF:Description RDF:about="urn:mimetype:irc" - NC:value="irc"> - <NC:handlerProp RDF:resource="urn:mimetype:handler:irc"/> - </RDF:Description> + <RDF:Description RDF:about="urn:mimetype:externalApplication:examplescheme.usehelperapp" + NC:prettyName="Example Default Handler" + NC:uriTemplate="https://www.example.com/?url=%s" /> <RDF:Description RDF:about="urn:mimetypes"> <NC:MIME-types RDF:resource="urn:mimetypes:root"/> </RDF:Description> + <RDF:Description RDF:about="urn:mimetype:handler:example/type.savetodisk" + NC:saveToDisk="true" + NC:alwaysAsk="true"> + <NC:externalApplication RDF:resource="urn:mimetype:externalApplication:example/type.savetodisk"/> + <NC:possibleApplication RDF:resource="urn:handler:web:https://www.example.com/?url=%s"/> + </RDF:Description> </RDF:RDF>
--- a/uriloader/exthandler/tests/unit/mimeTypes.rdf +++ b/uriloader/exthandler/tests/unit/mimeTypes.rdf @@ -1,83 +1,105 @@ <?xml version="1.0"?> <RDF:RDF xmlns:NC="http://home.netscape.com/NC-rdf#" xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <RDF:Description RDF:about="urn:mimetype:externalApplication:example/type.usehelperapp" + NC:prettyName="Example Default Handler" + NC:uriTemplate="https://www.example.com/?url=%s" /> + <RDF:Description RDF:about="urn:scheme:examplescheme.usesystemdefault" + NC:value="examplescheme.usesystemdefault"> + <NC:handlerProp RDF:resource="urn:scheme:handler:examplescheme.usesystemdefault"/> + </RDF:Description> + <RDF:Description RDF:about="urn:scheme:handler:examplescheme.usehelperapp" + NC:alwaysAsk="true"> + <NC:externalApplication RDF:resource="urn:scheme:externalApplication:examplescheme.usehelperapp"/> + <NC:possibleApplication RDF:resource="urn:handler:web:https://www.example.com/?url=%s"/> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?id=1&url=%s"/> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?id=2&url=%s"/> + </RDF:Description> + <RDF:Description RDF:about="urn:handler:web:http://www.example.com/?url=%s" + NC:prettyName="Example Possible Handler" + NC:uriTemplate="http://www.example.com/?url=%s" /> + <RDF:Description RDF:about="urn:scheme:handler:examplescheme.usesystemdefault" + NC:useSystemDefault="true" + NC:alwaysAsk="false"> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?url=%s"/> + </RDF:Description> + <RDF:Description RDF:about="urn:mimetype:example/type.savetodisk" + NC:value="example/type.savetodisk"> + <NC:handlerProp RDF:resource="urn:mimetype:handler:example/type.savetodisk"/> + <NC:fileExtensions>example_two</NC:fileExtensions> + <NC:fileExtensions>example_three</NC:fileExtensions> + </RDF:Description> + <RDF:Description RDF:about="urn:mimetype:example/type.usesystemdefault" + NC:value="example/type.usesystemdefault"> + <NC:handlerProp RDF:resource="urn:mimetype:handler:example/type.usesystemdefault"/> + </RDF:Description> + <RDF:Description RDF:about="urn:mimetype:handler:example/type.savetodisk" + NC:saveToDisk="true" + NC:alwaysAsk="true"> + <NC:externalApplication RDF:resource="urn:mimetype:externalApplication:example/type.savetodisk"/> + <NC:possibleApplication RDF:resource="urn:handler:web:https://www.example.com/?url=%s"/> + </RDF:Description> + <RDF:Description RDF:about="urn:handler:web:http://www.example.com/?id=1&url=%s" + NC:prettyName="Example Possible Handler One" + NC:uriTemplate="http://www.example.com/?id=1&url=%s" /> + <RDF:Description RDF:about="urn:mimetype:example/type.handleinternally" + NC:value="example/type.handleinternally" + NC:fileExtensions="example_one"> + <NC:handlerProp RDF:resource="urn:mimetype:handler:example/type.handleinternally"/> + </RDF:Description> + <RDF:Description RDF:about="urn:root" + NC:en-US_defaultHandlersVersion="999" /> + <RDF:Description RDF:about="urn:mimetype:handler:example/type.usesystemdefault" + NC:useSystemDefault="true" + NC:alwaysAsk="false"> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?url=%s"/> + </RDF:Description> + <RDF:Description RDF:about="urn:scheme:externalApplication:examplescheme.usehelperapp" + NC:prettyName="Example Default Handler" + NC:uriTemplate="https://www.example.com/?url=%s" /> + <RDF:Seq RDF:about="urn:mimetypes:root"> + <RDF:li RDF:resource="urn:mimetype:example/type.handleinternally"/> + <RDF:li RDF:resource="urn:mimetype:example/type.savetodisk"/> + <RDF:li RDF:resource="urn:mimetype:example/type.usehelperapp"/> + <RDF:li RDF:resource="urn:mimetype:example/type.usesystemdefault"/> + </RDF:Seq> + <RDF:Description RDF:about="urn:mimetype:handler:example/type.usehelperapp" + NC:alwaysAsk="true"> + <NC:externalApplication RDF:resource="urn:mimetype:externalApplication:example/type.usehelperapp"/> + <NC:possibleApplication RDF:resource="urn:handler:web:https://www.example.com/?url=%s"/> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?id=1&url=%s"/> + <NC:possibleApplication RDF:resource="urn:handler:web:http://www.example.com/?id=2&url=%s"/> + </RDF:Description> + <RDF:Description RDF:about="urn:scheme:examplescheme.usehelperapp" + NC:value="examplescheme.usehelperapp"> + <NC:handlerProp RDF:resource="urn:scheme:handler:examplescheme.usehelperapp"/> + </RDF:Description> + <RDF:Seq RDF:about="urn:schemes:root"> + <RDF:li RDF:resource="urn:scheme:examplescheme.usehelperapp"/> + <RDF:li RDF:resource="urn:scheme:examplescheme.usesystemdefault"/> + </RDF:Seq> + <RDF:Description RDF:about="urn:handler:web:https://www.example.com/?url=%s" + NC:prettyName="Example Default Handler" + NC:uriTemplate="https://www.example.com/?url=%s" /> + <RDF:Description RDF:about="urn:mimetype:externalApplication:example/type.savetodisk" + NC:prettyName="Example Default Handler" + NC:uriTemplate="https://www.example.com/?url=%s" /> + <RDF:Description RDF:about="urn:mimetypes"> + <NC:MIME-types RDF:resource="urn:mimetypes:root"/> + </RDF:Description> + <RDF:Description RDF:about="urn:handler:web:http://www.example.com/?id=2&url=%s" + NC:prettyName="Example Possible Handler Two" + NC:uriTemplate="http://www.example.com/?id=2&url=%s" /> <RDF:Description RDF:about="urn:schemes"> <NC:Protocol-Schemes RDF:resource="urn:schemes:root"/> </RDF:Description> - <RDF:Seq RDF:about="urn:mimetypes:root"> - <RDF:li RDF:resource="urn:mimetype:nonexistent/type"/> - </RDF:Seq> - <RDF:Description RDF:about="urn:mimetype:handler:nonexistent/type" + <RDF:Description RDF:about="urn:mimetype:handler:example/type.handleinternally" NC:handleInternal="true" NC:alwaysAsk="false" /> - <RDF:Description RDF:about="urn:scheme:irc" - NC:value="irc"> - <NC:handlerProp RDF:resource="urn:scheme:handler:irc"/> - </RDF:Description> - <RDF:Description RDF:about="urn:scheme:externalApplication:webcal" - NC:prettyName="30 Boxes" - NC:uriTemplate="http://30boxes.com/external/widget?refer=ff&url=%s" /> - <RDF:Description RDF:about="urn:scheme:handler:ircs" - NC:alwaysAsk="true"> - <NC:possibleApplication RDF:resource="urn:handler:web:https://www.mibbit.com/?url=%s"/> - </RDF:Description> - <RDF:Description RDF:about="urn:scheme:handler:mailto" - NC:useSystemDefault="true" - NC:alwaysAsk="false"> - <NC:possibleApplication RDF:resource="urn:handler:web:https://compose.mail.yahoo.com/?To=%s"/> - <NC:possibleApplication RDF:resource="urn:handler:web:https://mail.google.com/mail/?extsrc=mailto&url=%s"/> - </RDF:Description> - <RDF:Description RDF:about="urn:scheme:ircs" - NC:value="ircs"> - <NC:handlerProp RDF:resource="urn:scheme:handler:ircs"/> - </RDF:Description> - <RDF:Description RDF:about="urn:handler:web:https://www.mibbit.com/?url=%s" - NC:prettyName="Mibbit" - NC:uriTemplate="https://www.mibbit.com/?url=%s" /> - <RDF:Description RDF:about="urn:mimetype:nonexistent/type" - NC:value="nonexistent/type" - NC:fileExtensions="pdf"> - <NC:handlerProp RDF:resource="urn:mimetype:handler:nonexistent/type"/> + <RDF:Description RDF:about="urn:mimetype:example/type.usehelperapp" + NC:value="example/type.usehelperapp"> + <NC:handlerProp RDF:resource="urn:mimetype:handler:example/type.usehelperapp"/> + <NC:fileExtensions>example_two</NC:fileExtensions> + <NC:fileExtensions>example_three</NC:fileExtensions> </RDF:Description> - <RDF:Description RDF:about="urn:handler:web:https://compose.mail.yahoo.com/?To=%s" - NC:prettyName="Yahoo! Mail" - NC:uriTemplate="https://compose.mail.yahoo.com/?To=%s" /> - <RDF:Description RDF:about="urn:handler:web:https://mail.google.com/mail/?extsrc=mailto&url=%s" - NC:prettyName="Gmail" - NC:uriTemplate="https://mail.google.com/mail/?extsrc=mailto&url=%s" /> - <RDF:Description RDF:about="urn:scheme:webcal" - NC:value="webcal"> - <NC:handlerProp RDF:resource="urn:scheme:handler:webcal"/> - </RDF:Description> - <RDF:Description RDF:about="urn:root" - NC:en-US_defaultHandlersVersion="999" /> - <RDF:Description RDF:about="urn:scheme:mailto" - NC:value="mailto"> - <NC:handlerProp RDF:resource="urn:scheme:handler:mailto"/> - </RDF:Description> - <RDF:Description RDF:about="urn:scheme:handler:irc" - NC:alwaysAsk="true"> - <NC:possibleApplication RDF:resource="urn:handler:web:https://www.mibbit.com/?url=%s"/> - </RDF:Description> - <RDF:Description RDF:about="urn:scheme:handler:webcal" - NC:alwaysAsk="true"> - <NC:externalApplication RDF:resource="urn:scheme:externalApplication:webcal"/> - <NC:possibleApplication RDF:resource="urn:handler:web:http://30boxes.com/external/widget?refer=ff&url=%s"/> - <NC:possibleApplication RDF:resource="urn:handler:web:https://30boxes.com/external/widget?refer=ff&url=%s"/> - </RDF:Description> - <RDF:Seq RDF:about="urn:schemes:root"> - <RDF:li RDF:resource="urn:scheme:irc"/> - <RDF:li RDF:resource="urn:scheme:ircs"/> - <RDF:li RDF:resource="urn:scheme:mailto"/> - <RDF:li RDF:resource="urn:scheme:webcal"/> - </RDF:Seq> - <RDF:Description RDF:about="urn:mimetypes"> - <NC:MIME-types RDF:resource="urn:mimetypes:root"/> - </RDF:Description> - <RDF:Description RDF:about="urn:handler:web:http://30boxes.com/external/widget?refer=ff&url=%s" - NC:prettyName="30 Boxes" - NC:uriTemplate="http://30boxes.com/external/widget?refer=ff&url=%s" /> - <RDF:Description RDF:about="urn:handler:web:https://30boxes.com/external/widget?refer=ff&url=%s" - NC:prettyName="30 Boxes" - NC:uriTemplate="https://30boxes.com/external/widget?refer=ff&url=%s" /> </RDF:RDF>
--- a/uriloader/exthandler/tests/unit/test_handlerService_json.js +++ b/uriloader/exthandler/tests/unit/test_handlerService_json.js @@ -1,35 +1,65 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/** - * Tests the handlerService interfaces using JSON backend. +/* + * Tests the nsIHandlerService interface using the JSON backend. */ XPCOMUtils.defineLazyServiceGetter(this, "gHandlerService", "@mozilla.org/uriloader/handler-service-json;1", "nsIHandlerService"); -var scriptFile = do_get_file("common_test_handlerService.js"); -Services.scriptloader.loadSubScript(NetUtil.newURI(scriptFile).spec); +/** + * Unloads the nsIHandlerService data store, so the back-end file can be + * accessed or modified, and the new data will be loaded at the next access. + */ +let unloadHandlerStore = Task.async(function* () { + // If this function is called before the nsIHandlerService instance has been + // initialized for the first time, the observer below will not be registered. + // We have to force initialization to prevent the function from stalling. + gHandlerService; + + let promise = TestUtils.topicObserved("handlersvc-json-replace-complete"); + Services.obs.notifyObservers(null, "handlersvc-json-replace", null); + yield promise; +}); -var prepareImportDB = Task.async(function* () { - yield reloadData(); +/** + * Unloads the data store and deletes it. + */ +let deleteHandlerStore = Task.async(function* () { + yield unloadHandlerStore(); + + yield OS.File.remove(jsonPath, { ignoreAbsent: true }); +}); + +/** + * Unloads the data store and replaces it with the test data file. + */ +let copyTestDataToHandlerStore = Task.async(function* () { + yield unloadHandlerStore(); yield OS.File.copy(do_get_file("handlers.json").path, jsonPath); }); -var removeImportDB = Task.async(function* () { - yield reloadData(); - - yield OS.File.remove(jsonPath, { ignoreAbsent: true }); -}); +var scriptFile = do_get_file("common_test_handlerService.js"); +Services.scriptloader.loadSubScript(NetUtil.newURI(scriptFile).spec); -var reloadData = Task.async(function* () { - // Force the initialization of handlerService to prevent observer is not initialized yet. - let svc = gHandlerService; - let promise = TestUtils.topicObserved("handlersvc-json-replace-complete"); - Services.obs.notifyObservers(null, "handlersvc-json-replace", null); - yield promise; +/** + * Ensures forward compatibility by checking that the "store" method preserves + * unknown properties in the test data. This is specific to the JSON back-end. + */ +add_task(function* test_store_keeps_unknown_properties() { + // Create a new nsIHandlerInfo instance before loading the test data. + yield deleteHandlerStore(); + let handlerInfo = + HandlerServiceTestUtils.getHandlerInfo("example/type.handleinternally"); + + yield copyTestDataToHandlerStore(); + gHandlerService.store(handlerInfo); + + yield unloadHandlerStore(); + let data = JSON.parse(new TextDecoder().decode(yield OS.File.read(jsonPath))); + do_check_eq(data.mimeTypes["example/type.handleinternally"].unknownProperty, + "preserved"); });
--- a/uriloader/exthandler/tests/unit/test_handlerService_rdf.js +++ b/uriloader/exthandler/tests/unit/test_handlerService_rdf.js @@ -1,37 +1,48 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -/** - * Tests the handlerService interfaces using RDF backend. +/* + * Tests the nsIHandlerService interface using the JSON backend. */ XPCOMUtils.defineLazyServiceGetter(this, "gHandlerService", "@mozilla.org/uriloader/handler-service;1", "nsIHandlerService"); -var scriptFile = do_get_file("common_test_handlerService.js"); -Services.scriptloader.loadSubScript(NetUtil.newURI(scriptFile).spec); +/** + * Unloads the nsIHandlerService data store, so the back-end file can be + * accessed or modified, and the new data will be loaded at the next access. + */ +let unloadHandlerStore = Task.async(function* () { + // If this function is called before the nsIHandlerService instance has been + // initialized for the first time, the observer below will not be registered. + // We have to force initialization to prevent the function from stalling. + gHandlerService; + + let promise = TestUtils.topicObserved("handlersvc-rdf-replace-complete"); + Services.obs.notifyObservers(null, "handlersvc-rdf-replace", null); + yield promise; +}); -var prepareImportDB = Task.async(function* () { - yield reloadData(); +/** + * Unloads the data store and deletes it. + */ +let deleteHandlerStore = Task.async(function* () { + yield unloadHandlerStore(); + + yield OS.File.remove(rdfFile.path, { ignoreAbsent: true }); +}); + +/** + * Unloads the data store and replaces it with the test data file. + */ +let copyTestDataToHandlerStore = Task.async(function* () { + yield unloadHandlerStore(); let fileName = AppConstants.platform == "android" ? "mimeTypes-android.rdf" : "mimeTypes.rdf"; yield OS.File.copy(do_get_file(fileName).path, rdfFile.path); }); -var removeImportDB = Task.async(function* () { - yield reloadData(); - - yield OS.File.remove(rdfFile.path, { ignoreAbsent: true }); -}); - -var reloadData = Task.async(function* () { - // Force the initialization of handlerService to prevent observer is not initialized yet. - let svc = gHandlerService; - let promise = TestUtils.topicObserved("handlersvc-rdf-replace-complete"); - Services.obs.notifyObservers(null, "handlersvc-rdf-replace", null); - yield promise; -}); +var scriptFile = do_get_file("common_test_handlerService.js"); +Services.scriptloader.loadSubScript(NetUtil.newURI(scriptFile).spec);