--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -454,16 +454,18 @@
@RESPATH@/components/mozProtocolHandler.js
@RESPATH@/components/mozProtocolHandler.manifest
@RESPATH@/components/nsDefaultCLH.manifest
@RESPATH@/components/nsDefaultCLH.js
@RESPATH@/components/nsContentPrefService.manifest
@RESPATH@/components/nsContentPrefService.js
@RESPATH@/components/nsContentDispatchChooser.manifest
@RESPATH@/components/nsContentDispatchChooser.js
+@RESPATH@/components/nsHandlerService-json.manifest
+@RESPATH@/components/nsHandlerService-json.js
@RESPATH@/components/nsHandlerService.manifest
@RESPATH@/components/nsHandlerService.js
@RESPATH@/components/nsWebHandlerApp.manifest
@RESPATH@/components/nsWebHandlerApp.js
@RESPATH@/components/satchel.manifest
@RESPATH@/components/nsFormAutoComplete.js
@RESPATH@/components/FormHistoryStartup.js
@RESPATH@/components/nsInputListAutoComplete.js
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -325,16 +325,18 @@
@BINPATH@/components/MainProcessSingleton.js
@BINPATH@/components/ContentProcessSingleton.js
@BINPATH@/components/nsURLFormatter.manifest
@BINPATH@/components/nsURLFormatter.js
@BINPATH@/components/txEXSLTRegExFunctions.manifest
@BINPATH@/components/txEXSLTRegExFunctions.js
@BINPATH@/components/nsContentPrefService.manifest
@BINPATH@/components/nsContentPrefService.js
+@BINPATH@/components/nsHandlerService-json.manifest
+@BINPATH@/components/nsHandlerService-json.js
@BINPATH@/components/nsHandlerService.manifest
@BINPATH@/components/nsHandlerService.js
@BINPATH@/components/nsWebHandlerApp.manifest
@BINPATH@/components/nsWebHandlerApp.js
@BINPATH@/components/satchel.manifest
@BINPATH@/components/nsFormAutoComplete.js
@BINPATH@/components/FormHistoryStartup.js
@BINPATH@/components/nsInputListAutoComplete.js
--- a/uriloader/exthandler/moz.build
+++ b/uriloader/exthandler/moz.build
@@ -103,16 +103,18 @@ if CONFIG['MOZ_ENABLE_DBUS']:
]
if CONFIG['MOZ_ENABLE_CONTENTACTION']:
UNIFIED_SOURCES += [
'nsContentHandlerApp.cpp',
]
EXTRA_COMPONENTS += [
+ 'nsHandlerService-json.js',
+ 'nsHandlerService-json.manifest',
'nsHandlerService.js',
'nsHandlerService.manifest',
'nsWebHandlerApp.js',
'nsWebHandlerApp.manifest',
]
IPDL_SOURCES += [
'PExternalHelperApp.ipdl',
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/nsHandlerService-json.js
@@ -0,0 +1,385 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "JSONFile",
+ "resource://gre/modules/JSONFile.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gExternalProtocolService",
+ "@mozilla.org/uriloader/external-protocol-service;1",
+ "nsIExternalProtocolService");
+XPCOMUtils.defineLazyServiceGetter(this, "gMIMEService",
+ "@mozilla.org/mime;1",
+ "nsIMIMEService");
+
+function HandlerService() {
+ // Observe handlersvc-json-replace so we can switch to the datasource
+ Services.obs.addObserver(this, "handlersvc-json-replace", true);
+}
+
+HandlerService.prototype = {
+
+ classID: Components.ID("{220cc253-b60f-41f6-b9cf-fdcb325f970f}"),
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsISupportsWeakReference,
+ 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"),
+ dataPostProcessor: this._dataPostProcessor.bind(this),
+ });
+ this.__store.ensureDataReady();
+ this._updateDB();
+ }
+ return this.__store;
+ },
+
+ _dataPostProcessor(data) {
+ return data.schemes ? data : { version: {}, mimetypes: {}, schemes: {} };
+ },
+
+ _updateDB() {
+ try {
+
+ let locale = Cc["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Ci.nsIXULChromeRegistry)
+ .getSelectedLocale("global");
+ let prefsDefaultHandlersVersion = Number(Services.prefs.getComplexValue(
+ "gecko.handlerService.defaultHandlersVersion",
+ Ci.nsIPrefLocalizedString).data);
+
+ let defaultHandlersVersion = this._store.data.version[locale] || 0;
+ if (defaultHandlersVersion < prefsDefaultHandlersVersion ) {
+ this._injectNewDefaults();
+ this._store.data.version[locale] = prefsDefaultHandlersVersion;
+ }
+ } catch (ex) {
+ Cu.reportError(ex);
+ }
+ },
+
+ _injectNewDefaults() {
+ let schemesPrefBranch = Services.prefs.getBranch("gecko.handlerService.schemes.");
+ let schemePrefList = schemesPrefBranch.getChildList("");
+
+ let schemes = {};
+
+ // read all the scheme prefs into a hash
+ for (let schemePrefName of schemePrefList) {
+
+ let [scheme, handlerNumber, attribute] = schemePrefName.split(".");
+
+ try {
+ let attrData =
+ schemesPrefBranch.getComplexValue(schemePrefName,
+ Ci.nsIPrefLocalizedString).data;
+ if (!(scheme in schemes)) {
+ schemes[scheme] = {};
+ }
+
+ if (!(handlerNumber in schemes[scheme])) {
+ schemes[scheme][handlerNumber] = {};
+ }
+
+ schemes[scheme][handlerNumber][attribute] = attrData;
+ } catch (ex) {}
+ }
+
+ for (let scheme of Object.keys(schemes)) {
+
+ // This clause is essentially a reimplementation of
+ // nsIExternalProtocolHandlerService.getProtocolHandlerInfo().
+ // Necessary because we want to use this instance of the service,
+ // but nsIExternalProtocolHandlerService would call the RDF-based based version
+ // until we complete the conversion.
+ let osDefaultHandlerFound = {};
+ let protoInfo = gExternalProtocolService.getProtocolHandlerInfoFromOS(scheme,
+ osDefaultHandlerFound);
+
+ if (this.exists(protoInfo)) {
+ this.fillHandlerInfo(protoInfo, null);
+ } else {
+ gExternalProtocolService.setProtocolHandlerDefaults(protoInfo,
+ 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);
+ }
+ }
+
+ 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);
+ },
+
+ // nsIObserver
+ observe(subject, topic, data) {
+ if (topic != "handlersvc-json-replace") {
+ return;
+ }
+ 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 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,
+ };
+
+ if (handlerInfo.description) {
+ handlerObj.description = handlerInfo.description;
+ }
+
+ let preferredHandler = handlerInfo.preferredApplicationHandler;
+ if (preferredHandler) {
+ let serializable = this.handlerAppToSerializable(preferredHandler);
+ if (serializable) {
+ handlerObj.preferredHandler = serializable;
+ }
+ }
+
+ 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 (possibleHandlers.length) {
+ handlerObj.possibleHandlers = possibleHandlers;
+ }
+
+ if (handlerInfo instanceof Ci.nsIMIMEInfo) {
+ let extEnumerator = handlerInfo.getFileExtensions();
+ let extensions = [];
+ while (extEnumerator.hasMore()) {
+ let extension = extEnumerator.getNext();
+ if (!extensions.includes(extension)) {
+ extensions.push(extension);
+ }
+ }
+ if (extensions.length) {
+ handlerObj.fileExtensions = 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);
+ }
+ handlerInfo.description = storedHandlerInfo.description;
+
+ // 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;
+ }
+
+ 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);
+ }
+ }
+ }
+
+ // We always store "askBeforeHandling" in the JSON file. Just use this value.
+ handlerInfo.alwaysAskBeforeHandling = storedHandlerInfo.askBeforeHandling;
+
+ if (handlerInfo instanceof Ci.nsIMIMEInfo) {
+ if (storedHandlerInfo.fileExtensions) {
+ for (let extension of storedHandlerInfo.fileExtensions) {
+ handlerInfo.appendExtension(extension);
+ }
+ }
+ }
+ },
+
+ /**
+ * @param handler
+ * A nsIHandlerApp handler app
+ * @returns Serializable representation of a handler app object.
+ */
+ handlerAppToSerializable(handler) {
+ if (handler instanceof Ci.nsILocalHandlerApp) {
+ return {
+ name: handler.name,
+ path: handler.executable.path,
+ };
+ } else if (handler instanceof Ci.nsIWebHandlerApp) {
+ return {
+ name: handler.name,
+ uriTemplate: handler.uriTemplate,
+ };
+ } else if (handler instanceof Ci.nsIDBusHandlerApp) {
+ return {
+ name: handler.name,
+ service: handler.service,
+ method: handler.method,
+ objectPath: handler.objectPath,
+ dBusInterface: handler.dBusInterface,
+ };
+ }
+ // If the handler is an unknown handler type, return null.
+ // Android default application handler is the case.
+ return null;
+ },
+
+ /**
+ * @param handlerObj
+ * Serializable representation of a handler object.
+ * @returns {nsIHandlerApp} the handler app, if any; otherwise null
+ */
+ 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.executable = file;
+ } catch (ex) {
+ return null;
+ }
+ } else if ("uriTemplate" in handlerObj) {
+ 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.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
+ * based on which type of handlerInfo is provided.
+ */
+ _getHandlerListByHandlerInfoType(handlerInfo) {
+ if (handlerInfo instanceof Ci.nsIMIMEInfo) {
+ return this._store.data.mimetypes;
+ }
+ return this._store.data.schemes;
+ },
+
+ // nsIHandlerService
+ exists(handlerInfo) {
+ return handlerInfo.type in this._getHandlerListByHandlerInfoType(handlerInfo);
+ },
+
+ // nsIHandlerService
+ remove(handlerInfo) {
+ delete this._getHandlerListByHandlerInfoType(handlerInfo)[handlerInfo.type];
+ this._store.saveSoon();
+ },
+
+ // nsIHandlerService
+ getTypeFromExtension(fileExtension) {
+ let extension = fileExtension.toLowerCase();
+ let mimeTypes = this._store.data.mimetypes;
+ for (let type of Object.keys(mimeTypes)) {
+ if (mimeTypes[type].fileExtensions &&
+ mimeTypes[type].fileExtensions.includes(extension)) {
+ return type;
+ }
+ }
+ return "";
+ },
+
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HandlerService]);
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/nsHandlerService-json.manifest
@@ -0,0 +1,2 @@
+component {220cc253-b60f-41f6-b9cf-fdcb325f970f} nsHandlerService-json.js
+contract @mozilla.org/uriloader/handler-service-json;1 {220cc253-b60f-41f6-b9cf-fdcb325f970f} process=main
--- a/uriloader/exthandler/nsHandlerService.js
+++ b/uriloader/exthandler/nsHandlerService.js
@@ -113,19 +113,19 @@ HandlerService.prototype = {
this._observerSvc.addObserver(this, "profile-before-change", false);
// Observe xpcom-shutdown so we can remove these observers
// when the application shuts down.
this._observerSvc.addObserver(this, "xpcom-shutdown", false);
// Observe profile-do-change so that non-default profiles get upgraded too
this._observerSvc.addObserver(this, "profile-do-change", false);
-
- // do any necessary updating of the datastore
- this._updateDB();
+
+ // Observe handlersvc-rdf-replace so we can switch to the datasource
+ this._observerSvc.addObserver(this, "handlersvc-rdf-replace", false);
},
_updateDB: function HS__updateDB() {
try {
var defaultHandlersVersion = this._datastoreDefaultHandlersVersion;
} catch(ex) {
// accessing the datastore failed, we can't update anything
return;
@@ -155,16 +155,17 @@ HandlerService.prototype = {
var currentLocale = chromeRegistry.getSelectedLocale("global");
return currentLocale;
},
_destroy: function HS__destroy() {
this._observerSvc.removeObserver(this, "profile-before-change");
this._observerSvc.removeObserver(this, "xpcom-shutdown");
this._observerSvc.removeObserver(this, "profile-do-change");
+ this._observerSvc.removeObserver(this, "handlersvc-rdf-replace");
// XXX Should we also null references to all the services that get stored
// by our memoizing getters in the Convenience Getters section?
},
_onProfileChange: function HS__onProfileChange() {
// Lose our reference to the datasource so we reacquire it
// from the new profile the next time we need it.
@@ -284,17 +285,24 @@ HandlerService.prototype = {
case "profile-before-change":
this._onProfileChange();
break;
case "xpcom-shutdown":
this._destroy();
break;
case "profile-do-change":
this._updateDB();
- break;
+ break;
+ case "handlersvc-rdf-replace":
+ if (this.__ds) {
+ this._rdf.UnregisterDataSource(this.__ds);
+ this.__ds = null;
+ }
+ this._observerSvc.notifyObservers(null, "handlersvc-rdf-replace-complete", null);
+ break;
}
},
//**************************************************************************//
// nsIHandlerService
enumerate: function HS_enumerate() {
@@ -308,17 +316,18 @@ HandlerService.prototype = {
fillHandlerInfo: function HS_fillHandlerInfo(aHandlerInfo, aOverrideType) {
var type = aOverrideType || aHandlerInfo.type;
var typeID = this._getTypeID(this._getClass(aHandlerInfo), type);
// Determine whether or not information about this handler is available
// in the datastore by looking for its "value" property, which stores its
// type and should always be present.
if (!this._hasValue(typeID, NC_VALUE))
- throw Cr.NS_ERROR_NOT_AVAILABLE;
+ throw new Components.Exception("handlerSvc fillHandlerInfo: don't know this type",
+ Cr.NS_ERROR_NOT_AVAILABLE);
// Retrieve the human-readable description of the type.
if (this._hasValue(typeID, NC_DESCRIPTION))
aHandlerInfo.description = this._getValue(typeID, NC_DESCRIPTION);
// Note: for historical reasons, we don't actually check that the type
// record has a "handlerProp" property referencing the info record. It's
// unclear whether or not we should start doing this check; perhaps some
@@ -683,24 +692,40 @@ HandlerService.prototype = {
default:
this._removeTarget(infoID, NC_SAVE_TO_DISK);
this._removeTarget(infoID, NC_HANDLE_INTERNALLY);
this._removeTarget(infoID, NC_USE_SYSTEM_DEFAULT);
break;
}
},
+ _handlerAppIsUnknownType: function HS__handlerAppIsUnknownType(aHandlerApp) {
+ if (aHandlerApp instanceof Ci.nsILocalHandlerApp ||
+ aHandlerApp instanceof Ci.nsIWebHandlerApp ||
+ aHandlerApp instanceof Ci.nsIDBusHandlerApp) {
+ return false;
+ } else {
+ return true;
+ }
+ },
+
+
_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;
+ }
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
@@ -739,16 +764,21 @@ HandlerService.prototype = {
}
// Next, store any new handler apps.
var newHandlerApps =
aHandlerInfo.possibleApplicationHandlers.enumerate();
while (newHandlerApps.hasMoreElements()) {
let handlerApp =
newHandlerApps.getNext().QueryInterface(Ci.nsIHandlerApp);
+ // If the handlerApp is an unknown type, ignore it.
+ // Android default application handler is the case.
+ if (this._handlerAppIsUnknownType(handlerApp)) {
+ continue;
+ }
let handlerAppID = this._getPossibleHandlerAppID(handlerApp);
if (!this._hasResourceAssertion(infoID, NC_POSSIBLE_APP, handlerAppID)) {
this._storeHandlerApp(handlerAppID, handlerApp);
this._addResourceAssertion(infoID, NC_POSSIBLE_APP, handlerAppID);
}
delete currentHandlerApps[handlerAppID];
}
@@ -907,16 +937,18 @@ HandlerService.prototype = {
var file = this._dirSvc.get("UMimTyp", Ci.nsIFile);
// FIXME: make this a memoizing getter if we use it anywhere else.
var ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var fileHandler = ioService.getProtocolHandler("file").
QueryInterface(Ci.nsIFileProtocolHandler);
this.__ds =
this._rdf.GetDataSourceBlocking(fileHandler.getURLSpecFromFile(file));
+ // do any necessary updating of the datastore
+ this._updateDB();
}
return this.__ds;
},
//**************************************************************************//
// Datastore Utils
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/common_test_handlerService.js
@@ -0,0 +1,488 @@
+/* -*- 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).
+ */
+
+"use strict"
+
+Cu.import("resource://gre/modules/osfile.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://testing-common/TestUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gMIMEService",
+ "@mozilla.org/mime;1",
+ "nsIMIMEService");
+XPCOMUtils.defineLazyServiceGetter(this, "gExternalProtocolService",
+ "@mozilla.org/uriloader/external-protocol-service;1",
+ "nsIExternalProtocolService");
+
+const pdfHandlerInfo = gMIMEService.getFromTypeAndExtension("application/pdf", null);
+const gzipHandlerInfo = gMIMEService.getFromTypeAndExtension("application/x-gzip", null);
+const ircHandlerInfo = gExternalProtocolService.getProtocolHandlerInfo("irc");
+
+let executable = HandlerServiceTest._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;
+ }
+};
+
+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;
+ }
+};
+
+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;
+ }
+};
+
+function run_test() {
+ do_get_profile();
+ run_next_test();
+}
+
+/**
+ * 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.
+ */
+function getBlankHandlerInfo(type) {
+ let handlerInfo = gMIMEService.getFromTypeAndExtension(type, null);
+ do_check_true(handlerInfo.alwaysAskBeforeHandling);
+
+ if (handlerInfo.possibleApplicationHandlers.length) {
+ let apps = handlerInfo.possibleApplicationHandlers.enumerate();
+ let app = apps.getNext().QueryInterface(Ci.nsIHandlerApp);
+ do_check_eq(app.name, "Android chooser");
+ } else {
+ do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.saveToDisk);
+ }
+ return handlerInfo;
+}
+
+/**
+ * Get a handler info for a protocol that neither the application nor the OS
+ * knows about and make sure its properties are set to the proper default
+ * values.
+ */
+function getBlankHandlerInfoForProtocol(type) {
+ let handlerInfo = gExternalProtocolService.getProtocolHandlerInfo(type);
+ do_check_true(handlerInfo.alwaysAskBeforeHandling);
+
+ if (handlerInfo.possibleApplicationHandlers.length) {
+ let apps = handlerInfo.possibleApplicationHandlers.enumerate();
+ let app = apps.getNext().QueryInterface(Ci.nsIHandlerApp);
+ do_check_eq(app.name, "Android chooser");
+ } else {
+ do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.alwaysAsk);
+ }
+
+ return handlerInfo;
+}
+
+function getAllTypesByEnumerate() {
+ let handlerInfos = gHandlerService.enumerate();
+ let types = [];
+ while (handlerInfos.hasMoreElements()) {
+ let handlerInfo = handlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo);
+ types.push(handlerInfo.type);
+ }
+ return types.sort();
+}
+
+// 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(getAllTypesByEnumerate(), ["application/pdf", "irc", "ircs", "mailto", "webcal"]);
+
+ // II. do modifications first and reload the DS again
+ gHandlerService.store(gzipHandlerInfo);
+ gHandlerService.remove(pdfHandlerInfo);
+ yield reloadData();
+ Assert.deepEqual(getAllTypesByEnumerate(), ["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(getAllTypesByEnumerate(), ["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, "application/pdf");
+
+ 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, "application/pdf");
+});
+
+// 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 = getBlankHandlerInfo("nonexistent/type");
+ let handlerInfo2 = 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");
+ do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.useSystemDefault);
+ if (Services.appinfo.widgetToolkit == "android") {
+ do_check_eq(handlerInfo.possibleApplicationHandlers.length, 2);
+ } else {
+ do_check_eq(handlerInfo.possibleApplicationHandlers.length, 1);
+ }
+ do_check_false(handlerInfo.alwaysAskBeforeHandling);
+ let extEnumerator = handlerInfo.getFileExtensions();
+ do_check_eq(extEnumerator.getNext(), ".type2");
+ do_check_false(extEnumerator.hasMore());
+ do_check_eq(handlerInfo.preferredApplicationHandler.name, "Local Handler");
+ let apps = handlerInfo.possibleApplicationHandlers.enumerate();
+ let app;
+ if (Services.appinfo.widgetToolkit == "android") {
+ app = apps.getNext().QueryInterface(Ci.nsIHandlerApp);
+ do_check_eq(app.name, "Android chooser");
+ }
+ app = apps.getNext().QueryInterface(Ci.nsILocalHandlerApp);
+ do_check_eq(executable.path, app.executable.path);
+ do_check_eq(app.name, localHandler.name);
+ do_check_false(apps.hasMoreElements());
+});
+
+// Test the functionality of fillHandlerInfo :
+// - Check the failure case by requesting a non-existent handler type
+add_task(function* testFillHandlerInfoWithError() {
+ yield removeImportDB();
+
+ let handlerInfo = 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 = getBlankHandlerInfo("nonexistent/type");
+ let handlerInfo2 = 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");
+ let apps = handlerInfo.possibleApplicationHandlers.enumerate();
+ let app;
+ if (Services.appinfo.widgetToolkit == "android") {
+ app = apps.getNext().QueryInterface(Ci.nsIHandlerApp);
+ do_check_eq(app.name, "Android chooser");
+ }
+ app = apps.getNext().QueryInterface(Ci.nsIHandlerApp);
+ do_check_eq(app.name, webHandler.name);
+ app = apps.getNext().QueryInterface(Ci.nsIHandlerApp);
+ do_check_eq(app.name, localHandler.name);
+ do_check_false(apps.hasMoreElements());
+});
+
+// Verify the handling of app handler: web handler
+add_task(function* testStoreForWebHandler() {
+ yield removeImportDB();
+
+ let handlerInfo = getBlankHandlerInfo("nonexistent/type");
+ let handlerInfo2 = getBlankHandlerInfo("nonexistent/type2");
+ handlerInfo2.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
+ handlerInfo2.preferredApplicationHandler = webHandler;
+ handlerInfo2.alwaysAskBeforeHandling = false;
+ gHandlerService.store(handlerInfo2);
+
+ gHandlerService.fillHandlerInfo(handlerInfo, "nonexistent/type2");
+ let apps = handlerInfo.possibleApplicationHandlers.enumerate();
+ let app;
+ if (Services.appinfo.widgetToolkit == "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);
+});
+
+// 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.");
+ return;
+ }
+
+ yield removeImportDB();
+
+ let handlerInfo = getBlankHandlerInfo("nonexistent/type");
+ let handlerInfo2 = getBlankHandlerInfo("nonexistent/type2");
+ handlerInfo2.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
+ handlerInfo2.preferredApplicationHandler = dBusHandler;
+ handlerInfo2.alwaysAskBeforeHandling = false;
+ gHandlerService.store(handlerInfo2);
+
+ gHandlerService.fillHandlerInfo(handlerInfo, "nonexistent/type2");
+ let app = handlerInfo.preferredApplicationHandler.QueryInterface(Ci.nsIDBusHandlerApp);
+ do_check_eq(app.name, dBusHandler.name);
+ do_check_eq(app.service, dBusHandler.service);
+ do_check_eq(app.method, dBusHandler.method);
+ do_check_eq(app.objectPath, dBusHandler.objectPath);
+ do_check_eq(app.dBusInterface, dBusHandler.dBusInterface);
+});
+
+// Test the functionality of _IsInHandlerArray() by injecting default handler again
+// Since we don't have defaultHandlersVersion pref on Android, skip this test.
+add_task(function* testIsInHandlerArray() {
+ if (Services.appinfo.widgetToolkit == "android") {
+ do_print("Skipping test because it does not apply to this platform.");
+ return;
+ }
+
+ yield removeImportDB();
+
+ let protoInfo = getBlankHandlerInfoForProtocol("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 osDefaultHandlerFound = {};
+ let ircInfo = gExternalProtocolService.getProtocolHandlerInfoFromOS("irc",
+ osDefaultHandlerFound);
+ 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 = getBlankHandlerInfoForProtocol("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.
+add_task(function* testFillHandlerInfoForProtocol() {
+ if (Services.appinfo.widgetToolkit == "android") {
+ do_print("Skipping test because it does not apply to this platform.");
+ return;
+ }
+
+ yield removeImportDB();
+
+ let osDefaultHandlerFound = {};
+ let protoInfo = getBlankHandlerInfoForProtocol("nonexistent");
+
+ let ircInfo = gExternalProtocolService.getProtocolHandlerInfoFromOS("irc",
+ osDefaultHandlerFound);
+ do_check_true(gHandlerService.exists(ircInfo));
+
+ gHandlerService.fillHandlerInfo(protoInfo, "irc");
+ do_check_true(protoInfo.alwaysAskBeforeHandling);
+ let possibleHandlers = protoInfo.possibleApplicationHandlers;
+ do_check_eq(possibleHandlers.length, 1);
+ let app = possibleHandlers.enumerate().getNext().QueryInterface(Ci.nsIWebHandlerApp);
+ do_check_eq(app.name, "Mibbit");
+ do_check_eq(app.uriTemplate, "https://www.mibbit.com/?url=%s");
+});
+
+
+// Test the functionality of store() and fillHandlerInfo for protocol
+add_task(function* testStoreForProtocol() {
+ yield removeImportDB();
+
+ let protoInfo = getBlankHandlerInfoForProtocol("nonexistent");
+ let protoInfo2 = getBlankHandlerInfoForProtocol("nonexistent2");
+ protoInfo2.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
+ protoInfo2.alwaysAskBeforeHandling = false;
+ protoInfo2.preferredApplicationHandler = webHandler;
+ gHandlerService.store(protoInfo2);
+
+ yield reloadData();
+ do_check_true(gHandlerService.exists(protoInfo2));
+
+ gHandlerService.fillHandlerInfo(protoInfo, "nonexistent2");
+ do_check_eq(protoInfo.preferredAction, Ci.nsIHandlerInfo.useHelperApp);
+ do_check_false(protoInfo.alwaysAskBeforeHandling);
+ do_check_eq(protoInfo.preferredApplicationHandler.name, webHandler.name);
+ let apps = protoInfo.possibleApplicationHandlers.enumerate();
+ let app;
+ if (Services.appinfo.widgetToolkit == "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);
+});
+
+// Test the functionality of fillHandlerInfo when there is no overrideType
+add_task(function* testFillHandlerInfoWithoutOverrideType() {
+ yield removeImportDB();
+
+ // mimeType
+ let mimeInfo = getBlankHandlerInfo("nonexistent/type");
+ let storedHandlerInfo = getBlankHandlerInfo("nonexistent/type");
+ storedHandlerInfo.preferredAction = Ci.nsIHandlerInfo.useSystemDefault;
+ storedHandlerInfo.preferredApplicationHandler = webHandler;
+ storedHandlerInfo.alwaysAskBeforeHandling = false;
+ gHandlerService.store(storedHandlerInfo);
+
+ // protocol type
+ let protoInfo = getBlankHandlerInfoForProtocol("nonexistent");
+ let storedProtoInfo = getBlankHandlerInfoForProtocol("nonexistent");
+ storedProtoInfo.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
+ storedProtoInfo.alwaysAskBeforeHandling = false;
+ storedProtoInfo.preferredApplicationHandler = webHandler;
+ gHandlerService.store(storedProtoInfo);
+
+ // 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 (Services.appinfo.widgetToolkit == "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);
+ }
+});
+
+// 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 = getBlankHandlerInfoForProtocol("nonexistent");
+ let protoInfo2 = getBlankHandlerInfoForProtocol("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);
+ }
+});
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/handlers.json
@@ -0,0 +1,1 @@
+{"version":{"en-US":999},"mimetypes":{"application/pdf":{"description":"PDF document","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"}]}}}
--- a/uriloader/exthandler/tests/unit/head_handlerService.js
+++ b/uriloader/exthandler/tests/unit/head_handlerService.js
@@ -47,16 +47,17 @@ var HandlerServiceTest = {
// Initialization & Destruction
init: function HandlerServiceTest_init() {
// Register ourselves as a directory provider for the datasource file
// if there isn't one registered already.
try {
this._dirSvc.get("UMimTyp", Ci.nsIFile);
} catch (ex) {
+ do_get_profile();
this._dirSvc.registerProvider(this);
this._providerRegistered = true;
}
// Delete the existing datasource file, if any, so we start from scratch.
// We also do this after finishing the tests, so there shouldn't be an old
// file lying around, but just in case we delete it here as well.
this._deleteDatasourceFile();
@@ -81,17 +82,17 @@ var HandlerServiceTest = {
// nsIDirectoryServiceProvider
getFile: function HandlerServiceTest_getFile(property, persistent) {
this.log("getFile: requesting " + property);
persistent.value = true;
if (property == "UMimTyp") {
- var datasourceFile = this._dirSvc.get("CurProcD", Ci.nsIFile);
+ var datasourceFile = this._dirSvc.get("ProfD", Ci.nsIFile);
datasourceFile.append("mimeTypes.rdf");
return datasourceFile;
}
// This causes extraneous errors to show up in the log when the directory
// service asks us first for CurProcD and MozBinD. I wish there was a way
// to suppress those errors.
this.log("the following NS_ERROR_FAILURE exception in " +
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/mimeTypes.rdf
@@ -0,0 +1,79 @@
+<?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: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:mimetype:application/pdf"
+ NC:value="application/pdf"
+ NC:fileExtensions="pdf">
+ <NC:handlerProp RDF:resource="urn:mimetype:handler:application/pdf"/>
+ </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:root"
+ NC:en-US_defaultHandlersVersion="999" />
+ <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:mailto"
+ NC:value="mailto">
+ <NC:handlerProp RDF:resource="urn:scheme:handler:mailto"/>
+ </RDF:Description>
+ <RDF:Seq RDF:about="urn:schemes:root">
+ <RDF:li RDF:resource="urn:scheme:webcal"/>
+ <RDF:li RDF:resource="urn:scheme:ircs"/>
+ <RDF:li RDF:resource="urn:scheme:mailto"/>
+ <RDF:li RDF:resource="urn:scheme:irc"/>
+ </RDF:Seq>
+ <RDF:Description RDF:about="urn:scheme:webcal"
+ NC:value="webcal">
+ <NC:handlerProp RDF:resource="urn:scheme:handler:webcal"/>
+ </RDF:Description>
+ <RDF:Seq RDF:about="urn:mimetypes:root">
+ <RDF:li RDF:resource="urn:mimetype:application/pdf"/>
+ </RDF:Seq>
+ <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:Description RDF:about="urn:schemes">
+ <NC:Protocol-Schemes RDF:resource="urn:schemes:root"/>
+ </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:scheme:handler:webcal"
+ NC:alwaysAsk="true">
+ <NC:possibleApplication RDF:resource="urn:handler:web:https://30boxes.com/external/widget?refer=ff&url=%s"/>
+ <NC:externalApplication RDF:resource="urn:scheme:externalApplication:webcal"/>
+ </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: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:handler:application/pdf"
+ NC:handleInternal="true"
+ NC:alwaysAsk="false" />
+ <RDF:Description RDF:about="urn:mimetypes">
+ <NC:MIME-types RDF:resource="urn:mimetypes:root"/>
+ </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:ircs"
+ NC:value="ircs">
+ <NC:handlerProp RDF:resource="urn:scheme:handler:ircs"/>
+ </RDF:Description>
+ <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:RDF>
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/test_handlerService_json.js
@@ -0,0 +1,46 @@
+/* -*- 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.
+ */
+
+"use strict"
+
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+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);
+
+var prepareImportDB = Task.async(function* () {
+ yield reloadData();
+
+ let dst = OS.Path.join(OS.Constants.Path.profileDir, "handlers.json");
+ yield OS.File.copy(do_get_file("handlers.json").path, dst);
+ Assert.ok((yield OS.File.exists(dst)), "should have a DB now");
+});
+
+var removeImportDB = Task.async(function* () {
+ yield reloadData();
+
+ let dst = OS.Path.join(OS.Constants.Path.profileDir, "handlers.json");
+ yield OS.File.remove(dst);
+ Assert.ok(!(yield OS.File.exists(dst)), "should not have a DB now");
+});
+
+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;
+});
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/test_handlerService_rdf.js
@@ -0,0 +1,44 @@
+/* -*- 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.
+ */
+
+"use strict"
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+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);
+
+var prepareImportDB = Task.async(function* () {
+ yield reloadData();
+
+ let dst = HandlerServiceTest._dirSvc.get("UMimTyp", Ci.nsIFile);
+ yield OS.File.copy(do_get_file("mimeTypes.rdf").path, dst.path);
+ Assert.ok((yield OS.File.exists(dst.path)), "should have a DB now");
+});
+
+var removeImportDB = Task.async(function* () {
+ yield reloadData();
+ HandlerServiceTest._deleteDatasourceFile();
+ let dst = HandlerServiceTest._dirSvc.get("UMimTyp", Ci.nsIFile);
+ Assert.ok(!(yield OS.File.exists(dst.path)), "should not have a DB now");
+});
+
+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;
+});
--- a/uriloader/exthandler/tests/unit/xpcshell.ini
+++ b/uriloader/exthandler/tests/unit/xpcshell.ini
@@ -1,14 +1,20 @@
[DEFAULT]
head = head_handlerService.js
run-sequentially = Bug 912235 - Intermittent failures
+firefox-appdir = browser
+support-files = common_test_handlerService.js
[test_getTypeFromExtension_ext_to_type_mapping.js]
[test_getTypeFromExtension_with_empty_Content_Type.js]
[test_badMIMEType.js]
[test_handlerService.js]
support-files = mailcap
# Bug 676997: test consistently fails on Android
fail-if = os == "android"
+[test_handlerService_json.js]
+support-files = handlers.json
+[test_handlerService_rdf.js]
+support-files = mimeTypes.rdf
[test_punycodeURIs.js]
# Bug 676997: test consistently fails on Android
fail-if = os == "android"