summaryrefslogtreecommitdiffstats
path: root/browser/components/tests
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/tests')
-rw-r--r--browser/components/tests/browser/.eslintrc.js7
-rw-r--r--browser/components/tests/browser/browser.ini4
-rw-r--r--browser/components/tests/browser/browser_bug538331.js426
-rw-r--r--browser/components/tests/browser/browser_contentpermissionprompt.js166
-rw-r--r--browser/components/tests/unit/.eslintrc.js7
-rw-r--r--browser/components/tests/unit/data/engine-de-DE.xml8
-rw-r--r--browser/components/tests/unit/distribution.ini58
-rw-r--r--browser/components/tests/unit/head.js9
-rw-r--r--browser/components/tests/unit/test_browserGlue_migration_loop_cleanup.js32
-rw-r--r--browser/components/tests/unit/test_distribution.js152
-rw-r--r--browser/components/tests/unit/xpcshell.ini10
11 files changed, 879 insertions, 0 deletions
diff --git a/browser/components/tests/browser/.eslintrc.js b/browser/components/tests/browser/.eslintrc.js
new file mode 100644
index 000000000..c764b133d
--- /dev/null
+++ b/browser/components/tests/browser/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../testing/mochitest/browser.eslintrc.js"
+ ]
+};
diff --git a/browser/components/tests/browser/browser.ini b/browser/components/tests/browser/browser.ini
new file mode 100644
index 000000000..6d00b69fa
--- /dev/null
+++ b/browser/components/tests/browser/browser.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+
+[browser_bug538331.js]
+[browser_contentpermissionprompt.js]
diff --git a/browser/components/tests/browser/browser_bug538331.js b/browser/components/tests/browser/browser_bug538331.js
new file mode 100644
index 000000000..fce3790a0
--- /dev/null
+++ b/browser/components/tests/browser/browser_bug538331.js
@@ -0,0 +1,426 @@
+/* -*- 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/
+ */
+
+const PREF_POSTUPDATE = "app.update.postupdate";
+const PREF_MSTONE = "browser.startup.homepage_override.mstone";
+const PREF_OVERRIDE_URL = "startup.homepage_override_url";
+
+const DEFAULT_PREF_URL = "http://pref.example.com/";
+const DEFAULT_UPDATE_URL = "http://example.com/";
+
+const XML_EMPTY = "<?xml version=\"1.0\"?><updates xmlns=" +
+ "\"http://www.mozilla.org/2005/app-update\"></updates>";
+
+const XML_PREFIX = "<updates xmlns=\"http://www.mozilla.org/2005/app-update\"" +
+ "><update appVersion=\"1.0\" buildID=\"20080811053724\" " +
+ "channel=\"nightly\" displayVersion=\"Version 1.0\" " +
+ "extensionVersion=\"1.0\" installDate=\"1238441400314\" " +
+ "isCompleteUpdate=\"true\" name=\"Update Test 1.0\" " +
+ "serviceURL=\"https://example.com/\" showNeverForVersion=" +
+ "\"false\" showPrompt=\"false\" type=" +
+ "\"minor\" version=\"version 1.0\" detailsURL=" +
+ "\"http://example.com/\" previousAppVersion=\"1.0\" " +
+ "statusText=\"The Update was successfully installed\" " +
+ "foregroundDownload=\"true\"";
+
+const XML_SUFFIX = "><patch type=\"complete\" URL=\"http://example.com/\" " +
+ "hashFunction=\"MD5\" hashValue=" +
+ "\"6232cd43a1c77e30191c53a329a3f99d\" size=\"775\" " +
+ "selected=\"true\" state=\"succeeded\"/></update></updates>";
+
+// nsBrowserContentHandler.js defaultArgs tests
+const BCH_TESTS = [
+ {
+ description: "no mstone change and no update",
+ noPostUpdatePref: true,
+ noMstoneChange: true
+ }, {
+ description: "mstone changed and no update",
+ noPostUpdatePref: true,
+ prefURL: DEFAULT_PREF_URL
+ }, {
+ description: "no mstone change and update with 'showURL' for actions",
+ actions: "showURL",
+ noMstoneChange: true
+ }, {
+ description: "update without actions",
+ prefURL: DEFAULT_PREF_URL
+ }, {
+ description: "update with 'showURL' for actions",
+ actions: "showURL",
+ prefURL: DEFAULT_PREF_URL
+ }, {
+ description: "update with 'showURL' for actions and openURL",
+ actions: "showURL",
+ openURL: DEFAULT_UPDATE_URL
+ }, {
+ description: "update with 'showURL showAlert' for actions",
+ actions: "showAlert showURL",
+ prefURL: DEFAULT_PREF_URL
+ }, {
+ description: "update with 'showAlert showURL' for actions and openURL",
+ actions: "showAlert showURL",
+ openURL: DEFAULT_UPDATE_URL
+ }, {
+ description: "update with 'showURL showNotification' for actions",
+ actions: "showURL showNotification",
+ prefURL: DEFAULT_PREF_URL
+ }, {
+ description: "update with 'showNotification showURL' for actions and " +
+ "openURL",
+ actions: "showNotification showURL",
+ openURL: DEFAULT_UPDATE_URL
+ }, {
+ description: "update with 'showAlert showURL showNotification' for actions",
+ actions: "showAlert showURL showNotification",
+ prefURL: DEFAULT_PREF_URL
+ }, {
+ description: "update with 'showNotification showURL showAlert' for " +
+ "actions and openURL",
+ actions: "showNotification showURL showAlert",
+ openURL: DEFAULT_UPDATE_URL
+ }, {
+ description: "update with 'showAlert' for actions",
+ actions: "showAlert"
+ }, {
+ description: "update with 'showAlert showNotification' for actions",
+ actions: "showAlert showNotification"
+ }, {
+ description: "update with 'showNotification' for actions",
+ actions: "showNotification"
+ }, {
+ description: "update with 'showNotification showAlert' for actions",
+ actions: "showNotification showAlert"
+ }, {
+ description: "update with 'silent' for actions",
+ actions: "silent"
+ }, {
+ description: "update with 'silent showURL showAlert showNotification' " +
+ "for actions and openURL",
+ actions: "silent showURL showAlert showNotification"
+ }
+];
+
+var gOriginalMStone;
+var gOriginalOverrideURL;
+
+this.__defineGetter__("gBG", function() {
+ delete this.gBG;
+ return this.gBG = Cc["@mozilla.org/browser/browserglue;1"].
+ getService(Ci.nsIObserver);
+});
+
+function test()
+{
+ waitForExplicitFinish();
+
+ // Reset the startup page pref since it may have been set by other tests
+ // and we will assume it is default.
+ Services.prefs.clearUserPref('browser.startup.page');
+
+ if (gPrefService.prefHasUserValue(PREF_MSTONE)) {
+ gOriginalMStone = gPrefService.getCharPref(PREF_MSTONE);
+ }
+
+ if (gPrefService.prefHasUserValue(PREF_OVERRIDE_URL)) {
+ gOriginalOverrideURL = gPrefService.getCharPref(PREF_OVERRIDE_URL);
+ }
+
+ testDefaultArgs();
+}
+
+var gWindowCatcher = {
+ windowsOpen: 0,
+ finishCalled: false,
+ start: function() {
+ Services.ww.registerNotification(this);
+ },
+
+ finish: function(aFunc) {
+ Services.ww.unregisterNotification(this);
+ this.finishFunc = aFunc;
+ if (this.windowsOpen > 0)
+ return;
+
+ this.finishFunc();
+ },
+
+ closeWindow: function (win) {
+ info("window catcher closing window: " + win.document.documentURI);
+ win.close();
+ this.windowsOpen--;
+ if (this.finishFunc) {
+ this.finish(this.finishFunc);
+ }
+ },
+
+ windowLoad: function (win) {
+ executeSoon(this.closeWindow.bind(this, win));
+ },
+
+ observe: function(subject, topic, data) {
+ if (topic != "domwindowopened")
+ return;
+
+ this.windowsOpen++;
+ let win = subject.QueryInterface(Ci.nsIDOMWindow);
+ info("window catcher caught window opening: " + win.document.documentURI);
+ win.addEventListener("load", function () {
+ win.removeEventListener("load", arguments.callee, false);
+ gWindowCatcher.windowLoad(win);
+ }, false);
+ }
+};
+
+function finish_test()
+{
+ // Reset browser.startup.homepage_override.mstone to the original value or
+ // clear it if it didn't exist.
+ if (gOriginalMStone) {
+ gPrefService.setCharPref(PREF_MSTONE, gOriginalMStone);
+ } else if (gPrefService.prefHasUserValue(PREF_MSTONE)) {
+ gPrefService.clearUserPref(PREF_MSTONE);
+ }
+
+ // Reset startup.homepage_override_url to the original value or clear it if
+ // it didn't exist.
+ if (gOriginalOverrideURL) {
+ gPrefService.setCharPref(PREF_OVERRIDE_URL, gOriginalOverrideURL);
+ } else if (gPrefService.prefHasUserValue(PREF_OVERRIDE_URL)) {
+ gPrefService.clearUserPref(PREF_OVERRIDE_URL);
+ }
+
+ writeUpdatesToXMLFile(XML_EMPTY);
+ reloadUpdateManagerData();
+
+ finish();
+}
+
+// Test the defaultArgs returned by nsBrowserContentHandler after an update
+function testDefaultArgs()
+{
+ // Clear any pre-existing override in defaultArgs that are hanging around.
+ // This will also set the browser.startup.homepage_override.mstone preference
+ // if it isn't already set.
+ Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler).defaultArgs;
+
+ let originalMstone = gPrefService.getCharPref(PREF_MSTONE);
+
+ gPrefService.setCharPref(PREF_OVERRIDE_URL, DEFAULT_PREF_URL);
+
+ writeUpdatesToXMLFile(XML_EMPTY);
+ reloadUpdateManagerData();
+
+ for (let i = 0; i < BCH_TESTS.length; i++) {
+ let test = BCH_TESTS[i];
+ ok(true, "Test nsBrowserContentHandler " + (i + 1) + ": " + test.description);
+
+ if (test.actions) {
+ let actionsXML = " actions=\"" + test.actions + "\"";
+ if (test.openURL) {
+ actionsXML += " openURL=\"" + test.openURL + "\"";
+ }
+ writeUpdatesToXMLFile(XML_PREFIX + actionsXML + XML_SUFFIX);
+ } else {
+ writeUpdatesToXMLFile(XML_EMPTY);
+ }
+
+ reloadUpdateManagerData();
+
+ let noOverrideArgs = Cc["@mozilla.org/browser/clh;1"].
+ getService(Ci.nsIBrowserHandler).defaultArgs;
+
+ let overrideArgs = "";
+ if (test.prefURL) {
+ overrideArgs = test.prefURL;
+ } else if (test.openURL) {
+ overrideArgs = test.openURL;
+ }
+
+ if (overrideArgs == "" && noOverrideArgs) {
+ overrideArgs = noOverrideArgs;
+ } else if (noOverrideArgs) {
+ overrideArgs += "|" + noOverrideArgs;
+ }
+
+ if (test.noMstoneChange === undefined) {
+ gPrefService.setCharPref(PREF_MSTONE, "PreviousMilestone");
+ }
+
+ if (test.noPostUpdatePref == undefined) {
+ gPrefService.setBoolPref(PREF_POSTUPDATE, true);
+ }
+
+ let defaultArgs = Cc["@mozilla.org/browser/clh;1"].
+ getService(Ci.nsIBrowserHandler).defaultArgs;
+ is(defaultArgs, overrideArgs, "correct value returned by defaultArgs");
+
+ if (test.noMstoneChange === undefined || test.noMstoneChange != true) {
+ let newMstone = gPrefService.getCharPref(PREF_MSTONE);
+ is(originalMstone, newMstone, "preference " + PREF_MSTONE +
+ " should have been updated");
+ }
+
+ if (gPrefService.prefHasUserValue(PREF_POSTUPDATE)) {
+ gPrefService.clearUserPref(PREF_POSTUPDATE);
+ }
+ }
+
+ testShowNotification();
+}
+
+// nsBrowserGlue.js _showUpdateNotification notification tests
+const BG_NOTIFY_TESTS = [
+ {
+ description: "'silent showNotification' actions should not display a notification",
+ actions: "silent showNotification"
+ }, {
+ description: "'showNotification' for actions should display a notification",
+ actions: "showNotification"
+ }, {
+ description: "no actions and empty updates.xml",
+ }, {
+ description: "'showAlert' for actions should not display a notification",
+ actions: "showAlert"
+ }, {
+ // This test MUST be the last test in the array to test opening the url
+ // provided by the updates.xml.
+ description: "'showNotification' for actions with custom notification " +
+ "attributes should display a notification",
+ actions: "showNotification",
+ notificationText: "notification text",
+ notificationURL: DEFAULT_UPDATE_URL,
+ notificationButtonLabel: "button label",
+ notificationButtonAccessKey: "b"
+ }
+];
+
+// Test showing a notification after an update
+// _showUpdateNotification in nsBrowserGlue.js
+function testShowNotification()
+{
+ let notifyBox = document.getElementById("high-priority-global-notificationbox");
+
+ // Catches any windows opened by these tests (e.g. alert windows) and closes
+ // them
+ gWindowCatcher.start();
+
+ for (let i = 0; i < BG_NOTIFY_TESTS.length; i++) {
+ let test = BG_NOTIFY_TESTS[i];
+ ok(true, "Test showNotification " + (i + 1) + ": " + test.description);
+
+ if (test.actions) {
+ let actionsXML = " actions=\"" + test.actions + "\"";
+ if (test.notificationText) {
+ actionsXML += " notificationText=\"" + test.notificationText + "\"";
+ }
+ if (test.notificationURL) {
+ actionsXML += " notificationURL=\"" + test.notificationURL + "\"";
+ }
+ if (test.notificationButtonLabel) {
+ actionsXML += " notificationButtonLabel=\"" + test.notificationButtonLabel + "\"";
+ }
+ if (test.notificationButtonAccessKey) {
+ actionsXML += " notificationButtonAccessKey=\"" + test.notificationButtonAccessKey + "\"";
+ }
+ writeUpdatesToXMLFile(XML_PREFIX + actionsXML + XML_SUFFIX);
+ } else {
+ writeUpdatesToXMLFile(XML_EMPTY);
+ }
+
+ reloadUpdateManagerData();
+ gPrefService.setBoolPref(PREF_POSTUPDATE, true);
+
+ gBG.observe(null, "browser-glue-test", "post-update-notification");
+
+ let updateBox = notifyBox.getNotificationWithValue("post-update-notification");
+ if (test.actions && test.actions.indexOf("showNotification") != -1 &&
+ test.actions.indexOf("silent") == -1) {
+ ok(updateBox, "Update notification box should have been displayed");
+ if (updateBox) {
+ if (test.notificationText) {
+ is(updateBox.label, test.notificationText, "Update notification box " +
+ "should have the label provided by the update");
+ }
+ if (test.notificationButtonLabel) {
+ var button = updateBox.getElementsByTagName("button").item(0);
+ is(button.label, test.notificationButtonLabel, "Update notification " +
+ "box button should have the label provided by the update");
+ if (test.notificationButtonAccessKey) {
+ let accessKey = button.getAttribute("accesskey");
+ is(accessKey, test.notificationButtonAccessKey, "Update " +
+ "notification box button should have the accesskey " +
+ "provided by the update");
+ }
+ }
+ // The last test opens an url and verifies the url from the updates.xml
+ // is correct.
+ if (i == (BG_NOTIFY_TESTS.length - 1)) {
+ // Wait for any windows caught by the windowcatcher to close
+ gWindowCatcher.finish(function () {
+ BrowserTestUtils.waitForNewTab(gBrowser).then(testNotificationURL);
+ button.click();
+ });
+ } else {
+ notifyBox.removeAllNotifications(true);
+ }
+ } else if (i == (BG_NOTIFY_TESTS.length - 1)) {
+ // If updateBox is null the test has already reported errors so bail
+ finish_test();
+ }
+ } else {
+ ok(!updateBox, "Update notification box should not have been displayed");
+ }
+
+ let prefHasUserValue = gPrefService.prefHasUserValue(PREF_POSTUPDATE);
+ is(prefHasUserValue, false, "preference " + PREF_POSTUPDATE +
+ " shouldn't have a user value");
+ }
+}
+
+// Test opening the url provided by the updates.xml in the last test
+function testNotificationURL()
+{
+ ok(true, "Test testNotificationURL: clicking the notification button " +
+ "opened the url specified by the update");
+ let href = gBrowser.currentURI.spec;
+ let expectedURL = BG_NOTIFY_TESTS[BG_NOTIFY_TESTS.length - 1].notificationURL;
+ is(href, expectedURL, "The url opened from the notification should be the " +
+ "url provided by the update");
+ gBrowser.removeCurrentTab();
+ window.focus();
+ finish_test();
+}
+
+/* Reloads the update metadata from disk */
+function reloadUpdateManagerData()
+{
+ Cc["@mozilla.org/updates/update-manager;1"].getService(Ci.nsIUpdateManager).
+ QueryInterface(Ci.nsIObserver).observe(null, "um-reload-update-data", "");
+}
+
+
+function writeUpdatesToXMLFile(aText)
+{
+ const PERMS_FILE = 0o644;
+
+ const MODE_WRONLY = 0x02;
+ const MODE_CREATE = 0x08;
+ const MODE_TRUNCATE = 0x20;
+
+ let file = Cc["@mozilla.org/file/directory_service;1"].
+ getService(Ci.nsIProperties).
+ get("UpdRootD", Ci.nsIFile);
+ file.append("updates.xml");
+ let fos = Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(Ci.nsIFileOutputStream);
+ if (!file.exists()) {
+ file.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
+ }
+ fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE, 0);
+ fos.write(aText, aText.length);
+ fos.close();
+}
diff --git a/browser/components/tests/browser/browser_contentpermissionprompt.js b/browser/components/tests/browser/browser_contentpermissionprompt.js
new file mode 100644
index 000000000..054aa22e8
--- /dev/null
+++ b/browser/components/tests/browser/browser_contentpermissionprompt.js
@@ -0,0 +1,166 @@
+/**
+ * These tests test nsBrowserGlue's nsIContentPermissionPrompt
+ * implementation behaviour with various types of
+ * nsIContentPermissionRequests.
+ */
+
+"use strict";
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Integration.jsm", this);
+
+XPCOMUtils.defineLazyServiceGetter(this, "ContentPermissionPrompt",
+ "@mozilla.org/content-permission/prompt;1",
+ "nsIContentPermissionPrompt");
+
+/**
+ * This is a partial implementation of nsIContentPermissionType.
+ *
+ * @param {string} type
+ * The string defining what type of permission is being requested.
+ * Example: "geo", "desktop-notification".
+ * @return nsIContentPermissionType implementation.
+ */
+function MockContentPermissionType(type) {
+ this.type = type;
+}
+
+MockContentPermissionType.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType]),
+ // We expose the wrappedJSObject so that we can be sure
+ // in some of our tests that we're passing the right
+ // nsIContentPermissionType around.
+ wrappedJSObject: this,
+};
+
+/**
+ * This is a partial implementation of nsIContentPermissionRequest.
+ *
+ * @param {Array<nsIContentPermissionType>} typesArray
+ * The types to assign to this nsIContentPermissionRequest,
+ * in order. You probably want to use MockContentPermissionType.
+ * @return nsIContentPermissionRequest implementation.
+ */
+function MockContentPermissionRequest(typesArray) {
+ this.types = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+ for (let type of typesArray) {
+ this.types.appendElement(type, false);
+ }
+}
+
+MockContentPermissionRequest.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
+ // We expose the wrappedJSObject so that we can be sure
+ // in some of our tests that we're passing the right
+ // nsIContentPermissionRequest around.
+ wrappedJSObject: this,
+ // For some of our tests, we want to make sure that the
+ // request is cancelled, so we add some instrumentation here
+ // to check that cancel() is called.
+ cancel() {
+ this.cancelled = true;
+ },
+ cancelled: false,
+};
+
+/**
+ * Tests that if the nsIContentPermissionRequest has an empty
+ * types array, that NS_ERROR_UNEXPECTED is thrown, and the
+ * request is cancelled.
+ */
+add_task(function* test_empty_types() {
+ let mockRequest = new MockContentPermissionRequest([]);
+ Assert.throws(() => { ContentPermissionPrompt.prompt(mockRequest); },
+ /NS_ERROR_UNEXPECTED/,
+ "Should have thrown NS_ERROR_UNEXPECTED.");
+ Assert.ok(mockRequest.cancelled, "Should have cancelled the request.");
+});
+
+/**
+ * Tests that if the nsIContentPermissionRequest has more than
+ * one type, that NS_ERROR_UNEXPECTED is thrown, and the request
+ * is cancelled.
+ */
+add_task(function* test_multiple_types() {
+ let mockRequest = new MockContentPermissionRequest([
+ new MockContentPermissionType("test1"),
+ new MockContentPermissionType("test2"),
+ ]);
+
+ Assert.throws(() => { ContentPermissionPrompt.prompt(mockRequest); },
+ /NS_ERROR_UNEXPECTED/);
+ Assert.ok(mockRequest.cancelled, "Should have cancelled the request.");
+});
+
+/**
+ * Tests that if the nsIContentPermissionRequest has a type that
+ * does not implement nsIContentPermissionType that NS_NOINTERFACE
+ * is thrown, and the request is cancelled.
+ */
+add_task(function* test_not_permission_type() {
+ let mockRequest = new MockContentPermissionRequest([
+ { QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]) },
+ ]);
+
+ Assert.throws(() => { ContentPermissionPrompt.prompt(mockRequest); },
+ /NS_NOINTERFACE/);
+ Assert.ok(mockRequest.cancelled, "Should have cancelled the request.");
+});
+
+/**
+ * Tests that if the nsIContentPermissionRequest is for a type
+ * that is not recognized, that NS_ERROR_FAILURE is thrown and
+ * the request is cancelled.
+ */
+add_task(function* test_unrecognized_type() {
+ let mockRequest = new MockContentPermissionRequest([
+ new MockContentPermissionType("test1"),
+ ]);
+
+ Assert.throws(() => { ContentPermissionPrompt.prompt(mockRequest); },
+ /NS_ERROR_FAILURE/);
+ Assert.ok(mockRequest.cancelled, "Should have cancelled the request.");
+});
+
+/**
+ * Tests that if we meet the minimal requirements for a
+ * nsIContentPermissionRequest, that it will be passed to
+ * ContentPermissionIntegration's createPermissionPrompt
+ * method.
+ */
+add_task(function* test_working_request() {
+ let mockType = new MockContentPermissionType("test-permission-type");
+ let mockRequest = new MockContentPermissionRequest([mockType]);
+
+ // mockPermissionPrompt is what createPermissionPrompt
+ // will return. Returning some kind of object should be
+ // enough to convince nsBrowserGlue that everything went
+ // okay.
+ let didPrompt = false;
+ let mockPermissionPrompt = {
+ prompt() {
+ didPrompt = true;
+ }
+ };
+
+ let integration = (base) => ({
+ createPermissionPrompt(type, request) {
+ Assert.equal(type, "test-permission-type");
+ Assert.ok(Object.is(request.wrappedJSObject, mockRequest.wrappedJSObject));
+ return mockPermissionPrompt;
+ },
+ });
+
+ // Register an integration so that we can capture the
+ // calls into ContentPermissionIntegration.
+ try {
+ Integration.contentPermission.register(integration);
+
+ ContentPermissionPrompt.prompt(mockRequest);
+ Assert.ok(!mockRequest.cancelled,
+ "Should not have cancelled the request.");
+ Assert.ok(didPrompt, "Should have tried to show the prompt");
+ } finally {
+ Integration.contentPermission.unregister(integration);
+ }
+});
diff --git a/browser/components/tests/unit/.eslintrc.js b/browser/components/tests/unit/.eslintrc.js
new file mode 100644
index 000000000..fee088c17
--- /dev/null
+++ b/browser/components/tests/unit/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ]
+};
diff --git a/browser/components/tests/unit/data/engine-de-DE.xml b/browser/components/tests/unit/data/engine-de-DE.xml
new file mode 100644
index 000000000..b9fa0a464
--- /dev/null
+++ b/browser/components/tests/unit/data/engine-de-DE.xml
@@ -0,0 +1,8 @@
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Google</ShortName>
+<Description>override-de-DE</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Url type="text/html" method="GET" template="http://searchtest.local">
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+</SearchPlugin>
diff --git a/browser/components/tests/unit/distribution.ini b/browser/components/tests/unit/distribution.ini
new file mode 100644
index 000000000..d7d298808
--- /dev/null
+++ b/browser/components/tests/unit/distribution.ini
@@ -0,0 +1,58 @@
+# Distribution Configuration File
+# Test of distribution preferences
+
+[Global]
+id=disttest
+version=1.0
+about=Test distribution file
+about.en-US=Tèƨƭ δïƨƭřïβúƭïôñ ƒïℓè
+
+[Preferences]
+distribution.test.string="Test String"
+distribution.test.string.noquotes=Test String
+distribution.test.int=777
+distribution.test.bool.true=true
+distribution.test.bool.false=false
+distribution.test.empty=
+
+distribution.test.pref.locale="%LOCALE%"
+distribution.test.pref.language.reset="Preference Set"
+distribution.test.pref.locale.reset="Preference Set"
+distribution.test.pref.locale.set="Preference Set"
+distribution.test.pref.language.set="Preference Set"
+
+[Preferences-en]
+distribution.test.pref.language.en="en"
+distribution.test.pref.language.reset=
+distribution.test.pref.language.set="Language Set"
+distribution.test.pref.locale.set="Language Set"
+
+[Preferences-en-US]
+distribution.test.pref.locale.en-US="en-US"
+distribution.test.pref.locale.reset=
+distribution.test.pref.locale.set="Locale Set"
+
+
+[Preferences-de]
+distribution.test.pref.language.de="de"
+
+[LocalizablePreferences]
+distribution.test.locale="%LOCALE%"
+distribution.test.language.reset="Preference Set"
+distribution.test.locale.reset="Preference Set"
+distribution.test.locale.set="Preference Set"
+distribution.test.language.set="Preference Set"
+
+[LocalizablePreferences-en]
+distribution.test.language.en="en"
+distribution.test.language.reset=
+distribution.test.language.set="Language Set"
+distribution.test.locale.set="Language Set"
+
+[LocalizablePreferences-en-US]
+distribution.test.locale.en-US="en-US"
+distribution.test.locale.reset=
+distribution.test.locale.set="Locale Set"
+
+[LocalizablePreferences-de]
+distribution.test.language.de="de"
diff --git a/browser/components/tests/unit/head.js b/browser/components/tests/unit/head.js
new file mode 100644
index 000000000..3d4e23452
--- /dev/null
+++ b/browser/components/tests/unit/head.js
@@ -0,0 +1,9 @@
+/* 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 {interfaces: Ci, classes: Cc, results: Cr, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+var gProfD = do_get_profile().QueryInterface(Ci.nsILocalFile);
diff --git a/browser/components/tests/unit/test_browserGlue_migration_loop_cleanup.js b/browser/components/tests/unit/test_browserGlue_migration_loop_cleanup.js
new file mode 100644
index 000000000..a68503db3
--- /dev/null
+++ b/browser/components/tests/unit/test_browserGlue_migration_loop_cleanup.js
@@ -0,0 +1,32 @@
+const UI_VERSION = 41;
+const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
+const TOPICDATA_BROWSERGLUE_TEST = "force-ui-migration";
+
+var gBrowserGlue = Cc["@mozilla.org/browser/browserglue;1"]
+ .getService(Ci.nsIObserver);
+
+Services.prefs.setIntPref("browser.migration.version", UI_VERSION - 1);
+
+add_task(function* test_check_cleanup_loop_prefs() {
+ Services.prefs.setBoolPref("loop.createdRoom", true);
+ Services.prefs.setBoolPref("loop1.createdRoom", true);
+ Services.prefs.setBoolPref("loo.createdRoom", true);
+
+ // Simulate a migration.
+ gBrowserGlue.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_BROWSERGLUE_TEST);
+
+ Assert.throws(() => Services.prefs.getBoolPref("loop.createdRoom"),
+ /NS_ERROR_UNEXPECTED/,
+ "should have cleared old loop preference 'loop.createdRoom'");
+ Assert.ok(Services.prefs.getBoolPref("loop1.createdRoom"),
+ "should have left non-loop pref 'loop1.createdRoom' untouched");
+ Assert.ok(Services.prefs.getBoolPref("loo.createdRoom"),
+ "should have left non-loop pref 'loo.createdRoom' untouched");
+});
+
+do_register_cleanup(() => {
+ Services.prefs.clearUserPref("browser.migration.version");
+ Services.prefs.clearUserPref("loop.createdRoom");
+ Services.prefs.clearUserPref("loop1.createdRoom");
+ Services.prefs.clearUserPref("loo.createdRoom");
+});
diff --git a/browser/components/tests/unit/test_distribution.js b/browser/components/tests/unit/test_distribution.js
new file mode 100644
index 000000000..183ab85d6
--- /dev/null
+++ b/browser/components/tests/unit/test_distribution.js
@@ -0,0 +1,152 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that preferences are properly set by distribution.ini
+ */
+
+Cu.import("resource://gre/modules/LoadContextInfo.jsm");
+
+// Import common head.
+var commonFile = do_get_file("../../../../toolkit/components/places/tests/head_common.js", false);
+/* import-globals-from ../../../../toolkit/components/places/tests/head_common.js */
+if (commonFile) {
+ let uri = Services.io.newFileURI(commonFile);
+ Services.scriptloader.loadSubScript(uri.spec, this);
+}
+
+const TOPICDATA_DISTRIBUTION_CUSTOMIZATION = "force-distribution-customization";
+const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
+
+/**
+ * Copy the engine-distribution.xml engine to a fake distribution
+ * created in the profile, and registered with the directory service.
+ * Create an empty en-US directory to make sure it isn't used.
+ */
+function installDistributionEngine() {
+ const XRE_APP_DISTRIBUTION_DIR = "XREAppDist";
+
+ let dir = gProfD.clone();
+ dir.append("distribution");
+ let distDir = dir.clone();
+
+ dir.append("searchplugins");
+ dir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+ dir.append("locale");
+ dir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ let localeDir = dir.clone();
+
+ dir.append("en-US");
+ dir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+ localeDir.append("de-DE");
+ localeDir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+ do_get_file("data/engine-de-DE.xml").copyTo(localeDir, "engine-de-DE.xml");
+
+ Services.dirsvc.registerProvider({
+ getFile: function(aProp, aPersistent) {
+ aPersistent.value = true;
+ if (aProp == XRE_APP_DISTRIBUTION_DIR)
+ return distDir.clone();
+ return null;
+ }
+ });
+}
+
+function run_test() {
+ // Set special pref to load distribution.ini from the profile folder.
+ Services.prefs.setBoolPref("distribution.testing.loadFromProfile", true);
+
+ // Copy distribution.ini file to the profile dir.
+ let distroDir = gProfD.clone();
+ distroDir.leafName = "distribution";
+ let iniFile = distroDir.clone();
+ iniFile.append("distribution.ini");
+ if (iniFile.exists()) {
+ iniFile.remove(false);
+ print("distribution.ini already exists, did some test forget to cleanup?");
+ }
+
+ let testDistributionFile = gTestDir.clone();
+ testDistributionFile.append("distribution.ini");
+ testDistributionFile.copyTo(distroDir, "distribution.ini");
+ Assert.ok(testDistributionFile.exists());
+
+ installDistributionEngine();
+
+ run_next_test();
+}
+
+do_register_cleanup(function () {
+ // Remove the distribution dir, even if the test failed, otherwise all
+ // next tests will use it.
+ let distDir = gProfD.clone();
+ distDir.append("distribution");
+ distDir.remove(true);
+ Assert.ok(!distDir.exists());
+});
+
+add_task(function* () {
+ // Force distribution.
+ let glue = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver)
+ glue.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_DISTRIBUTION_CUSTOMIZATION);
+
+ var defaultBranch = Services.prefs.getDefaultBranch(null);
+
+ Assert.equal(defaultBranch.getCharPref("distribution.id"), "disttest");
+ Assert.equal(defaultBranch.getCharPref("distribution.version"), "1.0");
+ Assert.equal(defaultBranch.getComplexValue("distribution.about", Ci.nsISupportsString).data, "Tèƨƭ δïƨƭřïβúƭïôñ ƒïℓè");
+
+ Assert.equal(defaultBranch.getCharPref("distribution.test.string"), "Test String");
+ Assert.equal(defaultBranch.getCharPref("distribution.test.string.noquotes"), "Test String");
+ Assert.equal(defaultBranch.getIntPref("distribution.test.int"), 777);
+ Assert.equal(defaultBranch.getBoolPref("distribution.test.bool.true"), true);
+ Assert.equal(defaultBranch.getBoolPref("distribution.test.bool.false"), false);
+
+ Assert.throws(() => defaultBranch.getCharPref("distribution.test.empty"));
+ Assert.throws(() => defaultBranch.getIntPref("distribution.test.empty"));
+ Assert.throws(() => defaultBranch.getBoolPref("distribution.test.empty"));
+
+ Assert.equal(defaultBranch.getCharPref("distribution.test.pref.locale"), "en-US");
+ Assert.equal(defaultBranch.getCharPref("distribution.test.pref.language.en"), "en");
+ Assert.equal(defaultBranch.getCharPref("distribution.test.pref.locale.en-US"), "en-US");
+ Assert.throws(() => defaultBranch.getCharPref("distribution.test.pref.language.de"));
+ // This value was never set because of the empty language specific pref
+ Assert.throws(() => defaultBranch.getCharPref("distribution.test.pref.language.reset"));
+ // This value was never set because of the empty locale specific pref
+ Assert.throws(() => defaultBranch.getCharPref("distribution.test.pref.locale.reset"));
+ // This value was overridden by a locale specific setting
+ Assert.equal(defaultBranch.getCharPref("distribution.test.pref.locale.set"), "Locale Set");
+ // This value was overridden by a language specific setting
+ Assert.equal(defaultBranch.getCharPref("distribution.test.pref.language.set"), "Language Set");
+ // Language should not override locale
+ Assert.notEqual(defaultBranch.getCharPref("distribution.test.pref.locale.set"), "Language Set");
+
+ Assert.equal(defaultBranch.getComplexValue("distribution.test.locale", Ci.nsIPrefLocalizedString).data, "en-US");
+ Assert.equal(defaultBranch.getComplexValue("distribution.test.language.en", Ci.nsIPrefLocalizedString).data, "en");
+ Assert.equal(defaultBranch.getComplexValue("distribution.test.locale.en-US", Ci.nsIPrefLocalizedString).data, "en-US");
+ Assert.throws(() => defaultBranch.getComplexValue("distribution.test.language.de", Ci.nsIPrefLocalizedString));
+ // This value was never set because of the empty language specific pref
+ Assert.throws(() => defaultBranch.getComplexValue("distribution.test.language.reset", Ci.nsIPrefLocalizedString));
+ // This value was never set because of the empty locale specific pref
+ Assert.throws(() => defaultBranch.getComplexValue("distribution.test.locale.reset", Ci.nsIPrefLocalizedString));
+ // This value was overridden by a locale specific setting
+ Assert.equal(defaultBranch.getComplexValue("distribution.test.locale.set", Ci.nsIPrefLocalizedString).data, "Locale Set");
+ // This value was overridden by a language specific setting
+ Assert.equal(defaultBranch.getComplexValue("distribution.test.language.set", Ci.nsIPrefLocalizedString).data, "Language Set");
+ // Language should not override locale
+ Assert.notEqual(defaultBranch.getComplexValue("distribution.test.locale.set", Ci.nsIPrefLocalizedString).data, "Language Set");
+
+ do_test_pending();
+
+ Services.prefs.setCharPref("distribution.searchplugins.defaultLocale", "de-DE");
+
+ Services.search.init(function() {
+ Assert.equal(Services.search.isInitialized, true);
+ var engine = Services.search.getEngineByName("Google");
+ Assert.equal(engine.description, "override-de-DE");
+ do_test_finished();
+ });
+});
diff --git a/browser/components/tests/unit/xpcshell.ini b/browser/components/tests/unit/xpcshell.ini
new file mode 100644
index 000000000..c2f461966
--- /dev/null
+++ b/browser/components/tests/unit/xpcshell.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+head = head.js
+firefox-appdir = browser
+skip-if = toolkit == 'android'
+support-files =
+ distribution.ini
+ data/engine-de-DE.xml
+
+[test_distribution.js]
+[test_browserGlue_migration_loop_cleanup.js]