summaryrefslogtreecommitdiffstats
path: root/uriloader/exthandler/tests/HandlerServiceTestUtils.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'uriloader/exthandler/tests/HandlerServiceTestUtils.sys.mjs')
-rw-r--r--uriloader/exthandler/tests/HandlerServiceTestUtils.sys.mjs251
1 files changed, 251 insertions, 0 deletions
diff --git a/uriloader/exthandler/tests/HandlerServiceTestUtils.sys.mjs b/uriloader/exthandler/tests/HandlerServiceTestUtils.sys.mjs
new file mode 100644
index 0000000000..7ff2c17fe9
--- /dev/null
+++ b/uriloader/exthandler/tests/HandlerServiceTestUtils.sys.mjs
@@ -0,0 +1,251 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Shared functions for tests related to invoking external handler applications.
+ */
+
+import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+import { Assert } from "resource://testing-common/Assert.sys.mjs";
+
+const lazy = {};
+
+XPCOMUtils.defineLazyServiceGetter(
+ lazy,
+ "gExternalProtocolService",
+ "@mozilla.org/uriloader/external-protocol-service;1",
+ "nsIExternalProtocolService"
+);
+XPCOMUtils.defineLazyServiceGetter(
+ lazy,
+ "gMIMEService",
+ "@mozilla.org/mime;1",
+ "nsIMIMEService"
+);
+XPCOMUtils.defineLazyServiceGetter(
+ lazy,
+ "gHandlerService",
+ "@mozilla.org/uriloader/handler-service;1",
+ "nsIHandlerService"
+);
+
+export var HandlerServiceTestUtils = {
+ /**
+ * Retrieves the names of all the MIME types and protocols configured in the
+ * handler service instance currently under testing.
+ *
+ * @return Array of strings like "example/type" or "example-scheme", sorted
+ * alphabetically regardless of category.
+ */
+ getAllHandlerInfoTypes() {
+ return Array.from(
+ lazy.gHandlerService.enumerate(),
+ info => info.type
+ ).sort();
+ },
+
+ /**
+ * Retrieves all the configured handlers for MIME types and protocols.
+ *
+ * @note The nsIHandlerInfo instances returned by the "enumerate" method
+ * cannot be used for testing because they incorporate information from
+ * the operating system and also from the default nsIHandlerService
+ * instance, independently from what instance is under testing.
+ *
+ * @return Array of nsIHandlerInfo instances sorted by their "type" property.
+ */
+ getAllHandlerInfos() {
+ return this.getAllHandlerInfoTypes().map(type => this.getHandlerInfo(type));
+ },
+
+ /**
+ * Retrieves an nsIHandlerInfo for the given MIME type or protocol, which
+ * incorporates information from the operating system and also from the
+ * handler service instance currently under testing.
+ *
+ * @note If the handler service instance currently under testing is not the
+ * default one and the requested type is a MIME type, the returned
+ * nsIHandlerInfo will include information from the default
+ * nsIHandlerService instance. This cannot be avoided easily because the
+ * getMIMEInfoFromOS method is not exposed to JavaScript.
+ *
+ * @param type
+ * MIME type or scheme name of the nsIHandlerInfo to retrieve.
+ *
+ * @return The populated nsIHandlerInfo instance.
+ */
+ 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 = lazy.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 (lazy.gHandlerService.exists(handlerInfo)) {
+ lazy.gHandlerService.fillHandlerInfo(handlerInfo, "");
+ }
+ return handlerInfo;
+ }
+
+ // Populate the protocol information from the handler service instance under
+ // testing, like the nsIExternalProtocolService::GetProtocolHandlerInfo
+ // method does on the default nsIHandlerService instance.
+ let osDefaultHandlerFound = {};
+ let handlerInfo = lazy.gExternalProtocolService.getProtocolHandlerInfoFromOS(
+ type,
+ osDefaultHandlerFound
+ );
+ if (lazy.gHandlerService.exists(handlerInfo)) {
+ lazy.gHandlerService.fillHandlerInfo(handlerInfo, "");
+ } else {
+ lazy.gExternalProtocolService.setProtocolHandlerDefaults(
+ handlerInfo,
+ osDefaultHandlerFound.value
+ );
+ }
+ return handlerInfo;
+ },
+
+ /**
+ * Creates an nsIHandlerInfo for the given MIME type or protocol, initialized
+ * to the default values for the current platform.
+ *
+ * @note For this method to work, the specified MIME type or protocol must not
+ * be configured in the default handler service instance or the one
+ * under testing, and must not be registered in the operating system.
+ *
+ * @param type
+ * MIME type or scheme name of the nsIHandlerInfo to create.
+ *
+ * @return The blank nsIHandlerInfo instance.
+ */
+ getBlankHandlerInfo(type) {
+ let handlerInfo = this.getHandlerInfo(type);
+
+ let preferredAction, preferredApplicationHandler;
+ let alwaysAskBeforeHandling = true;
+
+ if (AppConstants.platform == "android") {
+ // On Android, the default preferredAction for MIME types is useHelperApp.
+ // For protocols, we always behave as if an operating system provided
+ // handler existed, and as such we initialize them to useSystemDefault.
+ // This is because the AndroidBridge is not available in xpcshell tests.
+ preferredAction = type.includes("/")
+ ? Ci.nsIHandlerInfo.useHelperApp
+ : Ci.nsIHandlerInfo.useSystemDefault;
+ // On Android, the default handler application is always the internal one.
+ preferredApplicationHandler = {
+ name: "Android chooser",
+ };
+ } else {
+ // On Desktop, the default preferredAction for MIME types is saveToDisk,
+ // while for protocols it is alwaysAsk. Since Bug 1735843, for new MIME
+ // types we default to not asking before handling unless a pref is set.
+ alwaysAskBeforeHandling = Services.prefs.getBoolPref(
+ "browser.download.always_ask_before_handling_new_types",
+ false
+ );
+
+ if (type.includes("/")) {
+ preferredAction = Ci.nsIHandlerInfo.saveToDisk;
+ } else {
+ preferredAction = Ci.nsIHandlerInfo.alwaysAsk;
+ // we'll always ask before handling protocols
+ alwaysAskBeforeHandling = true;
+ }
+ preferredApplicationHandler = null;
+ }
+
+ this.assertHandlerInfoMatches(handlerInfo, {
+ type,
+ preferredAction,
+ alwaysAskBeforeHandling,
+ preferredApplicationHandler,
+ });
+ return handlerInfo;
+ },
+
+ /**
+ * 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);
+
+ 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());
+ }
+
+ if (expected.preferredApplicationHandler) {
+ this.assertHandlerAppMatches(
+ handlerInfo.preferredApplicationHandler,
+ expected.preferredApplicationHandler
+ );
+ } else {
+ Assert.equal(handlerInfo.preferredApplicationHandler, null);
+ }
+
+ let handlerAppsArrayEnumerator = handlerInfo.possibleApplicationHandlers.enumerate();
+ if (AppConstants.platform == "android") {
+ // On Android, the first handler application is always the internal one.
+ this.assertHandlerAppMatches(handlerAppsArrayEnumerator.getNext(), {
+ name: "Android chooser",
+ });
+ }
+ for (let expectedHandlerApp of expected.possibleApplicationHandlers || []) {
+ this.assertHandlerAppMatches(
+ handlerAppsArrayEnumerator.getNext(),
+ expectedHandlerApp
+ );
+ }
+ Assert.ok(!handlerAppsArrayEnumerator.hasMoreElements());
+ },
+
+ /**
+ * Checks whether an nsIHandlerApp instance matches the provided object.
+ */
+ assertHandlerAppMatches(handlerApp, expected) {
+ Assert.ok(handlerApp instanceof Ci.nsIHandlerApp);
+ Assert.equal(handlerApp.name, expected.name);
+ if (expected.executable) {
+ Assert.ok(handlerApp instanceof Ci.nsILocalHandlerApp);
+ Assert.ok(expected.executable.equals(handlerApp.executable));
+ } else if (expected.uriTemplate) {
+ Assert.ok(handlerApp instanceof Ci.nsIWebHandlerApp);
+ Assert.equal(handlerApp.uriTemplate, expected.uriTemplate);
+ } else if (expected.service) {
+ Assert.ok(handlerApp instanceof Ci.nsIDBusHandlerApp);
+ Assert.equal(handlerApp.service, expected.service);
+ Assert.equal(handlerApp.method, expected.method);
+ Assert.equal(handlerApp.dBusInterface, expected.dBusInterface);
+ Assert.equal(handlerApp.objectPath, expected.objectPath);
+ }
+ },
+};