summaryrefslogtreecommitdiffstats
path: root/uriloader/exthandler/tests
diff options
context:
space:
mode:
Diffstat (limited to 'uriloader/exthandler/tests')
-rw-r--r--uriloader/exthandler/tests/Makefile.in11
-rw-r--r--uriloader/exthandler/tests/WriteArgument.cpp24
-rw-r--r--uriloader/exthandler/tests/mochitest/browser.ini8
-rw-r--r--uriloader/exthandler/tests/mochitest/browser_download_always_ask_preferred_app.js19
-rw-r--r--uriloader/exthandler/tests/mochitest/browser_remember_download_option.js49
-rw-r--r--uriloader/exthandler/tests/mochitest/browser_web_protocol_handlers.js76
-rw-r--r--uriloader/exthandler/tests/mochitest/handlerApp.xhtml30
-rw-r--r--uriloader/exthandler/tests/mochitest/handlerApps.js110
-rw-r--r--uriloader/exthandler/tests/mochitest/head.js105
-rw-r--r--uriloader/exthandler/tests/mochitest/mochitest.ini10
-rw-r--r--uriloader/exthandler/tests/mochitest/protocolHandler.html16
-rw-r--r--uriloader/exthandler/tests/mochitest/test_handlerApps.xhtml12
-rw-r--r--uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml77
-rw-r--r--uriloader/exthandler/tests/mochitest/unsafeBidiFileName.sjs14
-rw-r--r--uriloader/exthandler/tests/mochitest/unsafeBidi_chromeScript.js28
-rw-r--r--uriloader/exthandler/tests/moz.build24
-rw-r--r--uriloader/exthandler/tests/unit/head_handlerService.js163
-rw-r--r--uriloader/exthandler/tests/unit/mailcap2
-rw-r--r--uriloader/exthandler/tests/unit/tail_handlerService.js5
-rw-r--r--uriloader/exthandler/tests/unit/test_badMIMEType.js26
-rw-r--r--uriloader/exthandler/tests/unit/test_getTypeFromExtension_ext_to_type_mapping.js53
-rw-r--r--uriloader/exthandler/tests/unit/test_getTypeFromExtension_with_empty_Content_Type.js186
-rw-r--r--uriloader/exthandler/tests/unit/test_handlerService.js470
-rw-r--r--uriloader/exthandler/tests/unit/test_punycodeURIs.js126
-rw-r--r--uriloader/exthandler/tests/unit/xpcshell.ini15
-rw-r--r--uriloader/exthandler/tests/unit_ipc/test_encoding.js231
-rw-r--r--uriloader/exthandler/tests/unit_ipc/xpcshell.ini8
27 files changed, 1898 insertions, 0 deletions
diff --git a/uriloader/exthandler/tests/Makefile.in b/uriloader/exthandler/tests/Makefile.in
new file mode 100644
index 000000000..13250c8ab
--- /dev/null
+++ b/uriloader/exthandler/tests/Makefile.in
@@ -0,0 +1,11 @@
+# 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/.
+
+include $(topsrcdir)/config/rules.mk
+
+# need the executable for running the xpcshell unit tests
+ifneq (,$(SIMPLE_PROGRAMS))
+libs::
+ $(INSTALL) $(SIMPLE_PROGRAMS) $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/unit
+endif
diff --git a/uriloader/exthandler/tests/WriteArgument.cpp b/uriloader/exthandler/tests/WriteArgument.cpp
new file mode 100644
index 000000000..2efbed906
--- /dev/null
+++ b/uriloader/exthandler/tests/WriteArgument.cpp
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include "prenv.h"
+
+int main(int argc, char* argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ const char* value = PR_GetEnv("WRITE_ARGUMENT_FILE");
+
+ if (!value)
+ return 2;
+
+ FILE* outfile = fopen(value, "w");
+ if (!outfile)
+ return 3;
+
+ // We only need to write out the first argument (no newline).
+ fputs(argv[argc -1], outfile);
+
+ fclose(outfile);
+
+ return 0;
+}
diff --git a/uriloader/exthandler/tests/mochitest/browser.ini b/uriloader/exthandler/tests/mochitest/browser.ini
new file mode 100644
index 000000000..9647dcf8c
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/browser.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+head = head.js
+support-files =
+ protocolHandler.html
+
+[browser_download_always_ask_preferred_app.js]
+[browser_remember_download_option.js]
+[browser_web_protocol_handlers.js]
diff --git a/uriloader/exthandler/tests/mochitest/browser_download_always_ask_preferred_app.js b/uriloader/exthandler/tests/mochitest/browser_download_always_ask_preferred_app.js
new file mode 100644
index 000000000..40f7ef71e
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/browser_download_always_ask_preferred_app.js
@@ -0,0 +1,19 @@
+add_task(function*() {
+ // Create mocked objects for test
+ let launcher = createMockedObjects(false);
+ // Open helper app dialog with mocked launcher
+ let dlg = yield* openHelperAppDialog(launcher);
+ let doc = dlg.document;
+ let location = doc.getElementById("source");
+ let expectedValue = launcher.source.prePath;
+ if (location.value != expectedValue) {
+ info("Waiting for dialog to be populated.");
+ yield BrowserTestUtils.waitForAttribute("value", location, expectedValue);
+ }
+ is(doc.getElementById("mode").selectedItem.id, "open", "Should be opening the file.");
+ ok(!dlg.document.getElementById("openHandler").selectedItem.hidden,
+ "Should not have selected a hidden item.");
+ let helperAppDialogHiddenPromise = BrowserTestUtils.windowClosed(dlg);
+ doc.documentElement.cancelDialog();
+ yield helperAppDialogHiddenPromise;
+});
diff --git a/uriloader/exthandler/tests/mochitest/browser_remember_download_option.js b/uriloader/exthandler/tests/mochitest/browser_remember_download_option.js
new file mode 100644
index 000000000..996e5ad69
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/browser_remember_download_option.js
@@ -0,0 +1,49 @@
+add_task(function*() {
+ // create mocked objects
+ let launcher = createMockedObjects(true);
+
+ // open helper app dialog with mocked launcher
+ let dlg = yield* openHelperAppDialog(launcher);
+
+ let doc = dlg.document;
+
+ // Set remember choice
+ ok(!doc.getElementById("rememberChoice").checked,
+ "Remember choice checkbox should be not checked.");
+ doc.getElementById("rememberChoice").checked = true;
+
+ // Make sure the mock handler information is not in nsIHandlerService
+ ok(!gHandlerSvc.exists(launcher.MIMEInfo), "Should not be in nsIHandlerService.");
+
+ // close the dialog by pushing the ok button.
+ let dialogClosedPromise = BrowserTestUtils.windowClosed(dlg);
+ // Make sure the ok button is enabled, since the ok button might be disabled by
+ // EnableDelayHelper mechanism. Please refer the detailed
+ // https://dxr.mozilla.org/mozilla-central/source/toolkit/components/prompts/src/SharedPromptUtils.jsm#53
+ doc.documentElement.getButton("accept").disabled = false;
+ doc.documentElement.acceptDialog();
+ yield dialogClosedPromise;
+
+ // check the mocked handler information is saved in nsIHandlerService
+ ok(gHandlerSvc.exists(launcher.MIMEInfo), "Should be in nsIHandlerService.");
+ // check the extension.
+ var mimeType = gHandlerSvc.getTypeFromExtension("abc");
+ is(mimeType, launcher.MIMEInfo.type, "Got correct mime type.");
+ var handlerInfos = gHandlerSvc.enumerate();
+ while (handlerInfos.hasMoreElements()) {
+ let handlerInfo = handlerInfos.getNext().QueryInterface(Ci.nsIHandlerInfo);
+ if (handlerInfo.type == launcher.MIMEInfo.type) {
+ // check the alwaysAskBeforeHandling
+ ok(!handlerInfo.alwaysAskBeforeHandling,
+ "Should turn off the always ask.");
+ // check the preferredApplicationHandler
+ ok(handlerInfo.preferredApplicationHandler.equals(
+ launcher.MIMEInfo.preferredApplicationHandler),
+ "Should be equal to the mockedHandlerApp.");
+ // check the perferredAction
+ is(handlerInfo.preferredAction, launcher.MIMEInfo.preferredAction,
+ "Should be equal to Ci.nsIHandlerInfo.useHelperApp.");
+ break;
+ }
+ }
+});
diff --git a/uriloader/exthandler/tests/mochitest/browser_web_protocol_handlers.js b/uriloader/exthandler/tests/mochitest/browser_web_protocol_handlers.js
new file mode 100644
index 000000000..ac3e66258
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/browser_web_protocol_handlers.js
@@ -0,0 +1,76 @@
+let testURL = "http://example.com/browser/" +
+ "uriloader/exthandler/tests/mochitest/protocolHandler.html";
+
+add_task(function*() {
+ // Load a page registering a protocol handler.
+ let browser = gBrowser.selectedBrowser;
+ browser.loadURI(testURL);
+ yield BrowserTestUtils.browserLoaded(browser, testURL);
+
+ // Register the protocol handler by clicking the notificationbar button.
+ let notificationValue = "Protocol Registration: testprotocol";
+ let getNotification = () =>
+ gBrowser.getNotificationBox().getNotificationWithValue(notificationValue);
+ yield BrowserTestUtils.waitForCondition(getNotification);
+ let notification = getNotification();
+ let button =
+ notification.getElementsByClassName("notification-button-default")[0];
+ ok(button, "got registration button");
+ button.click();
+
+ // Set the new handler as default.
+ const protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
+ getService(Ci.nsIExternalProtocolService);
+ let protoInfo = protoSvc.getProtocolHandlerInfo("testprotocol");
+ is(protoInfo.preferredAction, protoInfo.useHelperApp,
+ "using a helper application is the preferred action");
+ ok(!protoInfo.preferredApplicationHandler, "no preferred handler is set");
+ let handlers = protoInfo.possibleApplicationHandlers;
+ is(1, handlers.length, "only one handler registered for testprotocol");
+ let handler = handlers.queryElementAt(0, Ci.nsIHandlerApp);
+ ok(handler instanceof Ci.nsIWebHandlerApp, "the handler is a web handler");
+ is(handler.uriTemplate, "https://example.com/foobar?uri=%s",
+ "correct url template")
+ protoInfo.preferredApplicationHandler = handler;
+ protoInfo.alwaysAskBeforeHandling = false;
+ const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].
+ getService(Ci.nsIHandlerService);
+ handlerSvc.store(protoInfo);
+
+ // Middle-click a testprotocol link and check the new tab is correct
+ let link = "#link";
+ const expectedURL = "https://example.com/foobar?uri=testprotocol%3Atest";
+
+ let promiseTabOpened =
+ BrowserTestUtils.waitForNewTab(gBrowser, expectedURL);
+ yield BrowserTestUtils.synthesizeMouseAtCenter(link, {button: 1}, browser);
+ let tab = yield promiseTabOpened;
+ gBrowser.selectedTab = tab;
+ is(gURLBar.value, expectedURL,
+ "the expected URL is displayed in the location bar");
+ yield BrowserTestUtils.removeTab(tab);
+
+ // Shift-click the testprotocol link and check the new window.
+ let newWindowPromise = BrowserTestUtils.waitForNewWindow();
+ yield BrowserTestUtils.synthesizeMouseAtCenter(link, {shiftKey: true},
+ browser);
+ let win = yield newWindowPromise;
+ yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
+ yield BrowserTestUtils.waitForCondition(() => win.gBrowser.currentURI.spec == expectedURL);
+ is(win.gURLBar.value, expectedURL,
+ "the expected URL is displayed in the location bar");
+ yield BrowserTestUtils.closeWindow(win);
+
+ // Click the testprotocol link and check the url in the current tab.
+ let loadPromise = BrowserTestUtils.browserLoaded(browser);
+ yield BrowserTestUtils.synthesizeMouseAtCenter(link, {}, browser);
+ yield loadPromise;
+ yield BrowserTestUtils.waitForCondition(() => gURLBar.value != testURL);
+ is(gURLBar.value, expectedURL,
+ "the expected URL is displayed in the location bar");
+
+ // Cleanup.
+ protoInfo.preferredApplicationHandler = null;
+ handlers.removeElementAt(0);
+ handlerSvc.store(protoInfo);
+});
diff --git a/uriloader/exthandler/tests/mochitest/handlerApp.xhtml b/uriloader/exthandler/tests/mochitest/handlerApp.xhtml
new file mode 100644
index 000000000..83ba0d1a5
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/handlerApp.xhtml
@@ -0,0 +1,30 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Pseudo Web Handler App</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="onLoad()">
+Pseudo Web Handler App
+
+<script class="testbody" type="text/javascript">
+<![CDATA[
+function onLoad() {
+
+ // if we have a window.opener, this must be the windowContext
+ // instance of this test. check that we got the URI right and clean up.
+ if (window.opener) {
+ window.opener.is(location.search,
+ "?uri=" + encodeURIComponent(window.opener.testURI),
+ "uri passed to web-handler app");
+ window.opener.SimpleTest.finish();
+ }
+
+ window.close();
+}
+]]>
+</script>
+
+</body>
+</html>
+
diff --git a/uriloader/exthandler/tests/mochitest/handlerApps.js b/uriloader/exthandler/tests/mochitest/handlerApps.js
new file mode 100644
index 000000000..597f9442d
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/handlerApps.js
@@ -0,0 +1,110 @@
+/* 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/. */
+
+// handlerApp.xhtml grabs this for verification purposes via window.opener
+var testURI = "webcal://127.0.0.1/rheeeeet.html";
+
+const Cc = SpecialPowers.Cc;
+
+function test() {
+
+ // set up the web handler object
+ var webHandler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
+ createInstance(SpecialPowers.Ci.nsIWebHandlerApp);
+ webHandler.name = "Test Web Handler App";
+ webHandler.uriTemplate =
+ "http://mochi.test:8888/tests/uriloader/exthandler/tests/mochitest/" +
+ "handlerApp.xhtml?uri=%s";
+
+ // set up the uri to test with
+ var ioService = Cc["@mozilla.org/network/io-service;1"].
+ getService(SpecialPowers.Ci.nsIIOService);
+ var uri = ioService.newURI(testURI, null, null);
+
+ // create a window, and launch the handler in it
+ var newWindow = window.open("", "handlerWindow", "height=300,width=300");
+ var windowContext =
+ SpecialPowers.wrap(newWindow).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
+ getInterface(SpecialPowers.Ci.nsIWebNavigation).
+ QueryInterface(SpecialPowers.Ci.nsIDocShell);
+
+ webHandler.launchWithURI(uri, windowContext);
+
+ // if we get this far without an exception, we've at least partly passed
+ // (remaining check in handlerApp.xhtml)
+ ok(true, "webHandler launchWithURI (existing window/tab) started");
+
+ // make the web browser launch in its own window/tab
+ webHandler.launchWithURI(uri);
+
+ // if we get this far without an exception, we've passed
+ ok(true, "webHandler launchWithURI (new window/tab) test started");
+
+ // set up the local handler object
+ var localHandler = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+ createInstance(SpecialPowers.Ci.nsILocalHandlerApp);
+ localHandler.name = "Test Local Handler App";
+
+ // get a local app that we know will be there and do something sane
+ var osString = Cc["@mozilla.org/xre/app-info;1"].
+ getService(SpecialPowers.Ci.nsIXULRuntime).OS;
+
+ var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
+ getService(SpecialPowers.Ci.nsIDirectoryServiceProvider);
+ if (osString == "WINNT") {
+ var windowsDir = dirSvc.getFile("WinD", {});
+ var exe = windowsDir.clone().QueryInterface(SpecialPowers.Ci.nsILocalFile);
+ exe.appendRelativePath("SYSTEM32\\HOSTNAME.EXE");
+
+ } else if (osString == "Darwin") {
+ var localAppsDir = dirSvc.getFile("LocApp", {});
+ exe = localAppsDir.clone();
+ exe.append("iCal.app"); // lingers after the tests finish, but this seems
+ // seems better than explicitly killing it, since
+ // developers who run the tests locally may well
+ // information in their running copy of iCal
+
+ if (navigator.userAgent.match(/ SeaMonkey\//)) {
+ // SeaMonkey tinderboxes don't like to have iCal lingering (and focused)
+ // on next test suite run(s).
+ todo(false, "On SeaMonkey, testing OS X as generic Unix. (Bug 749872)");
+
+ // assume a generic UNIX variant
+ exe = Cc["@mozilla.org/file/local;1"].
+ createInstance(SpecialPowers.Ci.nsILocalFile);
+ exe.initWithPath("/bin/echo");
+ }
+ } else {
+ // assume a generic UNIX variant
+ exe = Cc["@mozilla.org/file/local;1"].
+ createInstance(SpecialPowers.Ci.nsILocalFile);
+ exe.initWithPath("/bin/echo");
+ }
+
+ localHandler.executable = exe;
+ localHandler.launchWithURI(ioService.newURI(testURI, null, null));
+
+ // if we get this far without an exception, we've passed
+ ok(true, "localHandler launchWithURI test");
+
+ // if we ever decide that killing iCal is the right thing to do, change
+ // the if statement below from "NOTDarwin" to "Darwin"
+ if (osString == "NOTDarwin") {
+
+ var killall = Cc["@mozilla.org/file/local;1"].
+ createInstance(SpecialPowers.Ci.nsILocalFile);
+ killall.initWithPath("/usr/bin/killall");
+
+ var process = Cc["@mozilla.org/process/util;1"].
+ createInstance(SpecialPowers.Ci.nsIProcess);
+ process.init(killall);
+
+ var args = ['iCal'];
+ process.run(false, args, args.length);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+}
+
+test();
diff --git a/uriloader/exthandler/tests/mochitest/head.js b/uriloader/exthandler/tests/mochitest/head.js
new file mode 100644
index 000000000..dad0493f8
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/head.js
@@ -0,0 +1,105 @@
+Components.utils.import("resource://gre/modules/FileUtils.jsm");
+Components.utils.import("resource://gre/modules/Task.jsm");
+
+var gMimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
+var gHandlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService);
+
+function createMockedHandlerApp() {
+ // Mock the executable
+ let mockedExecutable = FileUtils.getFile("TmpD", ["mockedExecutable"]);
+ if (!mockedExecutable.exists()) {
+ mockedExecutable.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o755);
+ }
+
+ // Mock the handler app
+ let mockedHandlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"]
+ .createInstance(Ci.nsILocalHandlerApp);
+ mockedHandlerApp.executable = mockedExecutable;
+ mockedHandlerApp.detailedDescription = "Mocked handler app";
+
+ registerCleanupFunction(function() {
+ // remove the mocked executable from disk.
+ if (mockedExecutable.exists()) {
+ mockedExecutable.remove(true);
+ }
+ });
+
+ return mockedHandlerApp;
+}
+
+function createMockedObjects(createHandlerApp) {
+ // Mock the mime info
+ let internalMockedMIME = gMimeSvc.getFromTypeAndExtension("text/x-test-handler", null);
+ internalMockedMIME.alwaysAskBeforeHandling = true;
+ internalMockedMIME.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
+ internalMockedMIME.appendExtension("abc");
+ if (createHandlerApp) {
+ let mockedHandlerApp = createMockedHandlerApp();
+ internalMockedMIME.description = mockedHandlerApp.detailedDescription;
+ internalMockedMIME.possibleApplicationHandlers.appendElement(mockedHandlerApp, false);
+ internalMockedMIME.preferredApplicationHandler = mockedHandlerApp;
+ }
+
+ // Proxy for the mocked MIME info for faking the read-only attributes
+ let mockedMIME = new Proxy(internalMockedMIME, {
+ get: function (target, property) {
+ switch (property) {
+ case "hasDefaultHandler":
+ return true;
+ case "defaultDescription":
+ return "Default description";
+ default:
+ return target[property];
+ }
+ },
+ });
+
+ // Mock the launcher:
+ let mockedLauncher = {
+ MIMEInfo: mockedMIME,
+ source: Services.io.newURI("http://www.mozilla.org/", null, null),
+ suggestedFileName: "test_download_dialog.abc",
+ targetFileIsExecutable: false,
+ saveToDisk() {},
+ cancel() {},
+ launchWithApplication() {},
+ setWebProgressListener() {},
+ saveDestinationAvailable() {},
+ contentLength: 42,
+ targetFile: null, // never read
+ // PRTime is microseconds since epoch, Date.now() returns milliseconds:
+ timeDownloadStarted: Date.now() * 1000,
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable, Ci.nsIHelperAppLauncher])
+ };
+
+ registerCleanupFunction(function() {
+ // remove the mocked mime info from database.
+ let mockHandlerInfo = gMimeSvc.getFromTypeAndExtension("text/x-test-handler", null);
+ if (gHandlerSvc.exists(mockHandlerInfo)) {
+ gHandlerSvc.remove(mockHandlerInfo);
+ }
+ });
+
+ return mockedLauncher;
+}
+
+function* openHelperAppDialog(launcher) {
+ let helperAppDialog = Cc["@mozilla.org/helperapplauncherdialog;1"].
+ createInstance(Ci.nsIHelperAppLauncherDialog);
+
+ let helperAppDialogShownPromise = BrowserTestUtils.domWindowOpened();
+ try {
+ helperAppDialog.show(launcher, window, "foopy");
+ } catch (ex) {
+ ok(false, "Trying to show unknownContentType.xul failed with exception: " + ex);
+ Cu.reportError(ex);
+ }
+ let dlg = yield helperAppDialogShownPromise;
+
+ yield BrowserTestUtils.waitForEvent(dlg, "load", false);
+
+ is(dlg.location.href, "chrome://mozapps/content/downloads/unknownContentType.xul",
+ "Got correct dialog");
+
+ return dlg;
+}
diff --git a/uriloader/exthandler/tests/mochitest/mochitest.ini b/uriloader/exthandler/tests/mochitest/mochitest.ini
new file mode 100644
index 000000000..ae191dc7b
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/mochitest.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+support-files =
+ handlerApp.xhtml
+ handlerApps.js
+ unsafeBidi_chromeScript.js
+ unsafeBidiFileName.sjs
+
+[test_handlerApps.xhtml]
+skip-if = (toolkit == 'android' || os == 'mac') || e10s # OS X: bug 786938
+[test_unsafeBidiChars.xhtml]
diff --git a/uriloader/exthandler/tests/mochitest/protocolHandler.html b/uriloader/exthandler/tests/mochitest/protocolHandler.html
new file mode 100644
index 000000000..5a929ba99
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/protocolHandler.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Protocol handler</title>
+ <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+ <meta content="utf-8" http-equiv="encoding">
+ </head>
+ <body>
+ <script type="text/javascript">
+ navigator.registerProtocolHandler("testprotocol",
+ "https://example.com/foobar?uri=%s",
+ "Test Protocol");
+ </script>
+ <a id="link" href="testprotocol:test">testprotocol link</a>
+ </body>
+</html>
diff --git a/uriloader/exthandler/tests/mochitest/test_handlerApps.xhtml b/uriloader/exthandler/tests/mochitest/test_handlerApps.xhtml
new file mode 100644
index 000000000..e5d73a232
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/test_handlerApps.xhtml
@@ -0,0 +1,12 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Test for Handler Apps </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="handlerApps.js"/>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+</body>
+</html>
+
diff --git a/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml b/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml
new file mode 100644
index 000000000..fafe0b4c5
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/test_unsafeBidiChars.xhtml
@@ -0,0 +1,77 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Test for Handling of unsafe bidi chars</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<iframe id="test"></iframe>
+<script type="text/javascript">
+<![CDATA[
+
+var unsafeBidiChars = [
+ "\xe2\x80\xaa", // LRE
+ "\xe2\x80\xab", // RLE
+ "\xe2\x80\xac", // PDF
+ "\xe2\x80\xad", // LRO
+ "\xe2\x80\xae" // RLO
+];
+
+var tests = [
+ "{1}.test",
+ "{1}File.test",
+ "Fi{1}le.test",
+ "File{1}.test",
+ "File.{1}test",
+ "File.te{1}st",
+ "File.test{1}",
+ "File.{1}",
+];
+
+function replace(name, x) {
+ return name.replace(/\{1\}/, x);
+}
+
+function sanitize(name) {
+ return replace(name, '_');
+}
+
+add_task(function* () {
+ let url = SimpleTest.getTestFileURL("unsafeBidi_chromeScript.js");
+ let chromeScript = SpecialPowers.loadChromeScript(url);
+
+ for (let test of tests) {
+ for (let char of unsafeBidiChars) {
+ let promiseName = new Promise(function(resolve) {
+ chromeScript.addMessageListener("suggestedFileName",
+ function listener(data) {
+ chromeScript.removeMessageListener("suggestedFileName", listener);
+ resolve(data);
+ });
+ });
+ let name = replace(test, char);
+ let expected = sanitize(test);
+ document.getElementById("test").src =
+ "unsafeBidiFileName.sjs?name=" + encodeURIComponent(name);
+ is((yield promiseName), expected, "got the expected sanitized name");
+ }
+ }
+
+ let promise = new Promise(function(resolve) {
+ chromeScript.addMessageListener("unregistered", function listener() {
+ chromeScript.removeMessageListener("unregistered", listener);
+ resolve();
+ });
+ });
+ chromeScript.sendAsyncMessage("unregister");
+ yield promise;
+
+ chromeScript.destroy();
+});
+
+]]>
+</script>
+</body>
+</html>
diff --git a/uriloader/exthandler/tests/mochitest/unsafeBidiFileName.sjs b/uriloader/exthandler/tests/mochitest/unsafeBidiFileName.sjs
new file mode 100644
index 000000000..48301be5b
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/unsafeBidiFileName.sjs
@@ -0,0 +1,14 @@
+/* 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/. */
+
+function handleRequest(request, response) {
+ response.setStatusLine(request.httpVersion, 200, "OK");
+
+ if (!request.queryString.match(/^name=/))
+ return;
+ var name = decodeURIComponent(request.queryString.substring(5));
+
+ response.setHeader("Content-Type", "application/octet-stream; name=\"" + name + "\"");
+ response.setHeader("Content-Disposition", "inline; filename=\"" + name + "\"");
+}
diff --git a/uriloader/exthandler/tests/mochitest/unsafeBidi_chromeScript.js b/uriloader/exthandler/tests/mochitest/unsafeBidi_chromeScript.js
new file mode 100644
index 000000000..16c82d818
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/unsafeBidi_chromeScript.js
@@ -0,0 +1,28 @@
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const HELPERAPP_DIALOG_CONTRACT = "@mozilla.org/helperapplauncherdialog;1";
+const HELPERAPP_DIALOG_CID =
+ Components.ID(Cc[HELPERAPP_DIALOG_CONTRACT].number);
+
+const FAKE_CID = Cc["@mozilla.org/uuid-generator;1"].
+ getService(Ci.nsIUUIDGenerator).generateUUID();
+
+function HelperAppLauncherDialog() {}
+HelperAppLauncherDialog.prototype = {
+ show: function(aLauncher, aWindowContext, aReason) {
+ sendAsyncMessage("suggestedFileName", aLauncher.suggestedFileName);
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog])
+};
+
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(FAKE_CID, "", HELPERAPP_DIALOG_CONTRACT,
+ XPCOMUtils._getFactory(HelperAppLauncherDialog));
+
+addMessageListener("unregister", function() {
+ registrar.registerFactory(HELPERAPP_DIALOG_CID, "",
+ HELPERAPP_DIALOG_CONTRACT, null);
+ sendAsyncMessage("unregistered");
+});
diff --git a/uriloader/exthandler/tests/moz.build b/uriloader/exthandler/tests/moz.build
new file mode 100644
index 000000000..6aadfdc52
--- /dev/null
+++ b/uriloader/exthandler/tests/moz.build
@@ -0,0 +1,24 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']
+
+XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
+
+BROWSER_CHROME_MANIFESTS += ['mochitest/browser.ini']
+
+# The encoding test is already implemented in the Downloads API by a set of
+# test cases with the string "content_encoding" in their names.
+if not CONFIG['MOZ_JSDOWNLOADS']:
+ XPCSHELL_TESTS_MANIFESTS += ['unit_ipc/xpcshell.ini']
+
+GeckoSimplePrograms([
+ 'WriteArgument',
+], linkage=None)
+
+USE_LIBS += [
+ 'nspr',
+]
diff --git a/uriloader/exthandler/tests/unit/head_handlerService.js b/uriloader/exthandler/tests/unit/head_handlerService.js
new file mode 100644
index 000000000..8b6803d24
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/head_handlerService.js
@@ -0,0 +1,163 @@
+/* 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/. */
+
+// Inspired by the Places infrastructure in head_bookmarks.js
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cr = Components.results;
+var Cu = Components.utils;
+
+var HandlerServiceTest = {
+ //**************************************************************************//
+ // Convenience Getters
+
+ __dirSvc: null,
+ get _dirSvc() {
+ if (!this.__dirSvc)
+ this.__dirSvc = Cc["@mozilla.org/file/directory_service;1"].
+ getService(Ci.nsIProperties).
+ QueryInterface(Ci.nsIDirectoryService);
+ return this.__dirSvc;
+ },
+
+ __consoleSvc: null,
+ get _consoleSvc() {
+ if (!this.__consoleSvc)
+ this.__consoleSvc = Cc["@mozilla.org/consoleservice;1"].
+ getService(Ci.nsIConsoleService);
+ return this.__consoleSvc;
+ },
+
+
+ //**************************************************************************//
+ // nsISupports
+
+ interfaces: [Ci.nsIDirectoryServiceProvider, Ci.nsISupports],
+
+ QueryInterface: function HandlerServiceTest_QueryInterface(iid) {
+ if (!this.interfaces.some( function(v) { return iid.equals(v) } ))
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ return this;
+ },
+
+
+ //**************************************************************************//
+ // 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) {
+ 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();
+
+ // Turn on logging so we can troubleshoot problems with the tests.
+ var prefBranch = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch);
+ prefBranch.setBoolPref("browser.contentHandling.log", true);
+ },
+
+ destroy: function HandlerServiceTest_destroy() {
+ // Delete the existing datasource file, if any, so we don't leave test files
+ // lying around and we start from scratch the next time.
+ this._deleteDatasourceFile();
+ // Unregister the directory service provider
+ if (this._providerRegistered)
+ this._dirSvc.unregisterProvider(this);
+ },
+
+
+ //**************************************************************************//
+ // 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);
+ 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 " +
+ "nsIDirectoryServiceProvider::getFile is expected, " +
+ "as we don't provide the '" + property + "' file");
+ throw Cr.NS_ERROR_FAILURE;
+ },
+
+
+ //**************************************************************************//
+ // Utilities
+
+ /**
+ * Delete the datasource file.
+ */
+ _deleteDatasourceFile: function HandlerServiceTest__deleteDatasourceFile() {
+ var file = this._dirSvc.get("UMimTyp", Ci.nsIFile);
+ if (file.exists())
+ file.remove(false);
+ },
+
+ /**
+ * Get the contents of the datasource as a serialized string. Useful for
+ * debugging problems with test failures, i.e.:
+ *
+ * HandlerServiceTest.log(HandlerServiceTest.getDatasourceContents());
+ *
+ * @returns {string} the serialized datasource
+ */
+ getDatasourceContents: function HandlerServiceTest_getDatasourceContents() {
+ var rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
+
+ var ioService = Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService);
+ var fileHandler = ioService.getProtocolHandler("file").
+ QueryInterface(Ci.nsIFileProtocolHandler);
+ var fileURL = fileHandler.getURLSpecFromFile(this.getDatasourceFile());
+ var ds = rdf.GetDataSourceBlocking(fileURL);
+
+ var outputStream = {
+ data: "",
+ close: function() {},
+ flush: function() {},
+ write: function (buffer,count) {
+ this.data += buffer;
+ return count;
+ },
+ writeFrom: function (stream,count) {},
+ isNonBlocking: false
+ };
+
+ ds.QueryInterface(Components.interfaces.nsIRDFXMLSource);
+ ds.Serialize(outputStream);
+
+ return outputStream.data;
+ },
+
+ /**
+ * Log a message to the console and the test log.
+ */
+ log: function HandlerServiceTest_log(message) {
+ message = "*** HandlerServiceTest: " + message;
+ this._consoleSvc.logStringMessage(message);
+ print(message);
+ }
+
+};
+
+HandlerServiceTest.init();
diff --git a/uriloader/exthandler/tests/unit/mailcap b/uriloader/exthandler/tests/unit/mailcap
new file mode 100644
index 000000000..dc93ef804
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/mailcap
@@ -0,0 +1,2 @@
+text/plain; cat '%s'; needsterminal
+text/plain; sed '%s'
diff --git a/uriloader/exthandler/tests/unit/tail_handlerService.js b/uriloader/exthandler/tests/unit/tail_handlerService.js
new file mode 100644
index 000000000..1d1989127
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/tail_handlerService.js
@@ -0,0 +1,5 @@
+/* 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/. */
+
+HandlerServiceTest.destroy();
diff --git a/uriloader/exthandler/tests/unit/test_badMIMEType.js b/uriloader/exthandler/tests/unit/test_badMIMEType.js
new file mode 100644
index 000000000..df1202a0f
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/test_badMIMEType.js
@@ -0,0 +1,26 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * 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/. */
+
+function run_test() {
+ // "text/plain" has an 0xFF character appended to it. This means it's an
+ // invalid string, which is tricky to enter using a text editor (I used
+ // emacs' hexl-mode). It also means an ordinary text editor might drop it
+ // or convert it to something that *is* valid (in UTF8). So we measure
+ // its length to make sure this hasn't happened.
+ var badMimeType = "text/plainÿ";
+ do_check_eq(badMimeType.length, 11);
+
+ try {
+ var type = Cc["@mozilla.org/mime;1"].
+ getService(Ci.nsIMIMEService).
+ getFromTypeAndExtension(badMimeType, "txt");
+ } catch (e if (e instanceof Ci.nsIException &&
+ e.result == Cr.NS_ERROR_NOT_AVAILABLE)) {
+ // This is an expected exception, thrown if the type can't be determined
+ } finally {
+ }
+ // Not crashing is good enough
+ do_check_eq(true, true);
+}
diff --git a/uriloader/exthandler/tests/unit/test_getTypeFromExtension_ext_to_type_mapping.js b/uriloader/exthandler/tests/unit/test_getTypeFromExtension_ext_to_type_mapping.js
new file mode 100644
index 000000000..d83c486bb
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/test_getTypeFromExtension_ext_to_type_mapping.js
@@ -0,0 +1,53 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * 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/. */
+
+/**
+ * Test for bug 508030 <https://bugzilla.mozilla.org/show_bug.cgi?id=508030>:
+ * nsIMIMEService.getTypeFromExtension fails to find a match in the
+ * "ext-to-type-mapping" category if the provided extension is not lowercase.
+ */
+function run_test() {
+ // --- Common services ---
+
+ const mimeService = Cc["@mozilla.org/mime;1"].
+ getService(Ci.nsIMIMEService);
+
+ const categoryManager = Cc["@mozilla.org/categorymanager;1"].
+ getService(Ci.nsICategoryManager);
+
+ // --- Test procedure ---
+
+ const kTestExtension = "testextension";
+ const kTestExtensionMixedCase = "testExtensIon";
+ const kTestMimeType = "application/x-testextension";
+
+ // Ensure that the test extension is not initially recognized by the operating
+ // system or the "ext-to-type-mapping" category.
+ try {
+ // Try and get the MIME type associated with the extension.
+ mimeService.getTypeFromExtension(kTestExtension);
+ // The line above should have thrown an exception.
+ do_throw("nsIMIMEService.getTypeFromExtension succeeded unexpectedly");
+ } catch (e if (e instanceof Ci.nsIException &&
+ e.result == Cr.NS_ERROR_NOT_AVAILABLE)) {
+ // This is an expected exception, thrown if the type can't be determined.
+ // Any other exception would cause the test to fail.
+ }
+
+ // Add a temporary category entry mapping the extension to the MIME type.
+ categoryManager.addCategoryEntry("ext-to-type-mapping", kTestExtension,
+ kTestMimeType, false, true);
+
+ // Check that the mapping is recognized in the simple case.
+ var type = mimeService.getTypeFromExtension(kTestExtension);
+ do_check_eq(type, kTestMimeType);
+
+ // Check that the mapping is recognized even if the extension has mixed case.
+ type = mimeService.getTypeFromExtension(kTestExtensionMixedCase);
+ do_check_eq(type, kTestMimeType);
+
+ // Clean up after ourselves.
+ categoryManager.deleteCategoryEntry("ext-to-type-mapping", kTestExtension, false);
+}
diff --git a/uriloader/exthandler/tests/unit/test_getTypeFromExtension_with_empty_Content_Type.js b/uriloader/exthandler/tests/unit/test_getTypeFromExtension_with_empty_Content_Type.js
new file mode 100644
index 000000000..1ae2a6fcf
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/test_getTypeFromExtension_with_empty_Content_Type.js
@@ -0,0 +1,186 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * 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/. */
+
+/**
+ * Test for bug 484579 <https://bugzilla.mozilla.org/show_bug.cgi?id=484579>:
+ * nsIMIMEService.getTypeFromExtension may fail unexpectedly on Windows when
+ * "Content Type" is empty in the registry.
+ */
+function run_test() {
+ // --- Preliminary platform check ---
+
+ // If this test is not running on the Windows platform, stop now, before
+ // calling XPCOMUtils.generateQI during the MockWindowsRegKey declaration.
+ if (mozinfo.os != "win")
+ return;
+
+ // --- Modified nsIWindowsRegKey implementation ---
+
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ /**
+ * Constructs a new mock registry key by wrapping the provided object.
+ *
+ * This mock implementation is tailored for this test, and forces consumers
+ * of the readStringValue method to believe that the "Content Type" value of
+ * the ".txt" key under HKEY_CLASSES_ROOT is an empty string.
+ *
+ * The same value read from "HKEY_LOCAL_MACHINE\SOFTWARE\Classes" is not
+ * affected.
+ *
+ * @param aWrappedObject An actual nsIWindowsRegKey implementation.
+ */
+ function MockWindowsRegKey(aWrappedObject) {
+ this._wrappedObject = aWrappedObject;
+
+ // This function creates a forwarding function for wrappedObject
+ function makeForwardingFunction(functionName) {
+ return function() {
+ return aWrappedObject[functionName].apply(aWrappedObject, arguments);
+ }
+ }
+
+ // Forward all the functions that are not explicitly overridden
+ for (var propertyName in aWrappedObject) {
+ if (!(propertyName in this)) {
+ if (typeof aWrappedObject[propertyName] == "function") {
+ this[propertyName] = makeForwardingFunction(propertyName);
+ } else {
+ this[propertyName] = aWrappedObject[propertyName];
+ }
+ }
+ }
+ }
+
+ MockWindowsRegKey.prototype = {
+ // --- Overridden nsISupports interface functions ---
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowsRegKey]),
+
+ // --- Overridden nsIWindowsRegKey interface functions ---
+
+ open: function(aRootKey, aRelPath, aMode) {
+ // Remember the provided root key and path
+ this._rootKey = aRootKey;
+ this._relPath = aRelPath;
+
+ // Create the actual registry key
+ return this._wrappedObject.open(aRootKey, aRelPath, aMode);
+ },
+
+ openChild: function(aRelPath, aMode) {
+ // Open the child key and wrap it
+ var innerKey = this._wrappedObject.openChild(aRelPath, aMode);
+ var key = new MockWindowsRegKey(innerKey);
+
+ // Set the properties of the child key and return it
+ key._rootKey = this._rootKey;
+ key._relPath = this._relPath + aRelPath;
+ return key;
+ },
+
+ createChild: function(aRelPath, aMode) {
+ // Create the child key and wrap it
+ var innerKey = this._wrappedObject.createChild(aRelPath, aMode);
+ var key = new MockWindowsRegKey(innerKey);
+
+ // Set the properties of the child key and return it
+ key._rootKey = this._rootKey;
+ key._relPath = this._relPath + aRelPath;
+ return key;
+ },
+
+ get childCount() {
+ return this._wrappedObject.childCount;
+ },
+
+ get valueCount() {
+ return this._wrappedObject.valueCount;
+ },
+
+ readStringValue: function(aName) {
+ // If this is the key under test, return a fake value
+ if (this._rootKey == Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT &&
+ this._relPath.toLowerCase() == ".txt" &&
+ aName.toLowerCase() == "content type") {
+ return "";
+ }
+
+ // Return the real value in the registry
+ return this._wrappedObject.readStringValue(aName);
+ }
+ };
+
+ // --- Mock nsIWindowsRegKey factory ---
+
+ var componentRegistrar = Components.manager.
+ QueryInterface(Ci.nsIComponentRegistrar);
+
+ var originalWindowsRegKeyCID;
+ var mockWindowsRegKeyFactory;
+
+ const kMockCID = Components.ID("{9b23dfe9-296b-4740-ba1c-d39c9a16e55e}");
+ const kWindowsRegKeyContractID = "@mozilla.org/windows-registry-key;1";
+ const kWindowsRegKeyClassName = "nsWindowsRegKey";
+
+ function registerMockWindowsRegKeyFactory() {
+ mockWindowsRegKeyFactory = {
+ createInstance: function(aOuter, aIid) {
+ if (aOuter != null)
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+
+ var innerKey = originalWindowsRegKeyFactory.createInstance(null, aIid);
+ var key = new MockWindowsRegKey(innerKey);
+
+ return key.QueryInterface(aIid);
+ }
+ };
+
+ // Preserve the original factory
+ originalWindowsRegKeyCID = Cc[kWindowsRegKeyContractID].number;
+
+ // Register the mock factory
+ componentRegistrar.registerFactory(
+ kMockCID,
+ "Mock Windows Registry Key Implementation",
+ kWindowsRegKeyContractID,
+ mockWindowsRegKeyFactory
+ );
+ }
+
+ function unregisterMockWindowsRegKeyFactory() {
+ // Free references to the mock factory
+ componentRegistrar.unregisterFactory(
+ kMockCID,
+ mockWindowsRegKeyFactory
+ );
+
+ // Restore the original factory
+ componentRegistrar.registerFactory(
+ Components.ID(originalWindowsRegKeyCID),
+ "",
+ kWindowsRegKeyContractID,
+ null
+ );
+ }
+
+ // --- Test procedure ---
+
+ // Activate the override of the ".txt" file association data in the registry
+ registerMockWindowsRegKeyFactory();
+ try {
+ // Try and get the MIME type associated with the extension. If this
+ // operation does not throw an unexpected exception, the test succeeds.
+ var type = Cc["@mozilla.org/mime;1"].
+ getService(Ci.nsIMIMEService).
+ getTypeFromExtension(".txt");
+ } catch (e if (e instanceof Ci.nsIException &&
+ e.result == Cr.NS_ERROR_NOT_AVAILABLE)) {
+ // This is an expected exception, thrown if the type can't be determined
+ } finally {
+ // Ensure we restore the original factory when the test is finished
+ unregisterMockWindowsRegKeyFactory();
+ }
+}
diff --git a/uriloader/exthandler/tests/unit/test_handlerService.js b/uriloader/exthandler/tests/unit/test_handlerService.js
new file mode 100644
index 000000000..3facc63ae
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/test_handlerService.js
@@ -0,0 +1,470 @@
+/* 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/. */
+
+function run_test() {
+ //**************************************************************************//
+ // Constants
+
+ const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].
+ getService(Ci.nsIHandlerService);
+
+ const mimeSvc = Cc["@mozilla.org/mime;1"].
+ getService(Ci.nsIMIMEService);
+
+ const protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
+ getService(Ci.nsIExternalProtocolService);
+
+ const prefSvc = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefService);
+
+ const ioService = Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService);
+
+ const env = Cc["@mozilla.org/process/environment;1"].
+ getService(Components.interfaces.nsIEnvironment);
+
+ const rootPrefBranch = prefSvc.getBranch("");
+
+ let noMailto = false;
+ if (mozinfo.os == "win") {
+ // Check mailto handler from registry.
+ // If registry entry is nothing, no mailto handler
+ let regSvc = Cc["@mozilla.org/windows-registry-key;1"].
+ createInstance(Ci.nsIWindowsRegKey);
+ try {
+ regSvc.open(regSvc.ROOT_KEY_CLASSES_ROOT,
+ "mailto",
+ regSvc.ACCESS_READ);
+ noMailto = false;
+ } catch (ex) {
+ noMailto = true;
+ }
+ regSvc.close();
+ }
+
+ if (mozinfo.os == "linux") {
+ // Check mailto handler from GIO
+ // If there isn't one, then we have no mailto handler
+ let gIOSvc = Cc["@mozilla.org/gio-service;1"].
+ createInstance(Ci.nsIGIOService);
+ try {
+ gIOSvc.getAppForURIScheme("mailto");
+ noMailto = false;
+ } catch (ex) {
+ noMailto = true;
+ }
+ }
+
+ //**************************************************************************//
+ // Sample Data
+
+ // It doesn't matter whether or not this nsIFile is actually executable,
+ // only that it has a path and exists. Since we don't know any executable
+ // that exists on all platforms (except possibly the application being
+ // tested, but there doesn't seem to be a way to get a reference to that
+ // from the directory service), we use the temporary directory itself.
+ var executable = HandlerServiceTest._dirSvc.get("TmpD", Ci.nsIFile);
+ // XXX We could, of course, create an actual executable in the directory:
+ //executable.append("localhandler");
+ //if (!executable.exists())
+ // executable.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o755);
+
+ var 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;
+ }
+ };
+
+ var webHandler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
+ createInstance(Ci.nsIWebHandlerApp);
+ webHandler.name = "Web Handler";
+ webHandler.uriTemplate = "http://www.example.com/?%s";
+
+ // FIXME: these tests create and manipulate enough variables that it would
+ // make sense to move each test into its own scope so we don't run the risk
+ // of one test stomping on another's data.
+
+
+ //**************************************************************************//
+ // Test Default Properties
+
+ // 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.
+
+ var handlerInfo = mimeSvc.getFromTypeAndExtension("nonexistent/type", null);
+
+ // Make sure it's also an nsIHandlerInfo.
+ do_check_true(handlerInfo instanceof Ci.nsIHandlerInfo);
+
+ do_check_eq(handlerInfo.type, "nonexistent/type");
+
+ // Deprecated property, but we should still make sure it's set correctly.
+ do_check_eq(handlerInfo.MIMEType, "nonexistent/type");
+
+ // These properties are the ones the handler service knows how to store.
+ do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.saveToDisk);
+ do_check_eq(handlerInfo.preferredApplicationHandler, null);
+ do_check_eq(handlerInfo.possibleApplicationHandlers.length, 0);
+ do_check_true(handlerInfo.alwaysAskBeforeHandling);
+
+ // These properties are initialized to default values by the service,
+ // so we might as well make sure they're initialized to the right defaults.
+ do_check_eq(handlerInfo.description, "");
+ do_check_eq(handlerInfo.hasDefaultHandler, false);
+ do_check_eq(handlerInfo.defaultDescription, "");
+
+ // test some default protocol info properties
+ var haveDefaultHandlersVersion = false;
+ try {
+ // If we have a defaultHandlersVersion pref, then assume that we're in the
+ // firefox tree and that we'll also have default handlers.
+ // Bug 395131 has been filed to make this test work more generically
+ // by providing our own prefs for this test rather than this icky
+ // special casing.
+ rootPrefBranch.getCharPref("gecko.handlerService.defaultHandlersVersion");
+ haveDefaultHandlersVersion = true;
+ } catch (ex) {}
+
+ const kExternalWarningDefault =
+ "network.protocol-handler.warn-external-default";
+ prefSvc.setBoolPref(kExternalWarningDefault, true);
+
+ // XXX add more thorough protocol info property checking
+
+ // no OS default handler exists
+ var protoInfo = protoSvc.getProtocolHandlerInfo("x-moz-rheet");
+ do_check_eq(protoInfo.preferredAction, protoInfo.alwaysAsk);
+ do_check_true(protoInfo.alwaysAskBeforeHandling);
+
+ // OS default exists, injected default does not exist,
+ // explicit warning pref: false
+ const kExternalWarningPrefPrefix = "network.protocol-handler.warn-external.";
+ prefSvc.setBoolPref(kExternalWarningPrefPrefix + "http", false);
+ protoInfo = protoSvc.getProtocolHandlerInfo("http");
+ do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
+ do_check_false(protoInfo.alwaysAskBeforeHandling);
+
+ // OS default exists, injected default does not exist,
+ // explicit warning pref: true
+ prefSvc.setBoolPref(kExternalWarningPrefPrefix + "http", true);
+ protoInfo = protoSvc.getProtocolHandlerInfo("http");
+ // OS handler isn't included in possibleApplicationHandlers, so length is 0
+ // Once they become instances of nsILocalHandlerApp, this number will need
+ // to change.
+ do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
+ do_check_true(protoInfo.alwaysAskBeforeHandling);
+
+ // OS default exists, injected default exists, explicit warning pref: false
+ prefSvc.setBoolPref(kExternalWarningPrefPrefix + "mailto", false);
+ protoInfo = protoSvc.getProtocolHandlerInfo("mailto");
+ if (haveDefaultHandlersVersion)
+ do_check_eq(2, protoInfo.possibleApplicationHandlers.length);
+ else
+ do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
+
+ // Win7+ or Linux's GIO might not have a default mailto: handler
+ if (noMailto)
+ do_check_true(protoInfo.alwaysAskBeforeHandling);
+ else
+ do_check_false(protoInfo.alwaysAskBeforeHandling);
+
+ // OS default exists, injected default exists, explicit warning pref: true
+ prefSvc.setBoolPref(kExternalWarningPrefPrefix + "mailto", true);
+ protoInfo = protoSvc.getProtocolHandlerInfo("mailto");
+ if (haveDefaultHandlersVersion) {
+ do_check_eq(2, protoInfo.possibleApplicationHandlers.length);
+ // Win7+ or Linux's GIO may have no default mailto: handler. Otherwise
+ // alwaysAskBeforeHandling is expected to be false here, because although
+ // the pref is true, the value in RDF is false. The injected mailto handler
+ // carried over the default pref value, and so when we set the pref above
+ // to true it's ignored.
+ if (noMailto)
+ do_check_true(protoInfo.alwaysAskBeforeHandling);
+ else
+ do_check_false(protoInfo.alwaysAskBeforeHandling);
+
+ } else {
+ do_check_eq(0, protoInfo.possibleApplicationHandlers.length);
+ do_check_true(protoInfo.alwaysAskBeforeHandling);
+ }
+
+ if (haveDefaultHandlersVersion) {
+ // Now set the value stored in RDF to true, and the pref to false, to make
+ // sure we still get the right value. (Basically, same thing as above but
+ // with the values reversed.)
+ prefSvc.setBoolPref(kExternalWarningPrefPrefix + "mailto", false);
+ protoInfo.alwaysAskBeforeHandling = true;
+ handlerSvc.store(protoInfo);
+ protoInfo = protoSvc.getProtocolHandlerInfo("mailto");
+ do_check_eq(2, protoInfo.possibleApplicationHandlers.length);
+ do_check_true(protoInfo.alwaysAskBeforeHandling);
+ }
+
+
+ //**************************************************************************//
+ // Test Round-Trip Data Integrity
+
+ // Test round-trip data integrity by setting the properties of the handler
+ // info object to different values, telling the handler service to store the
+ // object, and then retrieving a new info object for the same type and making
+ // sure its properties are identical.
+
+ handlerInfo.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
+ handlerInfo.preferredApplicationHandler = localHandler;
+ handlerInfo.alwaysAskBeforeHandling = false;
+
+ handlerSvc.store(handlerInfo);
+
+ handlerInfo = mimeSvc.getFromTypeAndExtension("nonexistent/type", null);
+
+ do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.useHelperApp);
+
+ do_check_neq(handlerInfo.preferredApplicationHandler, null);
+ var preferredHandler = handlerInfo.preferredApplicationHandler;
+ do_check_eq(typeof preferredHandler, "object");
+ do_check_eq(preferredHandler.name, "Local Handler");
+ do_check_true(preferredHandler instanceof Ci.nsILocalHandlerApp);
+ preferredHandler.QueryInterface(Ci.nsILocalHandlerApp);
+ do_check_eq(preferredHandler.executable.path, localHandler.executable.path);
+
+ do_check_false(handlerInfo.alwaysAskBeforeHandling);
+
+ // Make sure the handler service's enumerate method lists all known handlers.
+ var handlerInfo2 = mimeSvc.getFromTypeAndExtension("nonexistent/type2", null);
+ handlerSvc.store(handlerInfo2);
+ var handlerTypes = ["nonexistent/type", "nonexistent/type2"];
+ if (haveDefaultHandlersVersion) {
+ handlerTypes.push("webcal");
+ handlerTypes.push("mailto");
+ handlerTypes.push("irc");
+ handlerTypes.push("ircs");
+ }
+ var handlers = handlerSvc.enumerate();
+ while (handlers.hasMoreElements()) {
+ var handler = handlers.getNext().QueryInterface(Ci.nsIHandlerInfo);
+ do_check_neq(handlerTypes.indexOf(handler.type), -1);
+ handlerTypes.splice(handlerTypes.indexOf(handler.type), 1);
+ }
+ do_check_eq(handlerTypes.length, 0);
+
+ // Make sure the handler service's remove method removes a handler record.
+ handlerSvc.remove(handlerInfo2);
+ handlers = handlerSvc.enumerate();
+ while (handlers.hasMoreElements())
+ do_check_neq(handlers.getNext().QueryInterface(Ci.nsIHandlerInfo).type,
+ handlerInfo2.type);
+
+ // Make sure we can store and retrieve a handler info object with no preferred
+ // handler.
+ var noPreferredHandlerInfo =
+ mimeSvc.getFromTypeAndExtension("nonexistent/no-preferred-handler", null);
+ handlerSvc.store(noPreferredHandlerInfo);
+ noPreferredHandlerInfo =
+ mimeSvc.getFromTypeAndExtension("nonexistent/no-preferred-handler", null);
+ do_check_eq(noPreferredHandlerInfo.preferredApplicationHandler, null);
+
+ // Make sure that the handler service removes an existing handler record
+ // if we store a handler info object with no preferred handler.
+ var removePreferredHandlerInfo =
+ mimeSvc.getFromTypeAndExtension("nonexistent/rem-preferred-handler", null);
+ removePreferredHandlerInfo.preferredApplicationHandler = localHandler;
+ handlerSvc.store(removePreferredHandlerInfo);
+ removePreferredHandlerInfo =
+ mimeSvc.getFromTypeAndExtension("nonexistent/rem-preferred-handler", null);
+ removePreferredHandlerInfo.preferredApplicationHandler = null;
+ handlerSvc.store(removePreferredHandlerInfo);
+ removePreferredHandlerInfo =
+ mimeSvc.getFromTypeAndExtension("nonexistent/rem-preferred-handler", null);
+ do_check_eq(removePreferredHandlerInfo.preferredApplicationHandler, null);
+
+ // Make sure we can store and retrieve a handler info object with possible
+ // handlers. We test both adding and removing handlers.
+
+ // Get a handler info and make sure it has no possible handlers.
+ var possibleHandlersInfo =
+ mimeSvc.getFromTypeAndExtension("nonexistent/possible-handlers", null);
+ do_check_eq(possibleHandlersInfo.possibleApplicationHandlers.length, 0);
+
+ // Store and re-retrieve the handler and make sure it still has no possible
+ // handlers.
+ handlerSvc.store(possibleHandlersInfo);
+ possibleHandlersInfo =
+ mimeSvc.getFromTypeAndExtension("nonexistent/possible-handlers", null);
+ do_check_eq(possibleHandlersInfo.possibleApplicationHandlers.length, 0);
+
+ // Add two handlers, store the object, re-retrieve it, and make sure it has
+ // two handlers.
+ possibleHandlersInfo.possibleApplicationHandlers.appendElement(localHandler,
+ false);
+ possibleHandlersInfo.possibleApplicationHandlers.appendElement(webHandler,
+ false);
+ handlerSvc.store(possibleHandlersInfo);
+ possibleHandlersInfo =
+ mimeSvc.getFromTypeAndExtension("nonexistent/possible-handlers", null);
+ do_check_eq(possibleHandlersInfo.possibleApplicationHandlers.length, 2);
+
+ // Figure out which is the local and which is the web handler and the index
+ // in the array of the local handler, which is the one we're going to remove
+ // to test removal of a handler.
+ var handler1 = possibleHandlersInfo.possibleApplicationHandlers.
+ queryElementAt(0, Ci.nsIHandlerApp);
+ var handler2 = possibleHandlersInfo.possibleApplicationHandlers.
+ queryElementAt(1, Ci.nsIHandlerApp);
+ var localPossibleHandler, webPossibleHandler, localIndex;
+ if (handler1 instanceof Ci.nsILocalHandlerApp)
+ [localPossibleHandler, webPossibleHandler, localIndex] = [handler1,
+ handler2,
+ 0];
+ else
+ [localPossibleHandler, webPossibleHandler, localIndex] = [handler2,
+ handler1,
+ 1];
+ localPossibleHandler.QueryInterface(Ci.nsILocalHandlerApp);
+ webPossibleHandler.QueryInterface(Ci.nsIWebHandlerApp);
+
+ // Make sure the two handlers are the ones we stored.
+ do_check_eq(localPossibleHandler.name, localHandler.name);
+ do_check_true(localPossibleHandler.equals(localHandler));
+ do_check_eq(webPossibleHandler.name, webHandler.name);
+ do_check_true(webPossibleHandler.equals(webHandler));
+
+ // Remove a handler, store the object, re-retrieve it, and make sure
+ // it only has one handler.
+ possibleHandlersInfo.possibleApplicationHandlers.removeElementAt(localIndex);
+ handlerSvc.store(possibleHandlersInfo);
+ possibleHandlersInfo =
+ mimeSvc.getFromTypeAndExtension("nonexistent/possible-handlers", null);
+ do_check_eq(possibleHandlersInfo.possibleApplicationHandlers.length, 1);
+
+ // Make sure the handler is the one we didn't remove.
+ webPossibleHandler = possibleHandlersInfo.possibleApplicationHandlers.
+ queryElementAt(0, Ci.nsIWebHandlerApp);
+ do_check_eq(webPossibleHandler.name, webHandler.name);
+ do_check_true(webPossibleHandler.equals(webHandler));
+
+ //////////////////////////////////////////////////////
+ // handler info command line parameters and equality
+ var localApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+ createInstance(Ci.nsILocalHandlerApp);
+ var handlerApp = localApp.QueryInterface(Ci.nsIHandlerApp);
+
+ do_check_true(handlerApp.equals(localApp));
+
+ localApp.executable = executable;
+
+ do_check_eq(0, localApp.parameterCount);
+ localApp.appendParameter("-test1");
+ do_check_eq(1, localApp.parameterCount);
+ localApp.appendParameter("-test2");
+ do_check_eq(2, localApp.parameterCount);
+ do_check_true(localApp.parameterExists("-test1"));
+ do_check_true(localApp.parameterExists("-test2"));
+ do_check_false(localApp.parameterExists("-false"));
+ localApp.clearParameters();
+ do_check_eq(0, localApp.parameterCount);
+
+ var localApp2 = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+ createInstance(Ci.nsILocalHandlerApp);
+
+ localApp2.executable = executable;
+
+ localApp.clearParameters();
+ do_check_true(localApp.equals(localApp2));
+
+ // equal:
+ // cut -d 1 -f 2
+ // cut -d 1 -f 2
+
+ localApp.appendParameter("-test1");
+ localApp.appendParameter("-test2");
+ localApp.appendParameter("-test3");
+ localApp2.appendParameter("-test1");
+ localApp2.appendParameter("-test2");
+ localApp2.appendParameter("-test3");
+ do_check_true(localApp.equals(localApp2));
+
+ // not equal:
+ // cut -d 1 -f 2
+ // cut -f 1 -d 2
+
+ localApp.clearParameters();
+ localApp2.clearParameters();
+
+ localApp.appendParameter("-test1");
+ localApp.appendParameter("-test2");
+ localApp.appendParameter("-test3");
+ localApp2.appendParameter("-test2");
+ localApp2.appendParameter("-test1");
+ localApp2.appendParameter("-test3");
+ do_check_false(localApp2.equals(localApp));
+
+ var str;
+ str = localApp.getParameter(0)
+ do_check_eq(str, "-test1");
+ str = localApp.getParameter(1)
+ do_check_eq(str, "-test2");
+ str = localApp.getParameter(2)
+ do_check_eq(str, "-test3");
+
+ // FIXME: test round trip integrity for a protocol.
+ // FIXME: test round trip integrity for a handler info with a web handler.
+
+ //**************************************************************************//
+ // getTypeFromExtension tests
+
+ // test nonexistent extension
+ var lolType = handlerSvc.getTypeFromExtension("lolcat");
+ do_check_eq(lolType, "");
+
+
+ // add a handler for the extension
+ var lolHandler = mimeSvc.getFromTypeAndExtension("application/lolcat", null);
+
+ do_check_false(lolHandler.extensionExists("lolcat"));
+ lolHandler.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
+ lolHandler.preferredApplicationHandler = localHandler;
+ lolHandler.alwaysAskBeforeHandling = false;
+
+ // store the handler
+ do_check_false(handlerSvc.exists(lolHandler));
+ handlerSvc.store(lolHandler);
+ do_check_true(handlerSvc.exists(lolHandler));
+
+ // Get a file:// string pointing to mimeTypes.rdf
+ var rdfFile = HandlerServiceTest._dirSvc.get("UMimTyp", Ci.nsIFile);
+ var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
+ var rdfFileURI = fileHandler.getURLSpecFromFile(rdfFile);
+
+ // Assign a file extenstion to the handler. handlerSvc.store() doesn't
+ // actually store any file extensions added with setFileExtensions(), you
+ // have to wade into RDF muck to do so.
+
+ // Based on toolkit/mozapps/downloads/content/helperApps.js :: addExtension()
+ var gRDF = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
+ var mimeSource = gRDF.GetUnicodeResource("urn:mimetype:application/lolcat");
+ var valueProperty = gRDF.GetUnicodeResource("http://home.netscape.com/NC-rdf#fileExtensions");
+ var mimeLiteral = gRDF.GetLiteral("lolcat");
+
+ var DS = gRDF.GetDataSourceBlocking(rdfFileURI);
+ DS.Assert(mimeSource, valueProperty, mimeLiteral, true);
+
+
+ // test now-existent extension
+ lolType = handlerSvc.getTypeFromExtension("lolcat");
+ do_check_eq(lolType, "application/lolcat");
+
+ // test mailcap entries with needsterminal are ignored on non-Windows non-Mac.
+ if (mozinfo.os != "win" && mozinfo.os != "mac") {
+ env.set('PERSONAL_MAILCAP', do_get_file('mailcap').path);
+ handlerInfo = mimeSvc.getFromTypeAndExtension("text/plain", null);
+ do_check_eq(handlerInfo.preferredAction, Ci.nsIHandlerInfo.useSystemDefault);
+ do_check_eq(handlerInfo.defaultDescription, "sed");
+ }
+}
diff --git a/uriloader/exthandler/tests/unit/test_punycodeURIs.js b/uriloader/exthandler/tests/unit/test_punycodeURIs.js
new file mode 100644
index 000000000..38622c840
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/test_punycodeURIs.js
@@ -0,0 +1,126 @@
+/* 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/. */
+
+// Encoded test URI to work on all platforms/independent of file encoding
+const kTestURI = "http://\u65e5\u672c\u8a93.jp/";
+const kExpectedURI = "http://xn--wgv71a309e.jp/";
+const kOutputFile = "result.txt";
+
+// Try several times in case the box we're running on is slow.
+const kMaxCheckExistAttempts = 30; // seconds
+var gCheckExistsAttempts = 0;
+
+const tempDir = do_get_tempdir();
+
+function checkFile() {
+ // This is where we expect the output
+ var tempFile = tempDir.clone();
+ tempFile.append(kOutputFile);
+
+ if (!tempFile.exists()) {
+ if (gCheckExistsAttempts >= kMaxCheckExistAttempts) {
+ do_throw("Expected File " + tempFile.path + " does not exist after " +
+ kMaxCheckExistAttempts + " seconds");
+ }
+ else {
+ ++gCheckExistsAttempts;
+ // Wait a bit longer then try again
+ do_timeout(1000, checkFile);
+ return;
+ }
+ }
+
+ // Now read it
+ var fstream =
+ Components.classes["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ var sstream =
+ Components.classes["@mozilla.org/scriptableinputstream;1"]
+ .createInstance(Components.interfaces.nsIScriptableInputStream);
+ fstream.init(tempFile, -1, 0, 0);
+ sstream.init(fstream);
+
+ // Read the first line only as that's the one we expect WriteArguments
+ // to be writing the argument to.
+ var data = sstream.read(4096);
+
+ sstream.close();
+ fstream.close();
+
+ // Now remove the old file
+ tempFile.remove(false);
+
+ // This currently fails on Mac with an argument like -psn_0_nnnnnn
+ // This seems to be to do with how the executable is called, but I couldn't
+ // find a way around it.
+ // Additionally the lack of OS detection in xpcshell tests sucks, so we'll
+ // have to check for the argument mac gives us.
+ if (data.substring(0, 7) != "-psn_0_")
+ do_check_eq(data, kExpectedURI);
+
+ do_test_finished();
+}
+
+function run_test() {
+ if (mozinfo.os == "mac") {
+ dump("INFO | test_punycodeURIs.js | Skipping test on mac, bug 599475")
+ return;
+ }
+
+ // set up the uri to test with
+ var ioService =
+ Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+
+ // set up the local handler object
+ var localHandler =
+ Components.classes["@mozilla.org/uriloader/local-handler-app;1"]
+ .createInstance(Components.interfaces.nsILocalHandlerApp);
+ localHandler.name = "Test Local Handler App";
+
+ // WriteArgument will just dump its arguments to a file for us.
+ var processDir = do_get_cwd();
+ var exe = processDir.clone();
+ exe.append("WriteArgument");
+
+ if (!exe.exists()) {
+ // Maybe we are on windows
+ exe.leafName = "WriteArgument.exe";
+ if (!exe.exists())
+ do_throw("Could not locate the WriteArgument tests executable\n");
+ }
+
+ var outFile = tempDir.clone();
+ outFile.append(kOutputFile);
+
+ // Set an environment variable for WriteArgument to pick up
+ var envSvc =
+ Components.classes["@mozilla.org/process/environment;1"]
+ .getService(Components.interfaces.nsIEnvironment);
+
+ // The Write Argument file needs to know where its libraries are, so
+ // just force the path variable
+ // For mac
+ var greDir = HandlerServiceTest._dirSvc.get("GreD", Components.interfaces.nsIFile);
+
+ envSvc.set("DYLD_LIBRARY_PATH", greDir.path);
+ // For Linux
+ envSvc.set("LD_LIBRARY_PATH", greDir.path);
+ //XXX: handle windows
+
+ // Now tell it where we want the file.
+ envSvc.set("WRITE_ARGUMENT_FILE", outFile.path);
+
+ var uri = ioService.newURI(kTestURI, null, null);
+
+ // Just check we've got these matching, if we haven't there's a problem
+ // with ascii spec or our test case.
+ do_check_eq(uri.asciiSpec, kExpectedURI);
+
+ localHandler.executable = exe;
+ localHandler.launchWithURI(uri);
+
+ do_test_pending();
+ do_timeout(1000, checkFile);
+}
diff --git a/uriloader/exthandler/tests/unit/xpcshell.ini b/uriloader/exthandler/tests/unit/xpcshell.ini
new file mode 100644
index 000000000..e268ff9c3
--- /dev/null
+++ b/uriloader/exthandler/tests/unit/xpcshell.ini
@@ -0,0 +1,15 @@
+[DEFAULT]
+head = head_handlerService.js
+tail = tail_handlerService.js
+run-sequentially = Bug 912235 - Intermittent failures
+
+[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_punycodeURIs.js]
+# Bug 676997: test consistently fails on Android
+fail-if = os == "android"
diff --git a/uriloader/exthandler/tests/unit_ipc/test_encoding.js b/uriloader/exthandler/tests/unit_ipc/test_encoding.js
new file mode 100644
index 000000000..a2a00937a
--- /dev/null
+++ b/uriloader/exthandler/tests/unit_ipc/test_encoding.js
@@ -0,0 +1,231 @@
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://testing-common/MockRegistrar.js");
+
+do_get_profile();
+
+var DownloadListener = {
+ init: function () {
+ let obs = Services.obs;
+ obs.addObserver(this, "dl-done", true);
+ },
+
+ observe: function (subject, topic, data) {
+ this.onFinished(subject, topic, data);
+ },
+
+ QueryInterface: function (iid) {
+ if (iid.equals(Ci.nsIObserver) ||
+ iid.equals(Ci.nsISupportsWeakReference) ||
+ iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+DownloadListener.init();
+
+function HelperAppDlg() { }
+HelperAppDlg.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]),
+ show: function (launcher, ctx, reason, usePrivateUI) {
+ launcher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.saveToFile;
+ launcher.launchWithApplication(null, false);
+ }
+}
+
+// Override the download-manager-ui to prevent anyone from trying to open
+// a window.
+function DownloadMgrUI() { }
+DownloadMgrUI.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadManagerUI]),
+ show: function (ir, aID, reason) { },
+
+ visible: false,
+
+ getAttention: function () { }
+}
+
+function AlertsSVC() { }
+AlertsSVC.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService]),
+ showAlertNotification: function (url, title, text, clickable, cookie, listener, name) { },
+}
+
+MockRegistrar.register("@mozilla.org/helperapplauncherdialog;1",
+ HelperAppDlg);
+MockRegistrar.register("@mozilla.org/download-manager-ui;1",
+ DownloadMgrUI);
+MockRegistrar.register("@mozilla.org/alerts-service;1",
+ AlertsSVC);
+
+function initChildTestEnv()
+{
+ sendCommand(' \
+ const Cc = Components.classes; \
+ const Ci = Components.interfaces; \
+ const Cr = Components.results; \
+ const Cu = Components.utils; \
+ Cu.import("resource://gre/modules/Services.jsm"); \
+ function WindowContext() { } \
+ \
+ WindowContext.prototype = { \
+ getInterface: function (iid) { \
+ if (iid.equals(Ci.nsIInterfaceRequestor) || \
+ iid.equals(Ci.nsIURIContentListener) || \
+ iid.equals(Ci.nsILoadGroup) || \
+ iid.equals(Ci.nsIDocumentLoader) || \
+ iid.equals(Ci.nsIDOMWindow)) \
+ return this; \
+ \
+ throw Cr.NS_ERROR_NO_INTERFACE; \
+ }, \
+ \
+ /* nsIURIContentListener */ \
+ onStartURIOpen: function (uri) { }, \
+ isPreferred: function (type, desiredtype) { return false; }, \
+ \
+ /* nsILoadGroup */ \
+ addRequest: function (request, context) { }, \
+ removeRequest: function (request, context, status) { } \
+ }; \
+ \
+ var ioservice = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);\
+ var uriloader = Cc["@mozilla.org/uriloader;1"].getService(Ci.nsIURILoader);\
+ ');
+}
+
+function testFinisher(endFunc) {
+ let ef = endFunc;
+ return function (file) {
+ ef(file);
+ runNextTest();
+ }
+}
+
+function runChildTestSet(set)
+{
+ DownloadListener.onFinished = testFinisher(set[2]);
+ sendCommand('\
+ let uri = ioservice.newURI("http://localhost:4444' + set[0] + '", null, null); \
+ let channel = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true}); \
+ uriloader.openURI(channel, Ci.nsIURILoader.IS_CONTENT_PREFERRED, new WindowContext()); \
+ ');
+}
+
+var httpserver = null;
+var currentTest = 0;
+function runNextTest()
+{
+ if (currentTest == tests.length) {
+ httpserver.stop(do_test_finished);
+ return;
+ }
+
+ let set = tests[currentTest++];
+ runChildTestSet(set);
+}
+
+const responseBody = [0x1f, 0x8b, 0x08, 0x00, 0x16, 0x5a, 0x8a, 0x48, 0x02,
+ 0x03, 0x2b, 0x49, 0x2d, 0x2e, 0xe1, 0x02, 0x00, 0xc6,
+ 0x35, 0xb9, 0x3b, 0x05, 0x00, 0x00, 0x00];
+
+/*
+ * First test: a file with Content-Type application/x-gzip and Content-Encoding gzip
+ * should not be decoded in a round-trip
+ */
+function testResponse1(metadata, response) {
+ response.setHeader("Content-Type", "application/x-gzip", false);
+ response.setHeader("Content-Encoding", "gzip", false);
+ response.setHeader("Content-Disposition", "attachment", false);
+
+ var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
+ bos.setOutputStream(response.bodyOutputStream);
+ bos.writeByteArray(responseBody, responseBody.length);
+}
+
+function finishTest1(subject, topic, data) {
+ let file = subject.QueryInterface(Ci.nsIDownload).targetFile;
+ do_check_true(file.path.search("test1.gz") != 0);
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
+ fis.init(file, -1, -1, 0);
+ let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
+ bis.setInputStream(fis);
+ let str = bis.readByteArray(bis.available());
+ do_check_matches(str, responseBody);
+}
+
+/*
+ * Second test: a file with Content-Type text/html and Content-Encoding gzip
+ * should not be decoded in a round-trip, if its filename ends in ".gz".
+ * We specify a Content-disposition header to force it to be saved as a file.
+ */
+function testResponse2(metadata, response) {
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Content-Encoding", "gzip", false);
+ response.setHeader("Content-Disposition", "attachment", false);
+
+ var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
+ bos.setOutputStream(response.bodyOutputStream);
+ bos.writeByteArray(responseBody, responseBody.length);
+}
+
+function finishTest2(subject, topic, data) {
+ let file = subject.QueryInterface(Ci.nsIDownload).targetFile;
+ do_check_true(file.path.search("test2.gz") != 0);
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
+ fis.init(file, -1, -1, 0);
+ let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
+ bis.setInputStream(fis);
+ let str = bis.readByteArray(bis.available());
+ do_check_matches(str, responseBody);
+}
+
+function testResponse3(metadata, response) {
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Content-Encoding", "gzip", false);
+ response.setHeader("Content-Disposition", "attachment", false);
+
+ var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
+ bos.setOutputStream(response.bodyOutputStream);
+ bos.writeByteArray(responseBody, responseBody.length);
+}
+
+function finishTest3(subject, topic, data) {
+ let file = subject.QueryInterface(Ci.nsIDownload).targetFile;
+ do_check_true(file.path.search("test3.txt") != 0);
+ let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
+ fis.init(file, -1, -1, 0);
+ let bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
+ bis.setInputStream(fis);
+ let str = bis.readByteArray(bis.available());
+ let decodedBody = [ 116, 101, 115, 116, 10 ]; // 't','e','s','t','\n'
+ do_check_matches(str, decodedBody);
+}
+
+var tests = [
+ [ "/test1.gz", testResponse1, finishTest1 ],
+ [ "/test2.gz", testResponse2, finishTest2 ],
+ [ "/test3.txt", testResponse3, finishTest3 ],
+];
+
+function run_test() {
+// do_load_child_test_harness();
+ httpserver = new HttpServer();
+ httpserver.start(4444);
+ do_test_pending();
+
+ initChildTestEnv();
+
+ for (let set of tests)
+ httpserver.registerPathHandler(set[0], set[1]);
+
+ runNextTest();
+}
diff --git a/uriloader/exthandler/tests/unit_ipc/xpcshell.ini b/uriloader/exthandler/tests/unit_ipc/xpcshell.ini
new file mode 100644
index 000000000..962b93dec
--- /dev/null
+++ b/uriloader/exthandler/tests/unit_ipc/xpcshell.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+head =
+tail =
+
+[test_encoding.js]
+# Bug 676995: test hangs consistently on Android
+# Bug 907732: thunderbird still uses legacy downloads manager.
+skip-if = (os == "android" || buildapp == '../mail')