Bug 1287005 - Normalize line endings in browser/; r?jaws draft
authorGregory Szorc <gps@mozilla.com>
Thu, 14 Jul 2016 17:38:13 -0700
changeset 387904 5563d3af0e19fb9c6b968250ee61d9171067cc43
parent 387903 820287c05a9ede8b994a2b73a8853d635c463fe7
child 387905 6643243883409cdcab405ffbbccb94cfa33603a5
push id23098
push userbmo:gps@mozilla.com
push dateFri, 15 Jul 2016 00:40:01 +0000
reviewersjaws
bugs1287005
milestone50.0a1
Bug 1287005 - Normalize line endings in browser/; r?jaws MozReview-Commit-ID: 8JfOabc8gsS
browser/base/content/test/general/browser_aboutTabCrashed.js
browser/base/content/test/general/browser_backButtonFitts.js
browser/base/content/test/general/browser_bookmark_popup.js
browser/base/content/test/general/browser_bug882977.js
browser/base/content/test/general/browser_contextmenu.js
browser/base/content/test/general/browser_decoderDoctor.js
browser/base/content/test/general/browser_favicon_change.js
browser/base/content/test/general/browser_menuButtonFitts.js
browser/base/content/test/general/browser_remoteTroubleshoot.js
browser/base/content/test/general/browser_tabs_close_beforeunload.js
browser/base/content/test/social/social_crash_content_helper.js
browser/base/content/test/urlbar/browser_bug1025195_switchToTabHavingURI_aOpenParams.js
browser/components/preferences/in-content/tests/browser_bug1018066_resetScrollPosition.js
browser/components/preferences/in-content/tests/browser_defaultbrowser_alwayscheck.js
browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js
browser/components/preferences/in-content/tests/browser_notifications_do_not_disturb.js
browser/components/preferences/in-content/tests/browser_permissions_urlFieldHidden.js
browser/components/preferences/in-content/tests/browser_sanitizeOnShutdown_prefLocked.js
browser/components/shell/ShellService.jsm
browser/extensions/pocket/content/panels/js/vendor/jquery.tokeninput.min.js
--- a/browser/base/content/test/general/browser_aboutTabCrashed.js
+++ b/browser/base/content/test/general/browser_aboutTabCrashed.js
@@ -1,203 +1,203 @@
-"use strict";
-
-const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
-const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
-const COMMENTS = "Here's my test comment!";
-const EMAIL = "foo@privacy.com";
-
-/**
- * Sets up the browser to send crash reports to the local crash report
- * testing server.
- */
-add_task(function* setup() {
-  // The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables crash
-  // reports.  This test needs them enabled.  The test also needs a mock
-  // report server, and fortunately one is already set up by toolkit/
-  // crashreporter/test/Makefile.in.  Assign its URL to MOZ_CRASHREPORTER_URL,
-  // which CrashSubmit.jsm uses as a server override.
-  let env = Cc["@mozilla.org/process/environment;1"]
-              .getService(Components.interfaces.nsIEnvironment);
-  let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT");
-  let serverUrl = env.get("MOZ_CRASHREPORTER_URL");
-  env.set("MOZ_CRASHREPORTER_NO_REPORT", "");
-  env.set("MOZ_CRASHREPORTER_URL", SERVER_URL);
-
-  // On debug builds, crashing tabs results in much thinking, which
-  // slows down the test and results in intermittent test timeouts,
-  // so we'll pump up the expected timeout for this test.
-  requestLongerTimeout(2);
-
-  registerCleanupFunction(function() {
-    env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport);
-    env.set("MOZ_CRASHREPORTER_URL", serverUrl);
-  });
-});
-
-/**
- * This function returns a Promise that resolves once the following
- * actions have taken place:
- *
- * 1) A new tab is opened up at PAGE
- * 2) The tab is crashed
- * 3) The about:tabcrashed page's fields are set in accordance with
- *    fieldValues
- * 4) The tab is restored
- * 5) A crash report is received from the testing server
- * 6) Any tab crash prefs that were overwritten are reset
- *
- * @param fieldValues
- *        An Object describing how to set the about:tabcrashed
- *        fields. The following properties are accepted:
- *
- *        comments (String)
- *          The comments to put in the comment textarea
- *        email (String)
- *          The email address to put in the email address input
- *        emailMe (bool)
- *          The checked value of the "Email me" checkbox
- *        includeURL (bool)
- *          The checked value of the "Include URL" checkbox
- *
- *        If any of these fields are missing, the defaults from
- *        the user preferences are used.
- * @param expectedExtra
- *        An Object describing the expected values that the submitted
- *        crash report's extra data should contain.
- * @returns Promise
- */
-function crashTabTestHelper(fieldValues, expectedExtra) {
-  return BrowserTestUtils.withNewTab({
-    gBrowser,
-    url: PAGE,
-  }, function*(browser) {
-    let prefs = TabCrashHandler.prefs;
-    let originalSendReport = prefs.getBoolPref("sendReport");
-    let originalEmailMe = prefs.getBoolPref("emailMe");
-    let originalIncludeURL = prefs.getBoolPref("includeURL");
-    let originalEmail = prefs.getCharPref("email");
-
-    let tab = gBrowser.getTabForBrowser(browser);
-    yield BrowserTestUtils.crashBrowser(browser);
-    let doc = browser.contentDocument;
-
-    // Since about:tabcrashed will run in the parent process, we can safely
-    // manipulate its DOM nodes directly
-    let comments = doc.getElementById("comments");
-    let email = doc.getElementById("email");
-    let emailMe = doc.getElementById("emailMe");
-    let includeURL = doc.getElementById("includeURL");
-
-    if (fieldValues.hasOwnProperty("comments")) {
-      comments.value = fieldValues.comments;
-    }
-
-    if (fieldValues.hasOwnProperty("email")) {
-      email.value = fieldValues.email;
-    }
-
-    if (fieldValues.hasOwnProperty("emailMe")) {
-      emailMe.checked = fieldValues.emailMe;
-    }
-
-    if (fieldValues.hasOwnProperty("includeURL")) {
-      includeURL.checked = fieldValues.includeURL;
-    }
-
-    let crashReport = promiseCrashReport(expectedExtra);
-    let restoreTab = browser.contentDocument.getElementById("restoreTab");
-    restoreTab.click();
-    yield BrowserTestUtils.waitForEvent(tab, "SSTabRestored");
-    yield crashReport;
-
-    // Submitting the crash report may have set some prefs regarding how to
-    // send tab crash reports. Let's reset them for the next test.
-    prefs.setBoolPref("sendReport", originalSendReport);
-    prefs.setBoolPref("emailMe", originalEmailMe);
-    prefs.setBoolPref("includeURL", originalIncludeURL);
-    prefs.setCharPref("email", originalEmail);
-  });
-}
-
-/**
- * Tests what we send with the crash report by default. By default, we do not
- * send any comments, the URL of the crashing page, or the email address of
- * the user.
- */
-add_task(function* test_default() {
-  yield crashTabTestHelper({}, {
-    "Comments": null,
-    "URL": "",
-    "Email": null,
-  });
-});
-
-/**
- * Test just sending a comment.
- */
-add_task(function* test_just_a_comment() {
-  yield crashTabTestHelper({
-    comments: COMMENTS,
-  }, {
-    "Comments": COMMENTS,
-    "URL": "",
-    "Email": null,
-  });
-});
-
-/**
- * Test that we don't send email if emailMe is unchecked
- */
-add_task(function* test_no_email() {
-  yield crashTabTestHelper({
-    email: EMAIL,
-    emailMe: false,
-  }, {
-    "Comments": null,
-    "URL": "",
-    "Email": null,
-  });
-});
-
-/**
- * Test that we can send an email address if emailMe is checked
- */
-add_task(function* test_yes_email() {
-  yield crashTabTestHelper({
-    email: EMAIL,
-    emailMe: true,
-  }, {
-    "Comments": null,
-    "URL": "",
-    "Email": EMAIL,
-  });
-});
-
-/**
- * Test that we will send the URL of the page if includeURL is checked.
- */
-add_task(function* test_send_URL() {
-  yield crashTabTestHelper({
-    includeURL: true,
-  }, {
-    "Comments": null,
-    "URL": PAGE,
-    "Email": null,
-  });
-});
-
-/**
- * Test that we can send comments, the email address, and the URL
- */
-add_task(function* test_send_all() {
-  yield crashTabTestHelper({
-    includeURL: true,
-    emailMe: true,
-    email: EMAIL,
-    comments: COMMENTS,
-  }, {
-    "Comments": COMMENTS,
-    "URL": PAGE,
-    "Email": EMAIL,
-  });
-});
-
+"use strict";
+
+const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
+const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
+const COMMENTS = "Here's my test comment!";
+const EMAIL = "foo@privacy.com";
+
+/**
+ * Sets up the browser to send crash reports to the local crash report
+ * testing server.
+ */
+add_task(function* setup() {
+  // The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables crash
+  // reports.  This test needs them enabled.  The test also needs a mock
+  // report server, and fortunately one is already set up by toolkit/
+  // crashreporter/test/Makefile.in.  Assign its URL to MOZ_CRASHREPORTER_URL,
+  // which CrashSubmit.jsm uses as a server override.
+  let env = Cc["@mozilla.org/process/environment;1"]
+              .getService(Components.interfaces.nsIEnvironment);
+  let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT");
+  let serverUrl = env.get("MOZ_CRASHREPORTER_URL");
+  env.set("MOZ_CRASHREPORTER_NO_REPORT", "");
+  env.set("MOZ_CRASHREPORTER_URL", SERVER_URL);
+
+  // On debug builds, crashing tabs results in much thinking, which
+  // slows down the test and results in intermittent test timeouts,
+  // so we'll pump up the expected timeout for this test.
+  requestLongerTimeout(2);
+
+  registerCleanupFunction(function() {
+    env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport);
+    env.set("MOZ_CRASHREPORTER_URL", serverUrl);
+  });
+});
+
+/**
+ * This function returns a Promise that resolves once the following
+ * actions have taken place:
+ *
+ * 1) A new tab is opened up at PAGE
+ * 2) The tab is crashed
+ * 3) The about:tabcrashed page's fields are set in accordance with
+ *    fieldValues
+ * 4) The tab is restored
+ * 5) A crash report is received from the testing server
+ * 6) Any tab crash prefs that were overwritten are reset
+ *
+ * @param fieldValues
+ *        An Object describing how to set the about:tabcrashed
+ *        fields. The following properties are accepted:
+ *
+ *        comments (String)
+ *          The comments to put in the comment textarea
+ *        email (String)
+ *          The email address to put in the email address input
+ *        emailMe (bool)
+ *          The checked value of the "Email me" checkbox
+ *        includeURL (bool)
+ *          The checked value of the "Include URL" checkbox
+ *
+ *        If any of these fields are missing, the defaults from
+ *        the user preferences are used.
+ * @param expectedExtra
+ *        An Object describing the expected values that the submitted
+ *        crash report's extra data should contain.
+ * @returns Promise
+ */
+function crashTabTestHelper(fieldValues, expectedExtra) {
+  return BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: PAGE,
+  }, function*(browser) {
+    let prefs = TabCrashHandler.prefs;
+    let originalSendReport = prefs.getBoolPref("sendReport");
+    let originalEmailMe = prefs.getBoolPref("emailMe");
+    let originalIncludeURL = prefs.getBoolPref("includeURL");
+    let originalEmail = prefs.getCharPref("email");
+
+    let tab = gBrowser.getTabForBrowser(browser);
+    yield BrowserTestUtils.crashBrowser(browser);
+    let doc = browser.contentDocument;
+
+    // Since about:tabcrashed will run in the parent process, we can safely
+    // manipulate its DOM nodes directly
+    let comments = doc.getElementById("comments");
+    let email = doc.getElementById("email");
+    let emailMe = doc.getElementById("emailMe");
+    let includeURL = doc.getElementById("includeURL");
+
+    if (fieldValues.hasOwnProperty("comments")) {
+      comments.value = fieldValues.comments;
+    }
+
+    if (fieldValues.hasOwnProperty("email")) {
+      email.value = fieldValues.email;
+    }
+
+    if (fieldValues.hasOwnProperty("emailMe")) {
+      emailMe.checked = fieldValues.emailMe;
+    }
+
+    if (fieldValues.hasOwnProperty("includeURL")) {
+      includeURL.checked = fieldValues.includeURL;
+    }
+
+    let crashReport = promiseCrashReport(expectedExtra);
+    let restoreTab = browser.contentDocument.getElementById("restoreTab");
+    restoreTab.click();
+    yield BrowserTestUtils.waitForEvent(tab, "SSTabRestored");
+    yield crashReport;
+
+    // Submitting the crash report may have set some prefs regarding how to
+    // send tab crash reports. Let's reset them for the next test.
+    prefs.setBoolPref("sendReport", originalSendReport);
+    prefs.setBoolPref("emailMe", originalEmailMe);
+    prefs.setBoolPref("includeURL", originalIncludeURL);
+    prefs.setCharPref("email", originalEmail);
+  });
+}
+
+/**
+ * Tests what we send with the crash report by default. By default, we do not
+ * send any comments, the URL of the crashing page, or the email address of
+ * the user.
+ */
+add_task(function* test_default() {
+  yield crashTabTestHelper({}, {
+    "Comments": null,
+    "URL": "",
+    "Email": null,
+  });
+});
+
+/**
+ * Test just sending a comment.
+ */
+add_task(function* test_just_a_comment() {
+  yield crashTabTestHelper({
+    comments: COMMENTS,
+  }, {
+    "Comments": COMMENTS,
+    "URL": "",
+    "Email": null,
+  });
+});
+
+/**
+ * Test that we don't send email if emailMe is unchecked
+ */
+add_task(function* test_no_email() {
+  yield crashTabTestHelper({
+    email: EMAIL,
+    emailMe: false,
+  }, {
+    "Comments": null,
+    "URL": "",
+    "Email": null,
+  });
+});
+
+/**
+ * Test that we can send an email address if emailMe is checked
+ */
+add_task(function* test_yes_email() {
+  yield crashTabTestHelper({
+    email: EMAIL,
+    emailMe: true,
+  }, {
+    "Comments": null,
+    "URL": "",
+    "Email": EMAIL,
+  });
+});
+
+/**
+ * Test that we will send the URL of the page if includeURL is checked.
+ */
+add_task(function* test_send_URL() {
+  yield crashTabTestHelper({
+    includeURL: true,
+  }, {
+    "Comments": null,
+    "URL": PAGE,
+    "Email": null,
+  });
+});
+
+/**
+ * Test that we can send comments, the email address, and the URL
+ */
+add_task(function* test_send_all() {
+  yield crashTabTestHelper({
+    includeURL: true,
+    emailMe: true,
+    email: EMAIL,
+    comments: COMMENTS,
+  }, {
+    "Comments": COMMENTS,
+    "URL": PAGE,
+    "Email": EMAIL,
+  });
+});
+
--- a/browser/base/content/test/general/browser_backButtonFitts.js
+++ b/browser/base/content/test/general/browser_backButtonFitts.js
@@ -1,42 +1,42 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-add_task(function* () {
-  let firstLocation = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
-  yield BrowserTestUtils.openNewForegroundTab(gBrowser, firstLocation);
-
-  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
-    // Push the state before maximizing the window and clicking below.
-    content.history.pushState("page2", "page2", "page2");
-
-    // While in the child process, add a listener for the popstate event here. This
-    // event will fire when the mouse click happens.
-    content.addEventListener("popstate", function onPopState() {
-      content.removeEventListener("popstate", onPopState, false);
-      sendAsyncMessage("Test:PopStateOccurred", { location: content.document.location.href });
-    }, false);
-  });
-
-  window.maximize();
-
-  // Find where the nav-bar is vertically.
-  var navBar = document.getElementById("nav-bar");
-  var boundingRect = navBar.getBoundingClientRect();
-  var yPixel = boundingRect.top + Math.floor(boundingRect.height / 2);
-  var xPixel = 0; // Use the first pixel of the screen since it is maximized.
-
-  let resultLocation = yield new Promise(resolve => {
-    messageManager.addMessageListener("Test:PopStateOccurred", function statePopped(message) {
-      messageManager.removeMessageListener("Test:PopStateOccurred", statePopped);
-      resolve(message.data.location);
-    });
-
-    EventUtils.synthesizeMouseAtPoint(xPixel, yPixel, {}, window);
-  });
-
-  is(resultLocation, firstLocation, "Clicking the first pixel should have navigated back.");
-  window.restore();
-
-  gBrowser.removeCurrentTab();
-});
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+add_task(function* () {
+  let firstLocation = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, firstLocation);
+
+  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+    // Push the state before maximizing the window and clicking below.
+    content.history.pushState("page2", "page2", "page2");
+
+    // While in the child process, add a listener for the popstate event here. This
+    // event will fire when the mouse click happens.
+    content.addEventListener("popstate", function onPopState() {
+      content.removeEventListener("popstate", onPopState, false);
+      sendAsyncMessage("Test:PopStateOccurred", { location: content.document.location.href });
+    }, false);
+  });
+
+  window.maximize();
+
+  // Find where the nav-bar is vertically.
+  var navBar = document.getElementById("nav-bar");
+  var boundingRect = navBar.getBoundingClientRect();
+  var yPixel = boundingRect.top + Math.floor(boundingRect.height / 2);
+  var xPixel = 0; // Use the first pixel of the screen since it is maximized.
+
+  let resultLocation = yield new Promise(resolve => {
+    messageManager.addMessageListener("Test:PopStateOccurred", function statePopped(message) {
+      messageManager.removeMessageListener("Test:PopStateOccurred", statePopped);
+      resolve(message.data.location);
+    });
+
+    EventUtils.synthesizeMouseAtPoint(xPixel, yPixel, {}, window);
+  });
+
+  is(resultLocation, firstLocation, "Clicking the first pixel should have navigated back.");
+  window.restore();
+
+  gBrowser.removeCurrentTab();
+});
--- a/browser/base/content/test/general/browser_bookmark_popup.js
+++ b/browser/base/content/test/general/browser_bookmark_popup.js
@@ -1,257 +1,257 @@
-/* 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/. */
-
-"use strict";
-
-/**
- * Test opening and closing the bookmarks panel.
- */
-
-let bookmarkPanel = document.getElementById("editBookmarkPanel");
-let bookmarkStar = document.getElementById("bookmarks-menu-button");
-let bookmarkPanelTitle = document.getElementById("editBookmarkPanelTitle");
-
-StarUI._closePanelQuickForTesting = true;
-
-function* test_bookmarks_popup({isNewBookmark, popupShowFn, popupEditFn,
-                                shouldAutoClose, popupHideFn, isBookmarkRemoved}) {
-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home");
-  try {
-    if (!isNewBookmark) {
-      yield PlacesUtils.bookmarks.insert({
-        parentGuid: PlacesUtils.bookmarks.unfiledGuid,
-        url: "about:home",
-        title: "Home Page"
-      });
-    }
-
-    is(bookmarkStar.hasAttribute("starred"), !isNewBookmark,
-       "Page should only be starred prior to popupshown if editing bookmark");
-    is(bookmarkPanel.state, "closed", "Panel should be 'closed' to start test");
-    let shownPromise = promisePopupShown(bookmarkPanel);
-    yield popupShowFn(tab.linkedBrowser);
-    yield shownPromise;
-    is(bookmarkPanel.state, "open", "Panel should be 'open' after shownPromise is resolved");
-
-    if (popupEditFn) {
-      yield popupEditFn();
-    }
-    let bookmarks = [];
-    yield PlacesUtils.bookmarks.fetch({url: "about:home"}, bm => bookmarks.push(bm));
-    is(bookmarks.length, 1, "Only one bookmark should exist");
-    is(bookmarkStar.getAttribute("starred"), "true", "Page is starred");
-    is(bookmarkPanelTitle.value,
-      isNewBookmark ?
-        gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
-        gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle"),
-      "title should match isEditingBookmark state");
-
-    if (!shouldAutoClose) {
-      yield new Promise(resolve => setTimeout(resolve, 400));
-      is(bookmarkPanel.state, "open", "Panel should still be 'open' for non-autoclose");
-    }
-
-    let hiddenPromise = promisePopupHidden(bookmarkPanel);
-    if (popupHideFn) {
-      yield popupHideFn();
-    }
-    yield hiddenPromise;
-    is(bookmarkStar.hasAttribute("starred"), !isBookmarkRemoved,
-       "Page is starred after closing");
-  } finally {
-    let bookmark = yield PlacesUtils.bookmarks.fetch({url: "about:home"});
-    is(!!bookmark, !isBookmarkRemoved,
-       "bookmark should not be present if a panel action should've removed it");
-    if (bookmark) {
-      yield PlacesUtils.bookmarks.remove(bookmark);
-    }
-    gBrowser.removeTab(tab);
-  }
-}
-
-add_task(function* panel_shown_for_new_bookmarks_and_autocloses() {
-  yield test_bookmarks_popup({
-    isNewBookmark: true,
-    popupShowFn() {
-      bookmarkStar.click();
-    },
-    shouldAutoClose: true,
-    isBookmarkRemoved: false,
-  });
-});
-
-add_task(function* panel_shown_for_once_for_doubleclick_on_new_bookmark_star_and_autocloses() {
-  yield test_bookmarks_popup({
-    isNewBookmark: true,
-    popupShowFn() {
-      EventUtils.synthesizeMouse(bookmarkStar, 10, 10, { clickCount: 2 },
-                                 window);
-    },
-    shouldAutoClose: true,
-    isBookmarkRemoved: false,
-  });
-});
-
-add_task(function* panel_shown_once_for_slow_doubleclick_on_new_bookmark_star_and_autocloses() {
-  todo(false, "bug 1250267, may need to add some tracking state to " +
-              "browser-places.js for this.");
-  return;
-
-  yield test_bookmarks_popup({
-    isNewBookmark: true,
-    *popupShowFn() {
-      EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window);
-      yield new Promise(resolve => setTimeout(resolve, 300));
-      EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window);
-    },
-    shouldAutoClose: true,
-    isBookmarkRemoved: false,
-  });
-});
-
-add_task(function* panel_shown_for_keyboardshortcut_on_new_bookmark_star_and_autocloses() {
-  yield test_bookmarks_popup({
-    isNewBookmark: true,
-    popupShowFn() {
-      EventUtils.synthesizeKey("D", {accelKey: true}, window);
-    },
-    shouldAutoClose: true,
-    isBookmarkRemoved: false,
-  });
-});
-
-add_task(function* panel_shown_for_new_bookmarks_mousemove_mouseout() {
-  yield test_bookmarks_popup({
-    isNewBookmark: true,
-    popupShowFn() {
-      bookmarkStar.click();
-    },
-    *popupEditFn() {
-      let mouseMovePromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mousemove");
-      EventUtils.synthesizeMouseAtCenter(bookmarkPanel, {type: "mousemove"});
-      info("Waiting for mousemove event");
-      yield mouseMovePromise;
-      info("Got mousemove event");
-
-      yield new Promise(resolve => setTimeout(resolve, 400));
-      is(bookmarkPanel.state, "open", "Panel should still be open on mousemove");
-    },
-    *popupHideFn() {
-      let mouseOutPromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mouseout");
-      EventUtils.synthesizeMouse(bookmarkPanel, 0, 0, {type: "mouseout"});
-      EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
-      info("Waiting for mouseout event");
-      yield mouseOutPromise;
-      info("Got mouseout event, should autoclose now");
-    },
-    shouldAutoClose: false,
-    isBookmarkRemoved: false,
-  });
-});
-
-add_task(function* panel_shown_for_new_bookmark_no_autoclose_close_with_ESC() {
-  yield test_bookmarks_popup({
-    isNewBookmark: false,
-    popupShowFn() {
-      bookmarkStar.click();
-    },
-    shouldAutoClose: false,
-    popupHideFn() {
-      EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window);
-    },
-    isBookmarkRemoved: false,
-  });
-});
-
-add_task(function* panel_shown_for_editing_no_autoclose_close_with_ESC() {
-  yield test_bookmarks_popup({
-    isNewBookmark: false,
-    popupShowFn() {
-      bookmarkStar.click();
-    },
-    shouldAutoClose: false,
-    popupHideFn() {
-      EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window);
-    },
-    isBookmarkRemoved: false,
-  });
-});
-
-add_task(function* panel_shown_for_new_bookmark_keypress_no_autoclose() {
-  yield test_bookmarks_popup({
-    isNewBookmark: true,
-    popupShowFn() {
-      bookmarkStar.click();
-    },
-    popupEditFn() {
-      EventUtils.sendChar("VK_TAB", window);
-    },
-    shouldAutoClose: false,
-    popupHideFn() {
-      bookmarkPanel.hidePopup();
-    },
-    isBookmarkRemoved: false,
-  });
-});
-
-add_task(function* contextmenu_new_bookmark_keypress_no_autoclose() {
-  yield test_bookmarks_popup({
-    isNewBookmark: true,
-    *popupShowFn(browser) {
-      let contextMenu = document.getElementById("contentAreaContextMenu");
-      let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu,
-                                                          "popupshown");
-      let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu,
-                                                           "popuphidden");
-      yield BrowserTestUtils.synthesizeMouseAtCenter("body", {
-        type: "contextmenu",
-        button: 2
-      }, browser);
-      yield awaitPopupShown;
-      document.getElementById("context-bookmarkpage").click();
-      contextMenu.hidePopup();
-      yield awaitPopupHidden;
-    },
-    popupEditFn() {
-      EventUtils.sendChar("VK_TAB", window);
-    },
-    shouldAutoClose: false,
-    popupHideFn() {
-      bookmarkPanel.hidePopup();
-    },
-    isBookmarkRemoved: false,
-  });
-});
-
-add_task(function* bookmarks_menu_new_bookmark_remove_bookmark() {
-  yield test_bookmarks_popup({
-    isNewBookmark: true,
-    popupShowFn(browser) {
-      document.getElementById("menu_bookmarkThisPage").doCommand();
-    },
-    shouldAutoClose: true,
-    popupHideFn() {
-      document.getElementById("editBookmarkPanelRemoveButton").click();
-    },
-    isBookmarkRemoved: true,
-  });
-});
-
-add_task(function* ctrl_d_edit_bookmark_remove_bookmark() {
-  yield test_bookmarks_popup({
-    isNewBookmark: false,
-    popupShowFn(browser) {
-      EventUtils.synthesizeKey("D", {accelKey: true}, window);
-    },
-    shouldAutoClose: true,
-    popupHideFn() {
-      document.getElementById("editBookmarkPanelRemoveButton").click();
-    },
-    isBookmarkRemoved: true,
-  });
-});
-
-registerCleanupFunction(function() {
-  delete StarUI._closePanelQuickForTesting;
-})
+/* 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/. */
+
+"use strict";
+
+/**
+ * Test opening and closing the bookmarks panel.
+ */
+
+let bookmarkPanel = document.getElementById("editBookmarkPanel");
+let bookmarkStar = document.getElementById("bookmarks-menu-button");
+let bookmarkPanelTitle = document.getElementById("editBookmarkPanelTitle");
+
+StarUI._closePanelQuickForTesting = true;
+
+function* test_bookmarks_popup({isNewBookmark, popupShowFn, popupEditFn,
+                                shouldAutoClose, popupHideFn, isBookmarkRemoved}) {
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home");
+  try {
+    if (!isNewBookmark) {
+      yield PlacesUtils.bookmarks.insert({
+        parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+        url: "about:home",
+        title: "Home Page"
+      });
+    }
+
+    is(bookmarkStar.hasAttribute("starred"), !isNewBookmark,
+       "Page should only be starred prior to popupshown if editing bookmark");
+    is(bookmarkPanel.state, "closed", "Panel should be 'closed' to start test");
+    let shownPromise = promisePopupShown(bookmarkPanel);
+    yield popupShowFn(tab.linkedBrowser);
+    yield shownPromise;
+    is(bookmarkPanel.state, "open", "Panel should be 'open' after shownPromise is resolved");
+
+    if (popupEditFn) {
+      yield popupEditFn();
+    }
+    let bookmarks = [];
+    yield PlacesUtils.bookmarks.fetch({url: "about:home"}, bm => bookmarks.push(bm));
+    is(bookmarks.length, 1, "Only one bookmark should exist");
+    is(bookmarkStar.getAttribute("starred"), "true", "Page is starred");
+    is(bookmarkPanelTitle.value,
+      isNewBookmark ?
+        gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
+        gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle"),
+      "title should match isEditingBookmark state");
+
+    if (!shouldAutoClose) {
+      yield new Promise(resolve => setTimeout(resolve, 400));
+      is(bookmarkPanel.state, "open", "Panel should still be 'open' for non-autoclose");
+    }
+
+    let hiddenPromise = promisePopupHidden(bookmarkPanel);
+    if (popupHideFn) {
+      yield popupHideFn();
+    }
+    yield hiddenPromise;
+    is(bookmarkStar.hasAttribute("starred"), !isBookmarkRemoved,
+       "Page is starred after closing");
+  } finally {
+    let bookmark = yield PlacesUtils.bookmarks.fetch({url: "about:home"});
+    is(!!bookmark, !isBookmarkRemoved,
+       "bookmark should not be present if a panel action should've removed it");
+    if (bookmark) {
+      yield PlacesUtils.bookmarks.remove(bookmark);
+    }
+    gBrowser.removeTab(tab);
+  }
+}
+
+add_task(function* panel_shown_for_new_bookmarks_and_autocloses() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn() {
+      bookmarkStar.click();
+    },
+    shouldAutoClose: true,
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_once_for_doubleclick_on_new_bookmark_star_and_autocloses() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn() {
+      EventUtils.synthesizeMouse(bookmarkStar, 10, 10, { clickCount: 2 },
+                                 window);
+    },
+    shouldAutoClose: true,
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_once_for_slow_doubleclick_on_new_bookmark_star_and_autocloses() {
+  todo(false, "bug 1250267, may need to add some tracking state to " +
+              "browser-places.js for this.");
+  return;
+
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    *popupShowFn() {
+      EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window);
+      yield new Promise(resolve => setTimeout(resolve, 300));
+      EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window);
+    },
+    shouldAutoClose: true,
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_keyboardshortcut_on_new_bookmark_star_and_autocloses() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn() {
+      EventUtils.synthesizeKey("D", {accelKey: true}, window);
+    },
+    shouldAutoClose: true,
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_new_bookmarks_mousemove_mouseout() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn() {
+      bookmarkStar.click();
+    },
+    *popupEditFn() {
+      let mouseMovePromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mousemove");
+      EventUtils.synthesizeMouseAtCenter(bookmarkPanel, {type: "mousemove"});
+      info("Waiting for mousemove event");
+      yield mouseMovePromise;
+      info("Got mousemove event");
+
+      yield new Promise(resolve => setTimeout(resolve, 400));
+      is(bookmarkPanel.state, "open", "Panel should still be open on mousemove");
+    },
+    *popupHideFn() {
+      let mouseOutPromise = BrowserTestUtils.waitForEvent(bookmarkPanel, "mouseout");
+      EventUtils.synthesizeMouse(bookmarkPanel, 0, 0, {type: "mouseout"});
+      EventUtils.synthesizeMouseAtCenter(document.documentElement, {type: "mousemove"});
+      info("Waiting for mouseout event");
+      yield mouseOutPromise;
+      info("Got mouseout event, should autoclose now");
+    },
+    shouldAutoClose: false,
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_new_bookmark_no_autoclose_close_with_ESC() {
+  yield test_bookmarks_popup({
+    isNewBookmark: false,
+    popupShowFn() {
+      bookmarkStar.click();
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window);
+    },
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_editing_no_autoclose_close_with_ESC() {
+  yield test_bookmarks_popup({
+    isNewBookmark: false,
+    popupShowFn() {
+      bookmarkStar.click();
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window);
+    },
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* panel_shown_for_new_bookmark_keypress_no_autoclose() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn() {
+      bookmarkStar.click();
+    },
+    popupEditFn() {
+      EventUtils.sendChar("VK_TAB", window);
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      bookmarkPanel.hidePopup();
+    },
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* contextmenu_new_bookmark_keypress_no_autoclose() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    *popupShowFn(browser) {
+      let contextMenu = document.getElementById("contentAreaContextMenu");
+      let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu,
+                                                          "popupshown");
+      let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu,
+                                                           "popuphidden");
+      yield BrowserTestUtils.synthesizeMouseAtCenter("body", {
+        type: "contextmenu",
+        button: 2
+      }, browser);
+      yield awaitPopupShown;
+      document.getElementById("context-bookmarkpage").click();
+      contextMenu.hidePopup();
+      yield awaitPopupHidden;
+    },
+    popupEditFn() {
+      EventUtils.sendChar("VK_TAB", window);
+    },
+    shouldAutoClose: false,
+    popupHideFn() {
+      bookmarkPanel.hidePopup();
+    },
+    isBookmarkRemoved: false,
+  });
+});
+
+add_task(function* bookmarks_menu_new_bookmark_remove_bookmark() {
+  yield test_bookmarks_popup({
+    isNewBookmark: true,
+    popupShowFn(browser) {
+      document.getElementById("menu_bookmarkThisPage").doCommand();
+    },
+    shouldAutoClose: true,
+    popupHideFn() {
+      document.getElementById("editBookmarkPanelRemoveButton").click();
+    },
+    isBookmarkRemoved: true,
+  });
+});
+
+add_task(function* ctrl_d_edit_bookmark_remove_bookmark() {
+  yield test_bookmarks_popup({
+    isNewBookmark: false,
+    popupShowFn(browser) {
+      EventUtils.synthesizeKey("D", {accelKey: true}, window);
+    },
+    shouldAutoClose: true,
+    popupHideFn() {
+      document.getElementById("editBookmarkPanelRemoveButton").click();
+    },
+    isBookmarkRemoved: true,
+  });
+});
+
+registerCleanupFunction(function() {
+  delete StarUI._closePanelQuickForTesting;
+})
--- a/browser/base/content/test/general/browser_bug882977.js
+++ b/browser/base/content/test/general/browser_bug882977.js
@@ -1,36 +1,36 @@
-"use strict";
-
-/**
- * Tests that the identity-box shows the chromeUI styling
- * when viewing about:home in a new window.
- */
-add_task(function*(){
-  let homepage = "about:home";
-  yield SpecialPowers.pushPrefEnv({
-    "set": [
-      ["browser.startup.homepage", homepage],
-      ["browser.startup.page", 1],
-    ]
-  });
-
-  let win = OpenBrowserWindow();
-  yield BrowserTestUtils.waitForEvent(win, "load");
-
-  let browser = win.gBrowser.selectedBrowser;
-  // If we've finished loading about:home already, we can check
-  // right away - otherwise, we need to wait.
-  if (browser.contentDocument.readyState == "complete" &&
-      browser.currentURI.spec == homepage) {
-    checkIdentityMode(win);
-  } else {
-    yield BrowserTestUtils.browserLoaded(browser, false, homepage);
-    checkIdentityMode(win);
-  }
-
-  yield BrowserTestUtils.closeWindow(win);
-});
-
-function checkIdentityMode(win) {
-  let identityMode = win.document.getElementById("identity-box").className;
-  is(identityMode, "chromeUI", "Identity state should be chromeUI for about:home in a new window");
-}
+"use strict";
+
+/**
+ * Tests that the identity-box shows the chromeUI styling
+ * when viewing about:home in a new window.
+ */
+add_task(function*(){
+  let homepage = "about:home";
+  yield SpecialPowers.pushPrefEnv({
+    "set": [
+      ["browser.startup.homepage", homepage],
+      ["browser.startup.page", 1],
+    ]
+  });
+
+  let win = OpenBrowserWindow();
+  yield BrowserTestUtils.waitForEvent(win, "load");
+
+  let browser = win.gBrowser.selectedBrowser;
+  // If we've finished loading about:home already, we can check
+  // right away - otherwise, we need to wait.
+  if (browser.contentDocument.readyState == "complete" &&
+      browser.currentURI.spec == homepage) {
+    checkIdentityMode(win);
+  } else {
+    yield BrowserTestUtils.browserLoaded(browser, false, homepage);
+    checkIdentityMode(win);
+  }
+
+  yield BrowserTestUtils.closeWindow(win);
+});
+
+function checkIdentityMode(win) {
+  let identityMode = win.document.getElementById("identity-box").className;
+  is(identityMode, "chromeUI", "Identity state should be chromeUI for about:home in a new window");
+}
--- a/browser/base/content/test/general/browser_contextmenu.js
+++ b/browser/base/content/test/general/browser_contextmenu.js
@@ -1,879 +1,879 @@
-"use strict";
-
-let contextMenu;
-let LOGIN_FILL_ITEMS = [
-  "---", null,
-  "fill-login", null,
-    [
-      "fill-login-no-logins", false,
-      "---", null,
-      "fill-login-saved-passwords", true
-    ], null,
-];
-
-let hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled");
-let hasContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
-
-add_task(function* test_setup() {
-  const example_base = "http://example.com/browser/browser/base/content/test/general/";
-  const url = example_base + "subtst_contextmenu.html";
-  yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
-
-  const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/";
-  const contextmenu_common = chrome_base + "contextmenu_common.js";
-  Services.scriptloader.loadSubScript(contextmenu_common, this);
-
-  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
-    let doc = content.document;
-    let videoIframe = doc.querySelector("#test-video-in-iframe");
-    let video = videoIframe.contentDocument.querySelector("video");
-    let awaitPause = ContentTaskUtils.waitForEvent(video, "pause");
-    video.pause();
-    yield awaitPause;
-
-    let audioIframe = doc.querySelector("#test-audio-in-iframe");
-    // media documents always use a <video> tag.
-    let audio = audioIframe.contentDocument.querySelector("video");
-    awaitPause = ContentTaskUtils.waitForEvent(audio, "pause");
-    audio.pause();
-    yield awaitPause;
-  });
-});
-
-let plainTextItems;
-add_task(function* test_plaintext() {
-  plainTextItems = ["context-navigation",   null,
-                        ["context-back",         false,
-                         "context-forward",      false,
-                         "context-reload",       true,
-                         "context-bookmarkpage", true], null,
-                    "---",                  null,
-                    "context-savepage",     true,
-                    ...(hasPocket ? ["context-pocket", true] : []),
-                    "---",                  null,
-                    "context-viewbgimage",  false,
-                    "context-selectall",    true,
-                    "---",                  null,
-                    "context-viewsource",   true,
-                    "context-viewinfo",     true
-                   ];
-  yield test_contextmenu("#test-text", plainTextItems);
-});
-
-add_task(function* test_link() {
-  yield test_contextmenu("#test-link",
-    ["context-openlinkintab", true,
-     ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
-     // We need a blank entry here because the containers submenu is
-     // dynamically generated with no ids.
-     ...(hasContainers ? ["", null] : []),
-     "context-openlink",      true,
-     "context-openlinkprivate", true,
-     "---",                   null,
-     "context-bookmarklink",  true,
-     "context-savelink",      true,
-     ...(hasPocket ? ["context-savelinktopocket", true] : []),
-     "context-copylink",      true,
-     "context-searchselect",  true
-    ]
-  );
-});
-
-add_task(function* test_mailto() {
-  yield test_contextmenu("#test-mailto",
-    ["context-copyemail", true,
-     "context-searchselect", true
-    ]
-  );
-});
-
-add_task(function* test_image() {
-  yield test_contextmenu("#test-image",
-    ["context-viewimage",            true,
-     "context-copyimage-contents",   true,
-     "context-copyimage",            true,
-     "---",                          null,
-     "context-saveimage",            true,
-     "context-sendimage",            true,
-     "context-setDesktopBackground", true,
-     "context-viewimageinfo",        true
-    ]
-  );
-});
-
-add_task(function* test_canvas() {
-  yield test_contextmenu("#test-canvas",
-    ["context-viewimage",    true,
-     "context-saveimage",    true,
-     "context-selectall",    true
-    ]
-  );
-});
-
-add_task(function* test_video_ok() {
-  yield test_contextmenu("#test-video-ok",
-    ["context-media-play",         true,
-     "context-media-mute",         true,
-     "context-media-playbackrate", null,
-         ["context-media-playbackrate-050x", true,
-          "context-media-playbackrate-100x", true,
-          "context-media-playbackrate-125x", true,
-          "context-media-playbackrate-150x", true,
-          "context-media-playbackrate-200x", true], null,
-     "context-media-loop",         true,
-     "context-media-hidecontrols", true,
-     "context-video-fullscreen",   true,
-     "---",                        null,
-     "context-viewvideo",          true,
-     "context-copyvideourl",       true,
-     "---",                        null,
-     "context-savevideo",          true,
-     "context-video-saveimage",    true,
-     "context-sendvideo",          true,
-     "context-castvideo",          null,
-       [], null
-    ]
-  );
-});
-
-add_task(function* test_audio_in_video() {
-  yield test_contextmenu("#test-audio-in-video",
-    ["context-media-play",         true,
-     "context-media-mute",         true,
-     "context-media-playbackrate", null,
-         ["context-media-playbackrate-050x", true,
-          "context-media-playbackrate-100x", true,
-          "context-media-playbackrate-125x", true,
-          "context-media-playbackrate-150x", true,
-          "context-media-playbackrate-200x", true], null,
-     "context-media-loop",         true,
-     "context-media-showcontrols", true,
-     "---",                        null,
-     "context-copyaudiourl",       true,
-     "---",                        null,
-     "context-saveaudio",          true,
-     "context-sendaudio",          true
-    ]
-  );
-});
-
-add_task(function* test_video_bad() {
-  yield test_contextmenu("#test-video-bad",
-    ["context-media-play",         false,
-     "context-media-mute",         false,
-     "context-media-playbackrate", null,
-         ["context-media-playbackrate-050x", false,
-          "context-media-playbackrate-100x", false,
-          "context-media-playbackrate-125x", false,
-          "context-media-playbackrate-150x", false,
-          "context-media-playbackrate-200x", false], null,
-     "context-media-loop",         true,
-     "context-media-hidecontrols", false,
-     "context-video-fullscreen",   false,
-     "---",                        null,
-     "context-viewvideo",          true,
-     "context-copyvideourl",       true,
-     "---",                        null,
-     "context-savevideo",          true,
-     "context-video-saveimage",    false,
-     "context-sendvideo",          true,
-     "context-castvideo",          null,
-       [], null
-    ]
-  );
-});
-
-add_task(function* test_video_bad2() {
-  yield test_contextmenu("#test-video-bad2",
-    ["context-media-play",         false,
-     "context-media-mute",         false,
-     "context-media-playbackrate", null,
-         ["context-media-playbackrate-050x", false,
-          "context-media-playbackrate-100x", false,
-          "context-media-playbackrate-125x", false,
-          "context-media-playbackrate-150x", false,
-          "context-media-playbackrate-200x", false], null,
-     "context-media-loop",         true,
-     "context-media-hidecontrols", false,
-     "context-video-fullscreen",   false,
-     "---",                        null,
-     "context-viewvideo",          false,
-     "context-copyvideourl",       false,
-     "---",                        null,
-     "context-savevideo",          false,
-     "context-video-saveimage",    false,
-     "context-sendvideo",          false,
-     "context-castvideo",          null,
-       [], null
-    ]
-  );
-});
-
-add_task(function* test_iframe() {
-  yield test_contextmenu("#test-iframe",
-    ["context-navigation", null,
-         ["context-back",         false,
-          "context-forward",      false,
-          "context-reload",       true,
-          "context-bookmarkpage", true], null,
-     "---",                  null,
-     "context-savepage",     true,
-     ...(hasPocket ? ["context-pocket", true] : []),
-     "---",                  null,
-     "context-viewbgimage",  false,
-     "context-selectall",    true,
-     "frame",                null,
-         ["context-showonlythisframe", true,
-          "context-openframeintab",    true,
-          "context-openframe",         true,
-          "---",                       null,
-          "context-reloadframe",       true,
-          "---",                       null,
-          "context-bookmarkframe",     true,
-          "context-saveframe",         true,
-          "---",                       null,
-          "context-printframe",        true,
-          "---",                       null,
-          "context-viewframesource",   true,
-          "context-viewframeinfo",     true], null,
-     "---",                  null,
-     "context-viewsource",   true,
-     "context-viewinfo",     true
-    ]
-  );
-});
-
-add_task(function* test_video_in_iframe() {
-  yield test_contextmenu("#test-video-in-iframe",
-    ["context-media-play",         true,
-     "context-media-mute",         true,
-     "context-media-playbackrate", null,
-         ["context-media-playbackrate-050x", true,
-          "context-media-playbackrate-100x", true,
-          "context-media-playbackrate-125x", true,
-          "context-media-playbackrate-150x", true,
-          "context-media-playbackrate-200x", true], null,
-     "context-media-loop",         true,
-     "context-media-hidecontrols", true,
-     "context-video-fullscreen",   true,
-     "---",                        null,
-     "context-viewvideo",          true,
-     "context-copyvideourl",       true,
-     "---",                        null,
-     "context-savevideo",          true,
-     "context-video-saveimage",    true,
-     "context-sendvideo",          true,
-     "context-castvideo",          null,
-       [], null,
-     "frame",                null,
-         ["context-showonlythisframe", true,
-          "context-openframeintab",    true,
-          "context-openframe",         true,
-          "---",                       null,
-          "context-reloadframe",       true,
-          "---",                       null,
-          "context-bookmarkframe",     true,
-          "context-saveframe",         true,
-          "---",                       null,
-          "context-printframe",        true,
-          "---",                       null,
-          "context-viewframeinfo",     true], null]
-  );
-});
-
-add_task(function* test_audio_in_iframe() {
-  yield test_contextmenu("#test-audio-in-iframe",
-    ["context-media-play",         true,
-     "context-media-mute",         true,
-     "context-media-playbackrate", null,
-         ["context-media-playbackrate-050x", true,
-          "context-media-playbackrate-100x", true,
-          "context-media-playbackrate-125x", true,
-          "context-media-playbackrate-150x", true,
-          "context-media-playbackrate-200x", true], null,
-     "context-media-loop",         true,
-     "---",                        null,
-     "context-copyaudiourl",       true,
-     "---",                        null,
-     "context-saveaudio",          true,
-     "context-sendaudio",          true,
-     "frame",                null,
-         ["context-showonlythisframe", true,
-          "context-openframeintab",    true,
-          "context-openframe",         true,
-          "---",                       null,
-          "context-reloadframe",       true,
-          "---",                       null,
-          "context-bookmarkframe",     true,
-          "context-saveframe",         true,
-          "---",                       null,
-          "context-printframe",        true,
-          "---",                       null,
-          "context-viewframeinfo",     true], null]
-  );
-});
-
-add_task(function* test_image_in_iframe() {
-  yield test_contextmenu("#test-image-in-iframe",
-    ["context-viewimage",            true,
-     "context-copyimage-contents",   true,
-     "context-copyimage",            true,
-     "---",                          null,
-     "context-saveimage",            true,
-     "context-sendimage",            true,
-     "context-setDesktopBackground", true,
-     "context-viewimageinfo",        true,
-     "frame",                null,
-         ["context-showonlythisframe", true,
-          "context-openframeintab",    true,
-          "context-openframe",         true,
-          "---",                       null,
-          "context-reloadframe",       true,
-          "---",                       null,
-          "context-bookmarkframe",     true,
-          "context-saveframe",         true,
-          "---",                       null,
-          "context-printframe",        true,
-          "---",                       null,
-          "context-viewframeinfo",     true], null]
-  );
-});
-
-add_task(function* test_textarea() {
-  // Disabled since this is seeing spell-check-enabled
-  // instead of spell-add-dictionaries-main
-  todo(false, "spell checker tests are failing, bug 1246296");
-  return;
-
-  yield test_contextmenu("#test-textarea",
-    ["context-undo",                false,
-     "---",                         null,
-     "context-cut",                 true,
-     "context-copy",                true,
-     "context-paste",               null,
-     "context-delete",              false,
-     "---",                         null,
-     "context-selectall",           true,
-     "---",                         null,
-     "spell-add-dictionaries-main", true,
-    ],
-    {
-      skipFocusChange: true,
-    }
-  );
-});
-
-add_task(function* test_textarea_spellcheck() {
-  todo(false, "spell checker tests are failing, bug 1246296");
-  return;
-
-  yield test_contextmenu("#test-textarea",
-    ["*chubbiness",         true, // spelling suggestion
-     "spell-add-to-dictionary", true,
-     "---",                 null,
-     "context-undo",        false,
-     "---",                 null,
-     "context-cut",         true,
-     "context-copy",        true,
-     "context-paste",       null, // ignore clipboard state
-     "context-delete",      false,
-     "---",                 null,
-     "context-selectall",   true,
-     "---",                 null,
-     "spell-check-enabled", true,
-     "spell-dictionaries",  true,
-         ["spell-check-dictionary-en-US", true,
-          "---",                          null,
-          "spell-add-dictionaries",       true], null
-    ],
-    {
-      waitForSpellCheck: true,
-      offsetX: 6,
-      offsetY: 6,
-      postCheckContextMenuFn() {
-        document.getElementById("spell-add-to-dictionary").doCommand();
-      }
-    }
-  );
-});
-
-add_task(function* test_plaintext2() {
-  yield test_contextmenu("#test-text", plainTextItems);
-});
-
-add_task(function* test_undo_add_to_dictionary() {
-  todo(false, "spell checker tests are failing, bug 1246296");
-  return;
-
-  yield test_contextmenu("#test-textarea",
-    ["spell-undo-add-to-dictionary", true,
-     "---",                 null,
-     "context-undo",        false,
-     "---",                 null,
-     "context-cut",         true,
-     "context-copy",        true,
-     "context-paste",       null, // ignore clipboard state
-     "context-delete",      false,
-     "---",                 null,
-     "context-selectall",   true,
-     "---",                 null,
-     "spell-check-enabled", true,
-     "spell-dictionaries",  true,
-         ["spell-check-dictionary-en-US", true,
-          "---",                          null,
-          "spell-add-dictionaries",       true], null
-    ],
-    {
-      waitForSpellCheck: true,
-      postCheckContextMenuFn() {
-        document.getElementById("spell-undo-add-to-dictionary")
-                .doCommand();
-      }
-    }
-  );
-});
-
-add_task(function* test_contenteditable() {
-  todo(false, "spell checker tests are failing, bug 1246296");
-  return;
-
-  yield test_contextmenu("#test-contenteditable",
-    ["spell-no-suggestions", false,
-     "spell-add-to-dictionary", true,
-     "---",                 null,
-     "context-undo",        false,
-     "---",                 null,
-     "context-cut",         true,
-     "context-copy",        true,
-     "context-paste",       null, // ignore clipboard state
-     "context-delete",      false,
-     "---",                 null,
-     "context-selectall",   true,
-     "---",                 null,
-     "spell-check-enabled", true,
-     "spell-dictionaries",  true,
-         ["spell-check-dictionary-en-US", true,
-          "---",                          null,
-          "spell-add-dictionaries",       true], null
-    ],
-    {waitForSpellCheck: true}
-  );
-});
-
-add_task(function* test_copylinkcommand() {
-  yield test_contextmenu("#test-link", null, {
-    postCheckContextMenuFn: function*() {
-      document.commandDispatcher
-              .getControllerForCommand("cmd_copyLink")
-              .doCommand("cmd_copyLink");
-
-      // The easiest way to check the clipboard is to paste the contents
-      // into a textbox.
-      yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
-        let doc = content.document;
-        let input = doc.getElementById("test-input");
-        input.focus();
-        input.value = "";
-      });
-      document.commandDispatcher
-              .getControllerForCommand("cmd_paste")
-              .doCommand("cmd_paste");
-      yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
-        let doc = content.document;
-        let input = doc.getElementById("test-input");
-        Assert.equal(input.value, "http://mozilla.com/", "paste for command cmd_paste");
-      });
-    }
-  });
-});
-
-add_task(function* test_pagemenu() {
-  yield test_contextmenu("#test-pagemenu",
-    ["context-navigation",   null,
-         ["context-back",         false,
-          "context-forward",      false,
-          "context-reload",       true,
-          "context-bookmarkpage", true], null,
-     "---",                  null,
-     "+Plain item",          {type: "", icon: "", checked: false, disabled: false},
-     "+Disabled item",       {type: "", icon: "", checked: false, disabled: true},
-     "+Item w/ textContent", {type: "", icon: "", checked: false, disabled: false},
-     "---",                  null,
-     "+Checkbox",            {type: "checkbox", icon: "", checked: true, disabled: false},
-     "---",                  null,
-     "+Radio1",              {type: "checkbox", icon: "", checked: true, disabled: false},
-     "+Radio2",              {type: "checkbox", icon: "", checked: false, disabled: false},
-     "+Radio3",              {type: "checkbox", icon: "", checked: false, disabled: false},
-     "---",                  null,
-     "+Item w/ icon",        {type: "", icon: "favicon.ico", checked: false, disabled: false},
-     "+Item w/ bad icon",    {type: "", icon: "", checked: false, disabled: false},
-     "---",                  null,
-     "generated-submenu-1",  true,
-         ["+Radio1",             {type: "checkbox", icon: "", checked: false, disabled: false},
-          "+Radio2",             {type: "checkbox", icon: "", checked: true, disabled: false},
-          "+Radio3",             {type: "checkbox", icon: "", checked: false, disabled: false},
-          "---",                 null,
-          "+Checkbox",           {type: "checkbox", icon: "", checked: false, disabled: false}], null,
-     "---",                  null,
-     "context-savepage",     true,
-     ...(hasPocket ? ["context-pocket", true] : []),
-     "---",                  null,
-     "context-viewbgimage",  false,
-     "context-selectall",    true,
-     "---",                  null,
-     "context-viewsource",   true,
-     "context-viewinfo",     true
-    ],
-    {postCheckContextMenuFn: function*() {
-      let item = contextMenu.getElementsByAttribute("generateditemid", "1")[0];
-      ok(item, "Got generated XUL menu item");
-      item.doCommand();
-      yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
-        let pagemenu = content.document.getElementById("test-pagemenu");
-        Assert.ok(!pagemenu.hasAttribute("hopeless"), "attribute got removed");
-      });
-    }
-  });
-});
-
-add_task(function* test_dom_full_screen() {
-  yield test_contextmenu("#test-dom-full-screen",
-    ["context-navigation",           null,
-         ["context-back",            false,
-          "context-forward",         false,
-          "context-reload",          true,
-          "context-bookmarkpage",    true], null,
-     "---",                          null,
-     "context-leave-dom-fullscreen", true,
-     "---",                          null,
-     "context-savepage",             true,
-     ...(hasPocket ? ["context-pocket", true] : []),
-     "---",                          null,
-     "context-viewbgimage",          false,
-     "context-selectall",            true,
-     "---",                          null,
-     "context-viewsource",           true,
-     "context-viewinfo",             true
-    ],
-    {
-      shiftkey: true,
-      *preCheckContextMenuFn() {
-        yield pushPrefs(["full-screen-api.allow-trusted-requests-only", false],
-                        ["full-screen-api.transition-duration.enter", "0 0"],
-                        ["full-screen-api.transition-duration.leave", "0 0"])
-        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
-          let doc = content.document;
-          let win = doc.defaultView;
-          let full_screen_element = doc.getElementById("test-dom-full-screen");
-          let awaitFullScreenChange =
-            ContentTaskUtils.waitForEvent(win, "fullscreenchange");
-          full_screen_element.requestFullscreen();
-          yield awaitFullScreenChange;
-        });
-      },
-      *postCheckContextMenuFn() {
-        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
-          let win = content.document.defaultView;
-          let awaitFullScreenChange =
-            ContentTaskUtils.waitForEvent(win, "fullscreenchange");
-          content.document.exitFullscreen();
-          yield awaitFullScreenChange;
-        });
-      }
-    }
-  );
-});
-
-add_task(function* test_pagemenu2() {
-  yield test_contextmenu("#test-text",
-    ["context-navigation", null,
-         ["context-back",         false,
-          "context-forward",      false,
-          "context-reload",       true,
-          "context-bookmarkpage", true], null,
-     "---",                  null,
-     "context-savepage",     true,
-     ...(hasPocket ? ["context-pocket", true] : []),
-     "---",                  null,
-     "context-viewbgimage",  false,
-     "context-selectall",    true,
-     "---",                  null,
-     "context-viewsource",   true,
-     "context-viewinfo",     true
-    ],
-    {shiftkey: true}
-  );
-});
-
-add_task(function* test_select_text() {
-  yield test_contextmenu("#test-select-text",
-    ["context-copy",                        true,
-     "context-selectall",                   true,
-     "---",                                 null,
-     "context-searchselect",                true,
-     "context-viewpartialsource-selection", true
-    ],
-    {
-      offsetX: 6,
-      offsetY: 6,
-      *preCheckContextMenuFn() {
-        yield selectText("#test-select-text");
-      }
-    }
-  );
-});
-
-add_task(function* test_select_text_link() {
-  yield test_contextmenu("#test-select-text-link",
-    ["context-openlinkincurrent",           true,
-     "context-openlinkintab",               true,
-     ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
-     // We need a blank entry here because the containers submenu is
-     // dynamically generated with no ids.
-     ...(hasContainers ? ["", null] : []),
-     "context-openlink",                    true,
-     "context-openlinkprivate",             true,
-     "---",                                 null,
-     "context-bookmarklink",                true,
-     "context-savelink",                    true,
-     "context-copy",                        true,
-     "context-selectall",                   true,
-     "---",                                 null,
-     "context-searchselect",                true,
-     "context-viewpartialsource-selection", true
-    ],
-    {
-      offsetX: 6,
-      offsetY: 6,
-      *preCheckContextMenuFn() {
-        yield selectText("#test-select-text-link");
-      },
-      *postCheckContextMenuFn() {
-        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
-          let win = content.document.defaultView;
-          win.getSelection().removeAllRanges();
-        });
-      }
-    }
-  );
-});
-
-add_task(function* test_imagelink() {
-  yield test_contextmenu("#test-image-link",
-    ["context-openlinkintab", true,
-     ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
-     // We need a blank entry here because the containers submenu is
-     // dynamically generated with no ids.
-     ...(hasContainers ? ["", null] : []),
-     "context-openlink",      true,
-     "context-openlinkprivate", true,
-     "---",                   null,
-     "context-bookmarklink",  true,
-     "context-savelink",      true,
-     ...(hasPocket ? ["context-savelinktopocket", true] : []),
-     "context-copylink",      true,
-     "---",                   null,
-     "context-viewimage",            true,
-     "context-copyimage-contents",   true,
-     "context-copyimage",            true,
-     "---",                          null,
-     "context-saveimage",            true,
-     "context-sendimage",            true,
-     "context-setDesktopBackground", true,
-     "context-viewimageinfo",        true
-    ]
-  );
-});
-
-add_task(function* test_select_input_text() {
-  todo(false, "spell checker tests are failing, bug 1246296");
-  return;
-
-  yield test_contextmenu("#test-select-input-text",
-    ["context-undo",        false,
-     "---",                 null,
-     "context-cut",         true,
-     "context-copy",        true,
-     "context-paste",       null, // ignore clipboard state
-     "context-delete",      true,
-     "---",                 null,
-     "context-selectall",   true,
-     "context-searchselect",true,
-     "---",                 null,
-     "spell-check-enabled", true
-    ].concat(LOGIN_FILL_ITEMS),
-    {
-      *preCheckContextMenuFn() {
-        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
-          let doc = content.document;
-          let win = doc.defaultView;
-          win.getSelection().removeAllRanges();
-          let element = doc.querySelector("#test-select-input-text");
-          element.select();
-        });
-      }
-    }
-  );
-});
-
-add_task(function* test_select_input_text_password() {
-  todo(false, "spell checker tests are failing, bug 1246296");
-  return;
-
-  yield test_contextmenu("#test-select-input-text-type-password",
-    ["context-undo",        false,
-     "---",                 null,
-     "context-cut",         true,
-     "context-copy",        true,
-     "context-paste",       null, // ignore clipboard state
-     "context-delete",      true,
-     "---",                 null,
-     "context-selectall",   true,
-     "---",                 null,
-     "spell-check-enabled", true,
-     //spell checker is shown on input[type="password"] on this testcase
-     "spell-dictionaries",  true,
-         ["spell-check-dictionary-en-US", true,
-          "---",                          null,
-          "spell-add-dictionaries",       true], null
-    ].concat(LOGIN_FILL_ITEMS),
-    {
-      *preCheckContextMenuFn() {
-        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
-          let doc = content.document;
-          let win = doc.defaultView;
-          win.getSelection().removeAllRanges();
-          let element = doc.querySelector("#test-select-input-text-type-password");
-          element.select();
-        });
-      },
-      *postCheckContextMenuFn() {
-        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
-          let win = content.document.defaultView;
-          win.getSelection().removeAllRanges();
-        });
-      }
-    }
-  );
-});
-
-add_task(function* test_click_to_play_blocked_plugin() {
-  yield test_contextmenu("#test-plugin",
-    ["context-navigation", null,
-         ["context-back",         false,
-          "context-forward",      false,
-          "context-reload",       true,
-          "context-bookmarkpage", true], null,
-     "---",                  null,
-     "context-ctp-play",     true,
-     "context-ctp-hide",     true,
-     "---",                  null,
-     "context-savepage",     true,
-     ...(hasPocket ? ["context-pocket", true] : []),
-     "---",                  null,
-     "context-viewbgimage",  false,
-     "context-selectall",    true,
-     "---",                  null,
-     "context-viewsource",   true,
-     "context-viewinfo",     true
-    ],
-    {
-      preCheckContextMenuFn: function*() {
-        pushPrefs(["plugins.click_to_play", true]);
-        setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
-      },
-      postCheckContextMenuFn: function*() {
-        getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
-      }
-    }
-  );
-});
-
-add_task(function* test_longdesc() {
-  yield test_contextmenu("#test-longdesc",
-    ["context-viewimage",            true,
-     "context-copyimage-contents",   true,
-     "context-copyimage",            true,
-     "---",                          null,
-     "context-saveimage",            true,
-     "context-sendimage",            true,
-     "context-setDesktopBackground", true,
-     "context-viewimageinfo",        true,
-     "context-viewimagedesc",        true
-    ]
-  );
-});
-
-add_task(function* test_srcdoc() {
-  yield test_contextmenu("#test-srcdoc",
-    ["context-navigation", null,
-         ["context-back",         false,
-          "context-forward",      false,
-          "context-reload",       true,
-          "context-bookmarkpage", true], null,
-     "---",                  null,
-     "context-savepage",     true,
-     ...(hasPocket ? ["context-pocket", true] : []),
-     "---",                  null,
-     "context-viewbgimage",  false,
-     "context-selectall",    true,
-     "frame",                null,
-         ["context-reloadframe",       true,
-          "---",                       null,
-          "context-saveframe",         true,
-          "---",                       null,
-          "context-printframe",        true,
-          "---",                       null,
-          "context-viewframesource",   true,
-          "context-viewframeinfo",     true], null,
-     "---",                  null,
-     "context-viewsource",   true,
-     "context-viewinfo",     true
-    ]
-  );
-});
-
-add_task(function* test_input_spell_false() {
-  todo(false, "spell checker tests are failing, bug 1246296");
-  return;
-
-  yield test_contextmenu("#test-contenteditable-spellcheck-false",
-    ["context-undo",        false,
-     "---",                 null,
-     "context-cut",         true,
-     "context-copy",        true,
-     "context-paste",       null, // ignore clipboard state
-     "context-delete",      false,
-     "---",                 null,
-     "context-selectall",   true,
-     "---",                 null,
-     "spell-add-dictionaries-main",  true,
-    ]
-  );
-});
-
-add_task(function* test_cleanup() {
-  gBrowser.removeCurrentTab();
-});
-
-/**
- * Selects the text of the element that matches the provided `selector`
- *
- * @param {String} selector
- *        A selector passed to querySelector to find
- *        the element that will be referenced.
- */
-function* selectText(selector) {
-  yield ContentTask.spawn(gBrowser.selectedBrowser, selector, function*(selector) {
-    info(`Selecting text of ${selector}`);
-    let doc = content.document;
-    let win = doc.defaultView;
-    win.getSelection().removeAllRanges();
-    let div = doc.createRange();
-    let element = doc.querySelector(selector);
-    Assert.ok(element, "Found element to select text from");
-    div.setStartBefore(element);
-    div.setEndAfter(element);
-    win.getSelection().addRange(div);
-  });
-}
+"use strict";
+
+let contextMenu;
+let LOGIN_FILL_ITEMS = [
+  "---", null,
+  "fill-login", null,
+    [
+      "fill-login-no-logins", false,
+      "---", null,
+      "fill-login-saved-passwords", true
+    ], null,
+];
+
+let hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled");
+let hasContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
+
+add_task(function* test_setup() {
+  const example_base = "http://example.com/browser/browser/base/content/test/general/";
+  const url = example_base + "subtst_contextmenu.html";
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+
+  const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/";
+  const contextmenu_common = chrome_base + "contextmenu_common.js";
+  Services.scriptloader.loadSubScript(contextmenu_common, this);
+
+  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+    let doc = content.document;
+    let videoIframe = doc.querySelector("#test-video-in-iframe");
+    let video = videoIframe.contentDocument.querySelector("video");
+    let awaitPause = ContentTaskUtils.waitForEvent(video, "pause");
+    video.pause();
+    yield awaitPause;
+
+    let audioIframe = doc.querySelector("#test-audio-in-iframe");
+    // media documents always use a <video> tag.
+    let audio = audioIframe.contentDocument.querySelector("video");
+    awaitPause = ContentTaskUtils.waitForEvent(audio, "pause");
+    audio.pause();
+    yield awaitPause;
+  });
+});
+
+let plainTextItems;
+add_task(function* test_plaintext() {
+  plainTextItems = ["context-navigation",   null,
+                        ["context-back",         false,
+                         "context-forward",      false,
+                         "context-reload",       true,
+                         "context-bookmarkpage", true], null,
+                    "---",                  null,
+                    "context-savepage",     true,
+                    ...(hasPocket ? ["context-pocket", true] : []),
+                    "---",                  null,
+                    "context-viewbgimage",  false,
+                    "context-selectall",    true,
+                    "---",                  null,
+                    "context-viewsource",   true,
+                    "context-viewinfo",     true
+                   ];
+  yield test_contextmenu("#test-text", plainTextItems);
+});
+
+add_task(function* test_link() {
+  yield test_contextmenu("#test-link",
+    ["context-openlinkintab", true,
+     ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
+     // We need a blank entry here because the containers submenu is
+     // dynamically generated with no ids.
+     ...(hasContainers ? ["", null] : []),
+     "context-openlink",      true,
+     "context-openlinkprivate", true,
+     "---",                   null,
+     "context-bookmarklink",  true,
+     "context-savelink",      true,
+     ...(hasPocket ? ["context-savelinktopocket", true] : []),
+     "context-copylink",      true,
+     "context-searchselect",  true
+    ]
+  );
+});
+
+add_task(function* test_mailto() {
+  yield test_contextmenu("#test-mailto",
+    ["context-copyemail", true,
+     "context-searchselect", true
+    ]
+  );
+});
+
+add_task(function* test_image() {
+  yield test_contextmenu("#test-image",
+    ["context-viewimage",            true,
+     "context-copyimage-contents",   true,
+     "context-copyimage",            true,
+     "---",                          null,
+     "context-saveimage",            true,
+     "context-sendimage",            true,
+     "context-setDesktopBackground", true,
+     "context-viewimageinfo",        true
+    ]
+  );
+});
+
+add_task(function* test_canvas() {
+  yield test_contextmenu("#test-canvas",
+    ["context-viewimage",    true,
+     "context-saveimage",    true,
+     "context-selectall",    true
+    ]
+  );
+});
+
+add_task(function* test_video_ok() {
+  yield test_contextmenu("#test-video-ok",
+    ["context-media-play",         true,
+     "context-media-mute",         true,
+     "context-media-playbackrate", null,
+         ["context-media-playbackrate-050x", true,
+          "context-media-playbackrate-100x", true,
+          "context-media-playbackrate-125x", true,
+          "context-media-playbackrate-150x", true,
+          "context-media-playbackrate-200x", true], null,
+     "context-media-loop",         true,
+     "context-media-hidecontrols", true,
+     "context-video-fullscreen",   true,
+     "---",                        null,
+     "context-viewvideo",          true,
+     "context-copyvideourl",       true,
+     "---",                        null,
+     "context-savevideo",          true,
+     "context-video-saveimage",    true,
+     "context-sendvideo",          true,
+     "context-castvideo",          null,
+       [], null
+    ]
+  );
+});
+
+add_task(function* test_audio_in_video() {
+  yield test_contextmenu("#test-audio-in-video",
+    ["context-media-play",         true,
+     "context-media-mute",         true,
+     "context-media-playbackrate", null,
+         ["context-media-playbackrate-050x", true,
+          "context-media-playbackrate-100x", true,
+          "context-media-playbackrate-125x", true,
+          "context-media-playbackrate-150x", true,
+          "context-media-playbackrate-200x", true], null,
+     "context-media-loop",         true,
+     "context-media-showcontrols", true,
+     "---",                        null,
+     "context-copyaudiourl",       true,
+     "---",                        null,
+     "context-saveaudio",          true,
+     "context-sendaudio",          true
+    ]
+  );
+});
+
+add_task(function* test_video_bad() {
+  yield test_contextmenu("#test-video-bad",
+    ["context-media-play",         false,
+     "context-media-mute",         false,
+     "context-media-playbackrate", null,
+         ["context-media-playbackrate-050x", false,
+          "context-media-playbackrate-100x", false,
+          "context-media-playbackrate-125x", false,
+          "context-media-playbackrate-150x", false,
+          "context-media-playbackrate-200x", false], null,
+     "context-media-loop",         true,
+     "context-media-hidecontrols", false,
+     "context-video-fullscreen",   false,
+     "---",                        null,
+     "context-viewvideo",          true,
+     "context-copyvideourl",       true,
+     "---",                        null,
+     "context-savevideo",          true,
+     "context-video-saveimage",    false,
+     "context-sendvideo",          true,
+     "context-castvideo",          null,
+       [], null
+    ]
+  );
+});
+
+add_task(function* test_video_bad2() {
+  yield test_contextmenu("#test-video-bad2",
+    ["context-media-play",         false,
+     "context-media-mute",         false,
+     "context-media-playbackrate", null,
+         ["context-media-playbackrate-050x", false,
+          "context-media-playbackrate-100x", false,
+          "context-media-playbackrate-125x", false,
+          "context-media-playbackrate-150x", false,
+          "context-media-playbackrate-200x", false], null,
+     "context-media-loop",         true,
+     "context-media-hidecontrols", false,
+     "context-video-fullscreen",   false,
+     "---",                        null,
+     "context-viewvideo",          false,
+     "context-copyvideourl",       false,
+     "---",                        null,
+     "context-savevideo",          false,
+     "context-video-saveimage",    false,
+     "context-sendvideo",          false,
+     "context-castvideo",          null,
+       [], null
+    ]
+  );
+});
+
+add_task(function* test_iframe() {
+  yield test_contextmenu("#test-iframe",
+    ["context-navigation", null,
+         ["context-back",         false,
+          "context-forward",      false,
+          "context-reload",       true,
+          "context-bookmarkpage", true], null,
+     "---",                  null,
+     "context-savepage",     true,
+     ...(hasPocket ? ["context-pocket", true] : []),
+     "---",                  null,
+     "context-viewbgimage",  false,
+     "context-selectall",    true,
+     "frame",                null,
+         ["context-showonlythisframe", true,
+          "context-openframeintab",    true,
+          "context-openframe",         true,
+          "---",                       null,
+          "context-reloadframe",       true,
+          "---",                       null,
+          "context-bookmarkframe",     true,
+          "context-saveframe",         true,
+          "---",                       null,
+          "context-printframe",        true,
+          "---",                       null,
+          "context-viewframesource",   true,
+          "context-viewframeinfo",     true], null,
+     "---",                  null,
+     "context-viewsource",   true,
+     "context-viewinfo",     true
+    ]
+  );
+});
+
+add_task(function* test_video_in_iframe() {
+  yield test_contextmenu("#test-video-in-iframe",
+    ["context-media-play",         true,
+     "context-media-mute",         true,
+     "context-media-playbackrate", null,
+         ["context-media-playbackrate-050x", true,
+          "context-media-playbackrate-100x", true,
+          "context-media-playbackrate-125x", true,
+          "context-media-playbackrate-150x", true,
+          "context-media-playbackrate-200x", true], null,
+     "context-media-loop",         true,
+     "context-media-hidecontrols", true,
+     "context-video-fullscreen",   true,
+     "---",                        null,
+     "context-viewvideo",          true,
+     "context-copyvideourl",       true,
+     "---",                        null,
+     "context-savevideo",          true,
+     "context-video-saveimage",    true,
+     "context-sendvideo",          true,
+     "context-castvideo",          null,
+       [], null,
+     "frame",                null,
+         ["context-showonlythisframe", true,
+          "context-openframeintab",    true,
+          "context-openframe",         true,
+          "---",                       null,
+          "context-reloadframe",       true,
+          "---",                       null,
+          "context-bookmarkframe",     true,
+          "context-saveframe",         true,
+          "---",                       null,
+          "context-printframe",        true,
+          "---",                       null,
+          "context-viewframeinfo",     true], null]
+  );
+});
+
+add_task(function* test_audio_in_iframe() {
+  yield test_contextmenu("#test-audio-in-iframe",
+    ["context-media-play",         true,
+     "context-media-mute",         true,
+     "context-media-playbackrate", null,
+         ["context-media-playbackrate-050x", true,
+          "context-media-playbackrate-100x", true,
+          "context-media-playbackrate-125x", true,
+          "context-media-playbackrate-150x", true,
+          "context-media-playbackrate-200x", true], null,
+     "context-media-loop",         true,
+     "---",                        null,
+     "context-copyaudiourl",       true,
+     "---",                        null,
+     "context-saveaudio",          true,
+     "context-sendaudio",          true,
+     "frame",                null,
+         ["context-showonlythisframe", true,
+          "context-openframeintab",    true,
+          "context-openframe",         true,
+          "---",                       null,
+          "context-reloadframe",       true,
+          "---",                       null,
+          "context-bookmarkframe",     true,
+          "context-saveframe",         true,
+          "---",                       null,
+          "context-printframe",        true,
+          "---",                       null,
+          "context-viewframeinfo",     true], null]
+  );
+});
+
+add_task(function* test_image_in_iframe() {
+  yield test_contextmenu("#test-image-in-iframe",
+    ["context-viewimage",            true,
+     "context-copyimage-contents",   true,
+     "context-copyimage",            true,
+     "---",                          null,
+     "context-saveimage",            true,
+     "context-sendimage",            true,
+     "context-setDesktopBackground", true,
+     "context-viewimageinfo",        true,
+     "frame",                null,
+         ["context-showonlythisframe", true,
+          "context-openframeintab",    true,
+          "context-openframe",         true,
+          "---",                       null,
+          "context-reloadframe",       true,
+          "---",                       null,
+          "context-bookmarkframe",     true,
+          "context-saveframe",         true,
+          "---",                       null,
+          "context-printframe",        true,
+          "---",                       null,
+          "context-viewframeinfo",     true], null]
+  );
+});
+
+add_task(function* test_textarea() {
+  // Disabled since this is seeing spell-check-enabled
+  // instead of spell-add-dictionaries-main
+  todo(false, "spell checker tests are failing, bug 1246296");
+  return;
+
+  yield test_contextmenu("#test-textarea",
+    ["context-undo",                false,
+     "---",                         null,
+     "context-cut",                 true,
+     "context-copy",                true,
+     "context-paste",               null,
+     "context-delete",              false,
+     "---",                         null,
+     "context-selectall",           true,
+     "---",                         null,
+     "spell-add-dictionaries-main", true,
+    ],
+    {
+      skipFocusChange: true,
+    }
+  );
+});
+
+add_task(function* test_textarea_spellcheck() {
+  todo(false, "spell checker tests are failing, bug 1246296");
+  return;
+
+  yield test_contextmenu("#test-textarea",
+    ["*chubbiness",         true, // spelling suggestion
+     "spell-add-to-dictionary", true,
+     "---",                 null,
+     "context-undo",        false,
+     "---",                 null,
+     "context-cut",         true,
+     "context-copy",        true,
+     "context-paste",       null, // ignore clipboard state
+     "context-delete",      false,
+     "---",                 null,
+     "context-selectall",   true,
+     "---",                 null,
+     "spell-check-enabled", true,
+     "spell-dictionaries",  true,
+         ["spell-check-dictionary-en-US", true,
+          "---",                          null,
+          "spell-add-dictionaries",       true], null
+    ],
+    {
+      waitForSpellCheck: true,
+      offsetX: 6,
+      offsetY: 6,
+      postCheckContextMenuFn() {
+        document.getElementById("spell-add-to-dictionary").doCommand();
+      }
+    }
+  );
+});
+
+add_task(function* test_plaintext2() {
+  yield test_contextmenu("#test-text", plainTextItems);
+});
+
+add_task(function* test_undo_add_to_dictionary() {
+  todo(false, "spell checker tests are failing, bug 1246296");
+  return;
+
+  yield test_contextmenu("#test-textarea",
+    ["spell-undo-add-to-dictionary", true,
+     "---",                 null,
+     "context-undo",        false,
+     "---",                 null,
+     "context-cut",         true,
+     "context-copy",        true,
+     "context-paste",       null, // ignore clipboard state
+     "context-delete",      false,
+     "---",                 null,
+     "context-selectall",   true,
+     "---",                 null,
+     "spell-check-enabled", true,
+     "spell-dictionaries",  true,
+         ["spell-check-dictionary-en-US", true,
+          "---",                          null,
+          "spell-add-dictionaries",       true], null
+    ],
+    {
+      waitForSpellCheck: true,
+      postCheckContextMenuFn() {
+        document.getElementById("spell-undo-add-to-dictionary")
+                .doCommand();
+      }
+    }
+  );
+});
+
+add_task(function* test_contenteditable() {
+  todo(false, "spell checker tests are failing, bug 1246296");
+  return;
+
+  yield test_contextmenu("#test-contenteditable",
+    ["spell-no-suggestions", false,
+     "spell-add-to-dictionary", true,
+     "---",                 null,
+     "context-undo",        false,
+     "---",                 null,
+     "context-cut",         true,
+     "context-copy",        true,
+     "context-paste",       null, // ignore clipboard state
+     "context-delete",      false,
+     "---",                 null,
+     "context-selectall",   true,
+     "---",                 null,
+     "spell-check-enabled", true,
+     "spell-dictionaries",  true,
+         ["spell-check-dictionary-en-US", true,
+          "---",                          null,
+          "spell-add-dictionaries",       true], null
+    ],
+    {waitForSpellCheck: true}
+  );
+});
+
+add_task(function* test_copylinkcommand() {
+  yield test_contextmenu("#test-link", null, {
+    postCheckContextMenuFn: function*() {
+      document.commandDispatcher
+              .getControllerForCommand("cmd_copyLink")
+              .doCommand("cmd_copyLink");
+
+      // The easiest way to check the clipboard is to paste the contents
+      // into a textbox.
+      yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+        let doc = content.document;
+        let input = doc.getElementById("test-input");
+        input.focus();
+        input.value = "";
+      });
+      document.commandDispatcher
+              .getControllerForCommand("cmd_paste")
+              .doCommand("cmd_paste");
+      yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+        let doc = content.document;
+        let input = doc.getElementById("test-input");
+        Assert.equal(input.value, "http://mozilla.com/", "paste for command cmd_paste");
+      });
+    }
+  });
+});
+
+add_task(function* test_pagemenu() {
+  yield test_contextmenu("#test-pagemenu",
+    ["context-navigation",   null,
+         ["context-back",         false,
+          "context-forward",      false,
+          "context-reload",       true,
+          "context-bookmarkpage", true], null,
+     "---",                  null,
+     "+Plain item",          {type: "", icon: "", checked: false, disabled: false},
+     "+Disabled item",       {type: "", icon: "", checked: false, disabled: true},
+     "+Item w/ textContent", {type: "", icon: "", checked: false, disabled: false},
+     "---",                  null,
+     "+Checkbox",            {type: "checkbox", icon: "", checked: true, disabled: false},
+     "---",                  null,
+     "+Radio1",              {type: "checkbox", icon: "", checked: true, disabled: false},
+     "+Radio2",              {type: "checkbox", icon: "", checked: false, disabled: false},
+     "+Radio3",              {type: "checkbox", icon: "", checked: false, disabled: false},
+     "---",                  null,
+     "+Item w/ icon",        {type: "", icon: "favicon.ico", checked: false, disabled: false},
+     "+Item w/ bad icon",    {type: "", icon: "", checked: false, disabled: false},
+     "---",                  null,
+     "generated-submenu-1",  true,
+         ["+Radio1",             {type: "checkbox", icon: "", checked: false, disabled: false},
+          "+Radio2",             {type: "checkbox", icon: "", checked: true, disabled: false},
+          "+Radio3",             {type: "checkbox", icon: "", checked: false, disabled: false},
+          "---",                 null,
+          "+Checkbox",           {type: "checkbox", icon: "", checked: false, disabled: false}], null,
+     "---",                  null,
+     "context-savepage",     true,
+     ...(hasPocket ? ["context-pocket", true] : []),
+     "---",                  null,
+     "context-viewbgimage",  false,
+     "context-selectall",    true,
+     "---",                  null,
+     "context-viewsource",   true,
+     "context-viewinfo",     true
+    ],
+    {postCheckContextMenuFn: function*() {
+      let item = contextMenu.getElementsByAttribute("generateditemid", "1")[0];
+      ok(item, "Got generated XUL menu item");
+      item.doCommand();
+      yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+        let pagemenu = content.document.getElementById("test-pagemenu");
+        Assert.ok(!pagemenu.hasAttribute("hopeless"), "attribute got removed");
+      });
+    }
+  });
+});
+
+add_task(function* test_dom_full_screen() {
+  yield test_contextmenu("#test-dom-full-screen",
+    ["context-navigation",           null,
+         ["context-back",            false,
+          "context-forward",         false,
+          "context-reload",          true,
+          "context-bookmarkpage",    true], null,
+     "---",                          null,
+     "context-leave-dom-fullscreen", true,
+     "---",                          null,
+     "context-savepage",             true,
+     ...(hasPocket ? ["context-pocket", true] : []),
+     "---",                          null,
+     "context-viewbgimage",          false,
+     "context-selectall",            true,
+     "---",                          null,
+     "context-viewsource",           true,
+     "context-viewinfo",             true
+    ],
+    {
+      shiftkey: true,
+      *preCheckContextMenuFn() {
+        yield pushPrefs(["full-screen-api.allow-trusted-requests-only", false],
+                        ["full-screen-api.transition-duration.enter", "0 0"],
+                        ["full-screen-api.transition-duration.leave", "0 0"])
+        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+          let doc = content.document;
+          let win = doc.defaultView;
+          let full_screen_element = doc.getElementById("test-dom-full-screen");
+          let awaitFullScreenChange =
+            ContentTaskUtils.waitForEvent(win, "fullscreenchange");
+          full_screen_element.requestFullscreen();
+          yield awaitFullScreenChange;
+        });
+      },
+      *postCheckContextMenuFn() {
+        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+          let win = content.document.defaultView;
+          let awaitFullScreenChange =
+            ContentTaskUtils.waitForEvent(win, "fullscreenchange");
+          content.document.exitFullscreen();
+          yield awaitFullScreenChange;
+        });
+      }
+    }
+  );
+});
+
+add_task(function* test_pagemenu2() {
+  yield test_contextmenu("#test-text",
+    ["context-navigation", null,
+         ["context-back",         false,
+          "context-forward",      false,
+          "context-reload",       true,
+          "context-bookmarkpage", true], null,
+     "---",                  null,
+     "context-savepage",     true,
+     ...(hasPocket ? ["context-pocket", true] : []),
+     "---",                  null,
+     "context-viewbgimage",  false,
+     "context-selectall",    true,
+     "---",                  null,
+     "context-viewsource",   true,
+     "context-viewinfo",     true
+    ],
+    {shiftkey: true}
+  );
+});
+
+add_task(function* test_select_text() {
+  yield test_contextmenu("#test-select-text",
+    ["context-copy",                        true,
+     "context-selectall",                   true,
+     "---",                                 null,
+     "context-searchselect",                true,
+     "context-viewpartialsource-selection", true
+    ],
+    {
+      offsetX: 6,
+      offsetY: 6,
+      *preCheckContextMenuFn() {
+        yield selectText("#test-select-text");
+      }
+    }
+  );
+});
+
+add_task(function* test_select_text_link() {
+  yield test_contextmenu("#test-select-text-link",
+    ["context-openlinkincurrent",           true,
+     "context-openlinkintab",               true,
+     ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
+     // We need a blank entry here because the containers submenu is
+     // dynamically generated with no ids.
+     ...(hasContainers ? ["", null] : []),
+     "context-openlink",                    true,
+     "context-openlinkprivate",             true,
+     "---",                                 null,
+     "context-bookmarklink",                true,
+     "context-savelink",                    true,
+     "context-copy",                        true,
+     "context-selectall",                   true,
+     "---",                                 null,
+     "context-searchselect",                true,
+     "context-viewpartialsource-selection", true
+    ],
+    {
+      offsetX: 6,
+      offsetY: 6,
+      *preCheckContextMenuFn() {
+        yield selectText("#test-select-text-link");
+      },
+      *postCheckContextMenuFn() {
+        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+          let win = content.document.defaultView;
+          win.getSelection().removeAllRanges();
+        });
+      }
+    }
+  );
+});
+
+add_task(function* test_imagelink() {
+  yield test_contextmenu("#test-image-link",
+    ["context-openlinkintab", true,
+     ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
+     // We need a blank entry here because the containers submenu is
+     // dynamically generated with no ids.
+     ...(hasContainers ? ["", null] : []),
+     "context-openlink",      true,
+     "context-openlinkprivate", true,
+     "---",                   null,
+     "context-bookmarklink",  true,
+     "context-savelink",      true,
+     ...(hasPocket ? ["context-savelinktopocket", true] : []),
+     "context-copylink",      true,
+     "---",                   null,
+     "context-viewimage",            true,
+     "context-copyimage-contents",   true,
+     "context-copyimage",            true,
+     "---",                          null,
+     "context-saveimage",            true,
+     "context-sendimage",            true,
+     "context-setDesktopBackground", true,
+     "context-viewimageinfo",        true
+    ]
+  );
+});
+
+add_task(function* test_select_input_text() {
+  todo(false, "spell checker tests are failing, bug 1246296");
+  return;
+
+  yield test_contextmenu("#test-select-input-text",
+    ["context-undo",        false,
+     "---",                 null,
+     "context-cut",         true,
+     "context-copy",        true,
+     "context-paste",       null, // ignore clipboard state
+     "context-delete",      true,
+     "---",                 null,
+     "context-selectall",   true,
+     "context-searchselect",true,
+     "---",                 null,
+     "spell-check-enabled", true
+    ].concat(LOGIN_FILL_ITEMS),
+    {
+      *preCheckContextMenuFn() {
+        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+          let doc = content.document;
+          let win = doc.defaultView;
+          win.getSelection().removeAllRanges();
+          let element = doc.querySelector("#test-select-input-text");
+          element.select();
+        });
+      }
+    }
+  );
+});
+
+add_task(function* test_select_input_text_password() {
+  todo(false, "spell checker tests are failing, bug 1246296");
+  return;
+
+  yield test_contextmenu("#test-select-input-text-type-password",
+    ["context-undo",        false,
+     "---",                 null,
+     "context-cut",         true,
+     "context-copy",        true,
+     "context-paste",       null, // ignore clipboard state
+     "context-delete",      true,
+     "---",                 null,
+     "context-selectall",   true,
+     "---",                 null,
+     "spell-check-enabled", true,
+     //spell checker is shown on input[type="password"] on this testcase
+     "spell-dictionaries",  true,
+         ["spell-check-dictionary-en-US", true,
+          "---",                          null,
+          "spell-add-dictionaries",       true], null
+    ].concat(LOGIN_FILL_ITEMS),
+    {
+      *preCheckContextMenuFn() {
+        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+          let doc = content.document;
+          let win = doc.defaultView;
+          win.getSelection().removeAllRanges();
+          let element = doc.querySelector("#test-select-input-text-type-password");
+          element.select();
+        });
+      },
+      *postCheckContextMenuFn() {
+        yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+          let win = content.document.defaultView;
+          win.getSelection().removeAllRanges();
+        });
+      }
+    }
+  );
+});
+
+add_task(function* test_click_to_play_blocked_plugin() {
+  yield test_contextmenu("#test-plugin",
+    ["context-navigation", null,
+         ["context-back",         false,
+          "context-forward",      false,
+          "context-reload",       true,
+          "context-bookmarkpage", true], null,
+     "---",                  null,
+     "context-ctp-play",     true,
+     "context-ctp-hide",     true,
+     "---",                  null,
+     "context-savepage",     true,
+     ...(hasPocket ? ["context-pocket", true] : []),
+     "---",                  null,
+     "context-viewbgimage",  false,
+     "context-selectall",    true,
+     "---",                  null,
+     "context-viewsource",   true,
+     "context-viewinfo",     true
+    ],
+    {
+      preCheckContextMenuFn: function*() {
+        pushPrefs(["plugins.click_to_play", true]);
+        setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+      },
+      postCheckContextMenuFn: function*() {
+        getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+      }
+    }
+  );
+});
+
+add_task(function* test_longdesc() {
+  yield test_contextmenu("#test-longdesc",
+    ["context-viewimage",            true,
+     "context-copyimage-contents",   true,
+     "context-copyimage",            true,
+     "---",                          null,
+     "context-saveimage",            true,
+     "context-sendimage",            true,
+     "context-setDesktopBackground", true,
+     "context-viewimageinfo",        true,
+     "context-viewimagedesc",        true
+    ]
+  );
+});
+
+add_task(function* test_srcdoc() {
+  yield test_contextmenu("#test-srcdoc",
+    ["context-navigation", null,
+         ["context-back",         false,
+          "context-forward",      false,
+          "context-reload",       true,
+          "context-bookmarkpage", true], null,
+     "---",                  null,
+     "context-savepage",     true,
+     ...(hasPocket ? ["context-pocket", true] : []),
+     "---",                  null,
+     "context-viewbgimage",  false,
+     "context-selectall",    true,
+     "frame",                null,
+         ["context-reloadframe",       true,
+          "---",                       null,
+          "context-saveframe",         true,
+          "---",                       null,
+          "context-printframe",        true,
+          "---",                       null,
+          "context-viewframesource",   true,
+          "context-viewframeinfo",     true], null,
+     "---",                  null,
+     "context-viewsource",   true,
+     "context-viewinfo",     true
+    ]
+  );
+});
+
+add_task(function* test_input_spell_false() {
+  todo(false, "spell checker tests are failing, bug 1246296");
+  return;
+
+  yield test_contextmenu("#test-contenteditable-spellcheck-false",
+    ["context-undo",        false,
+     "---",                 null,
+     "context-cut",         true,
+     "context-copy",        true,
+     "context-paste",       null, // ignore clipboard state
+     "context-delete",      false,
+     "---",                 null,
+     "context-selectall",   true,
+     "---",                 null,
+     "spell-add-dictionaries-main",  true,
+    ]
+  );
+});
+
+add_task(function* test_cleanup() {
+  gBrowser.removeCurrentTab();
+});
+
+/**
+ * Selects the text of the element that matches the provided `selector`
+ *
+ * @param {String} selector
+ *        A selector passed to querySelector to find
+ *        the element that will be referenced.
+ */
+function* selectText(selector) {
+  yield ContentTask.spawn(gBrowser.selectedBrowser, selector, function*(selector) {
+    info(`Selecting text of ${selector}`);
+    let doc = content.document;
+    let win = doc.defaultView;
+    win.getSelection().removeAllRanges();
+    let div = doc.createRange();
+    let element = doc.querySelector(selector);
+    Assert.ok(element, "Found element to select text from");
+    div.setStartBefore(element);
+    div.setEndAfter(element);
+    win.getSelection().addRange(div);
+  });
+}
--- a/browser/base/content/test/general/browser_decoderDoctor.js
+++ b/browser/base/content/test/general/browser_decoderDoctor.js
@@ -1,97 +1,97 @@
-"use strict";
-
-function* test_decoder_doctor_notification(type, notificationMessage, options) {
-  yield BrowserTestUtils.withNewTab({ gBrowser }, function*(browser) {
-    let awaitNotificationBar =
-      BrowserTestUtils.waitForNotificationBar(gBrowser, browser, "decoder-doctor-notification");
-
-    yield ContentTask.spawn(browser, type, function*(type) {
-      Services.obs.notifyObservers(content.window,
-                                   "decoder-doctor-notification",
-                                   JSON.stringify({type: type,
-                                                   isSolved: false,
-                                                   decoderDoctorReportId: "test",
-                                                   formats: "test"}));
-    });
-
-    let notification;
-    try {
-      notification = yield awaitNotificationBar;
-    } catch (ex) {
-      ok(false, ex);
-      return;
-    }
-    ok(notification, "Got decoder-doctor-notification notification");
-
-    is(notification.getAttribute("label"), notificationMessage,
-      "notification message should match expectation");
-    let button = notification.childNodes[0];
-    if (options && options.noLearnMoreButton) {
-      ok(!button, "There should not be a Learn More button");
-      return;
-    }
-
-    is(button.getAttribute("label"), gNavigatorBundle.getString("decoder.noCodecs.button"),
-      "notification button should be 'Learn more'");
-    is(button.getAttribute("accesskey"), gNavigatorBundle.getString("decoder.noCodecs.accesskey"),
-      "notification button should have accesskey");
-
-    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
-    let url = baseURL + "fix-video-audio-problems-firefox-windows";
-    let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, url);
-    button.click();
-    let sumoTab = yield awaitNewTab;
-    yield BrowserTestUtils.removeTab(sumoTab);
-  });
-}
-
-add_task(function* test_adobe_cdm_not_found() {
-  // This is only sent on Windows.
-  if (AppConstants.platform != "win") {
-    return;
-  }
-
-  let message;
-  if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
-    message = gNavigatorBundle.getFormattedString("emeNotifications.drmContentDisabled.message", [""]);
-  } else {
-    message = gNavigatorBundle.getString("decoder.noCodecs.message");
-  }
-
-  yield test_decoder_doctor_notification("adobe-cdm-not-found", message);
-});
-
-add_task(function* test_adobe_cdm_not_activated() {
-  // This is only sent on Windows.
-  if (AppConstants.platform != "win") {
-    return;
-  }
-
-  let message;
-  if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
-    message = gNavigatorBundle.getString("decoder.noCodecsXP.message");
-  } else {
-    message = gNavigatorBundle.getString("decoder.noCodecs.message");
-  }
-
-  yield test_decoder_doctor_notification("adobe-cdm-not-activated", message);
-});
-
-add_task(function* test_platform_decoder_not_found() {
-  // Not sent on Windows XP.
-  if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
-    return;
-  }
-
-  let message;
-  let isLinux = AppConstants.platform == "linux";
-  if (isLinux) {
-    message = gNavigatorBundle.getString("decoder.noCodecsLinux.message");
-  } else {
-    message = gNavigatorBundle.getString("decoder.noHWAcceleration.message");
-  }
-
-  yield test_decoder_doctor_notification("platform-decoder-not-found",
-                                         message,
-                                         {noLearnMoreButton: isLinux});
-});
+"use strict";
+
+function* test_decoder_doctor_notification(type, notificationMessage, options) {
+  yield BrowserTestUtils.withNewTab({ gBrowser }, function*(browser) {
+    let awaitNotificationBar =
+      BrowserTestUtils.waitForNotificationBar(gBrowser, browser, "decoder-doctor-notification");
+
+    yield ContentTask.spawn(browser, type, function*(type) {
+      Services.obs.notifyObservers(content.window,
+                                   "decoder-doctor-notification",
+                                   JSON.stringify({type: type,
+                                                   isSolved: false,
+                                                   decoderDoctorReportId: "test",
+                                                   formats: "test"}));
+    });
+
+    let notification;
+    try {
+      notification = yield awaitNotificationBar;
+    } catch (ex) {
+      ok(false, ex);
+      return;
+    }
+    ok(notification, "Got decoder-doctor-notification notification");
+
+    is(notification.getAttribute("label"), notificationMessage,
+      "notification message should match expectation");
+    let button = notification.childNodes[0];
+    if (options && options.noLearnMoreButton) {
+      ok(!button, "There should not be a Learn More button");
+      return;
+    }
+
+    is(button.getAttribute("label"), gNavigatorBundle.getString("decoder.noCodecs.button"),
+      "notification button should be 'Learn more'");
+    is(button.getAttribute("accesskey"), gNavigatorBundle.getString("decoder.noCodecs.accesskey"),
+      "notification button should have accesskey");
+
+    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
+    let url = baseURL + "fix-video-audio-problems-firefox-windows";
+    let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, url);
+    button.click();
+    let sumoTab = yield awaitNewTab;
+    yield BrowserTestUtils.removeTab(sumoTab);
+  });
+}
+
+add_task(function* test_adobe_cdm_not_found() {
+  // This is only sent on Windows.
+  if (AppConstants.platform != "win") {
+    return;
+  }
+
+  let message;
+  if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
+    message = gNavigatorBundle.getFormattedString("emeNotifications.drmContentDisabled.message", [""]);
+  } else {
+    message = gNavigatorBundle.getString("decoder.noCodecs.message");
+  }
+
+  yield test_decoder_doctor_notification("adobe-cdm-not-found", message);
+});
+
+add_task(function* test_adobe_cdm_not_activated() {
+  // This is only sent on Windows.
+  if (AppConstants.platform != "win") {
+    return;
+  }
+
+  let message;
+  if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
+    message = gNavigatorBundle.getString("decoder.noCodecsXP.message");
+  } else {
+    message = gNavigatorBundle.getString("decoder.noCodecs.message");
+  }
+
+  yield test_decoder_doctor_notification("adobe-cdm-not-activated", message);
+});
+
+add_task(function* test_platform_decoder_not_found() {
+  // Not sent on Windows XP.
+  if (AppConstants.isPlatformAndVersionAtMost("win", "5.9")) {
+    return;
+  }
+
+  let message;
+  let isLinux = AppConstants.platform == "linux";
+  if (isLinux) {
+    message = gNavigatorBundle.getString("decoder.noCodecsLinux.message");
+  } else {
+    message = gNavigatorBundle.getString("decoder.noHWAcceleration.message");
+  }
+
+  yield test_decoder_doctor_notification("platform-decoder-not-found",
+                                         message,
+                                         {noLearnMoreButton: isLinux});
+});
--- a/browser/base/content/test/general/browser_favicon_change.js
+++ b/browser/base/content/test/general/browser_favicon_change.js
@@ -1,40 +1,40 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-const TEST_URL = "http://mochi.test:8888/browser/browser/base/content/test/general/file_favicon_change.html"
-
-add_task(function*() {
-  let extraTab = gBrowser.selectedTab = gBrowser.addTab();
-  let tabLoaded = promiseTabLoaded(extraTab);
-  extraTab.linkedBrowser.loadURI(TEST_URL);
-  let expectedFavicon = "http://example.org/one-icon";
-  let haveChanged = new Promise.defer();
-  let observer = new MutationObserver(function(mutations) {
-    for (let mut of mutations) {
-      if (mut.attributeName != "image") {
-        continue;
-      }
-      let imageVal = extraTab.getAttribute("image").replace(/#.*$/, "");
-      if (!imageVal) {
-        // The value gets removed because it doesn't load.
-        continue;
-      }
-      is(imageVal, expectedFavicon, "Favicon image should correspond to expected image.");
-      haveChanged.resolve();
-    }
-  });
-  observer.observe(extraTab, {attributes: true});
-  yield tabLoaded;
-  yield haveChanged.promise;
-  haveChanged = new Promise.defer();
-  expectedFavicon = "http://example.org/other-icon";
-  let contentWin = extraTab.linkedBrowser.contentWindow;
-  let ev = new contentWin.CustomEvent("PleaseChangeFavicon", {});
-  contentWin.dispatchEvent(ev);
-  yield haveChanged.promise;
-  observer.disconnect();
-  gBrowser.removeTab(extraTab);
-});
-
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URL = "http://mochi.test:8888/browser/browser/base/content/test/general/file_favicon_change.html"
+
+add_task(function*() {
+  let extraTab = gBrowser.selectedTab = gBrowser.addTab();
+  let tabLoaded = promiseTabLoaded(extraTab);
+  extraTab.linkedBrowser.loadURI(TEST_URL);
+  let expectedFavicon = "http://example.org/one-icon";
+  let haveChanged = new Promise.defer();
+  let observer = new MutationObserver(function(mutations) {
+    for (let mut of mutations) {
+      if (mut.attributeName != "image") {
+        continue;
+      }
+      let imageVal = extraTab.getAttribute("image").replace(/#.*$/, "");
+      if (!imageVal) {
+        // The value gets removed because it doesn't load.
+        continue;
+      }
+      is(imageVal, expectedFavicon, "Favicon image should correspond to expected image.");
+      haveChanged.resolve();
+    }
+  });
+  observer.observe(extraTab, {attributes: true});
+  yield tabLoaded;
+  yield haveChanged.promise;
+  haveChanged = new Promise.defer();
+  expectedFavicon = "http://example.org/other-icon";
+  let contentWin = extraTab.linkedBrowser.contentWindow;
+  let ev = new contentWin.CustomEvent("PleaseChangeFavicon", {});
+  contentWin.dispatchEvent(ev);
+  yield haveChanged.promise;
+  observer.disconnect();
+  gBrowser.removeTab(extraTab);
+});
+
--- a/browser/base/content/test/general/browser_menuButtonFitts.js
+++ b/browser/base/content/test/general/browser_menuButtonFitts.js
@@ -1,32 +1,32 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-function test () {
-  waitForExplicitFinish();
-  window.maximize();
-
-  // Find where the nav-bar is vertically.
-  var navBar = document.getElementById("nav-bar");
-  var boundingRect = navBar.getBoundingClientRect();
-  var yPixel = boundingRect.top + Math.floor(boundingRect.height / 2);
-  var xPixel = boundingRect.width - 1; // Use the last pixel of the screen since it is maximized.
-
-  function onPopupHidden() {
-    PanelUI.panel.removeEventListener("popuphidden", onPopupHidden);
-    window.restore();
-    finish();
-  }
-  function onPopupShown() {
-    PanelUI.panel.removeEventListener("popupshown", onPopupShown);
-    ok(true, "Clicking at the far edge of the window opened the menu popup.");
-    PanelUI.panel.addEventListener("popuphidden", onPopupHidden);
-    PanelUI.hide();
-  }
-  registerCleanupFunction(function() {
-    PanelUI.panel.removeEventListener("popupshown", onPopupShown);
-    PanelUI.panel.removeEventListener("popuphidden", onPopupHidden);
-  });
-  PanelUI.panel.addEventListener("popupshown", onPopupShown);
-  EventUtils.synthesizeMouseAtPoint(xPixel, yPixel, {}, window);
-}
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test () {
+  waitForExplicitFinish();
+  window.maximize();
+
+  // Find where the nav-bar is vertically.
+  var navBar = document.getElementById("nav-bar");
+  var boundingRect = navBar.getBoundingClientRect();
+  var yPixel = boundingRect.top + Math.floor(boundingRect.height / 2);
+  var xPixel = boundingRect.width - 1; // Use the last pixel of the screen since it is maximized.
+
+  function onPopupHidden() {
+    PanelUI.panel.removeEventListener("popuphidden", onPopupHidden);
+    window.restore();
+    finish();
+  }
+  function onPopupShown() {
+    PanelUI.panel.removeEventListener("popupshown", onPopupShown);
+    ok(true, "Clicking at the far edge of the window opened the menu popup.");
+    PanelUI.panel.addEventListener("popuphidden", onPopupHidden);
+    PanelUI.hide();
+  }
+  registerCleanupFunction(function() {
+    PanelUI.panel.removeEventListener("popupshown", onPopupShown);
+    PanelUI.panel.removeEventListener("popuphidden", onPopupHidden);
+  });
+  PanelUI.panel.addEventListener("popupshown", onPopupShown);
+  EventUtils.synthesizeMouseAtPoint(xPixel, yPixel, {}, window);
+}
--- a/browser/base/content/test/general/browser_remoteTroubleshoot.js
+++ b/browser/base/content/test/general/browser_remoteTroubleshoot.js
@@ -1,93 +1,93 @@
-/* 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/. */
-
-var {WebChannel} = Cu.import("resource://gre/modules/WebChannel.jsm", {});
-
-const TEST_URL_TAIL = "example.com/browser/browser/base/content/test/general/test_remoteTroubleshoot.html"
-const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URL_TAIL, null, null);
-const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URL_TAIL, null, null);
-const TEST_URI_GOOD_OBJECT = Services.io.newURI("https://" + TEST_URL_TAIL + "?object", null, null);
-
-// Creates a one-shot web-channel for the test data to be sent back from the test page.
-function promiseChannelResponse(channelID, originOrPermission) {
-  return new Promise((resolve, reject) => {
-    let channel = new WebChannel(channelID, originOrPermission);
-    channel.listen((id, data, target) => {
-      channel.stopListening();
-      resolve(data);
-    });
-  });
-};
-
-// Loads the specified URI in a new tab and waits for it to send us data on our
-// test web-channel and resolves with that data.
-function promiseNewChannelResponse(uri) {
-  let channelPromise = promiseChannelResponse("test-remote-troubleshooting-backchannel",
-                                              uri);
-  let tab = gBrowser.loadOneTab(uri.spec, { inBackground: false });
-  return promiseTabLoaded(tab).then(
-    () => channelPromise
-  ).then(data => {
-    gBrowser.removeTab(tab);
-    return data;
-  });
-}
-
-add_task(function*() {
-  // We haven't set a permission yet - so even the "good" URI should fail.
-  let got = yield promiseNewChannelResponse(TEST_URI_GOOD);
-  // Should have no data.
-  Assert.ok(got.message === undefined, "should have failed to get any data");
-
-  // Add a permission manager entry for our URI.
-  Services.perms.add(TEST_URI_GOOD,
-                     "remote-troubleshooting",
-                     Services.perms.ALLOW_ACTION);
-  registerCleanupFunction(() => {
-    Services.perms.remove(TEST_URI_GOOD, "remote-troubleshooting");
-  });
-
-  // Try again - now we are expecting a response with the actual data.
-  got = yield promiseNewChannelResponse(TEST_URI_GOOD);
-
-  // Check some keys we expect to always get.
-  Assert.ok(got.message.extensions, "should have extensions");
-  Assert.ok(got.message.graphics, "should have graphics");
-
-  // Check we have channel and build ID info:
-  Assert.equal(got.message.application.buildID, Services.appinfo.appBuildID,
-               "should have correct build ID");
-
-  let updateChannel = null;
-  try {
-    updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
-  } catch (ex) {}
-  if (!updateChannel) {
-    Assert.ok(!('updateChannel' in got.message.application),
-                "should not have update channel where not available.");
-  } else {
-    Assert.equal(got.message.application.updateChannel, updateChannel,
-                 "should have correct update channel.");
-  }
-
-
-  // And check some keys we know we decline to return.
-  Assert.ok(!got.message.modifiedPreferences, "should not have a modifiedPreferences key");
-  Assert.ok(!got.message.crashes, "should not have crash info");
-
-  // Now a http:// URI - should get nothing even with the permission setup.
-  got = yield promiseNewChannelResponse(TEST_URI_BAD);
-  Assert.ok(got.message === undefined, "should have failed to get any data");
-
-  // Check that the page can send an object as well if it's in the whitelist
-  let webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
-  let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
-  let newWhitelist = origWhitelist + " https://example.com";
-  Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref(webchannelWhitelistPref);
-  });
-  got = yield promiseNewChannelResponse(TEST_URI_GOOD_OBJECT);
-  Assert.ok(got.message, "should have gotten some data back");
-});
+/* 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/. */
+
+var {WebChannel} = Cu.import("resource://gre/modules/WebChannel.jsm", {});
+
+const TEST_URL_TAIL = "example.com/browser/browser/base/content/test/general/test_remoteTroubleshoot.html"
+const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URL_TAIL, null, null);
+const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URL_TAIL, null, null);
+const TEST_URI_GOOD_OBJECT = Services.io.newURI("https://" + TEST_URL_TAIL + "?object", null, null);
+
+// Creates a one-shot web-channel for the test data to be sent back from the test page.
+function promiseChannelResponse(channelID, originOrPermission) {
+  return new Promise((resolve, reject) => {
+    let channel = new WebChannel(channelID, originOrPermission);
+    channel.listen((id, data, target) => {
+      channel.stopListening();
+      resolve(data);
+    });
+  });
+};
+
+// Loads the specified URI in a new tab and waits for it to send us data on our
+// test web-channel and resolves with that data.
+function promiseNewChannelResponse(uri) {
+  let channelPromise = promiseChannelResponse("test-remote-troubleshooting-backchannel",
+                                              uri);
+  let tab = gBrowser.loadOneTab(uri.spec, { inBackground: false });
+  return promiseTabLoaded(tab).then(
+    () => channelPromise
+  ).then(data => {
+    gBrowser.removeTab(tab);
+    return data;
+  });
+}
+
+add_task(function*() {
+  // We haven't set a permission yet - so even the "good" URI should fail.
+  let got = yield promiseNewChannelResponse(TEST_URI_GOOD);
+  // Should have no data.
+  Assert.ok(got.message === undefined, "should have failed to get any data");
+
+  // Add a permission manager entry for our URI.
+  Services.perms.add(TEST_URI_GOOD,
+                     "remote-troubleshooting",
+                     Services.perms.ALLOW_ACTION);
+  registerCleanupFunction(() => {
+    Services.perms.remove(TEST_URI_GOOD, "remote-troubleshooting");
+  });
+
+  // Try again - now we are expecting a response with the actual data.
+  got = yield promiseNewChannelResponse(TEST_URI_GOOD);
+
+  // Check some keys we expect to always get.
+  Assert.ok(got.message.extensions, "should have extensions");
+  Assert.ok(got.message.graphics, "should have graphics");
+
+  // Check we have channel and build ID info:
+  Assert.equal(got.message.application.buildID, Services.appinfo.appBuildID,
+               "should have correct build ID");
+
+  let updateChannel = null;
+  try {
+    updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
+  } catch (ex) {}
+  if (!updateChannel) {
+    Assert.ok(!('updateChannel' in got.message.application),
+                "should not have update channel where not available.");
+  } else {
+    Assert.equal(got.message.application.updateChannel, updateChannel,
+                 "should have correct update channel.");
+  }
+
+
+  // And check some keys we know we decline to return.
+  Assert.ok(!got.message.modifiedPreferences, "should not have a modifiedPreferences key");
+  Assert.ok(!got.message.crashes, "should not have crash info");
+
+  // Now a http:// URI - should get nothing even with the permission setup.
+  got = yield promiseNewChannelResponse(TEST_URI_BAD);
+  Assert.ok(got.message === undefined, "should have failed to get any data");
+
+  // Check that the page can send an object as well if it's in the whitelist
+  let webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist";
+  let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref);
+  let newWhitelist = origWhitelist + " https://example.com";
+  Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist);
+  registerCleanupFunction(() => {
+    Services.prefs.clearUserPref(webchannelWhitelistPref);
+  });
+  got = yield promiseNewChannelResponse(TEST_URI_GOOD_OBJECT);
+  Assert.ok(got.message, "should have gotten some data back");
+});
--- a/browser/base/content/test/general/browser_tabs_close_beforeunload.js
+++ b/browser/base/content/test/general/browser_tabs_close_beforeunload.js
@@ -1,49 +1,49 @@
-"use strict";
-
-SimpleTest.requestCompleteLog();
-
-SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
-
-const FIRST_TAB = getRootDirectory(gTestPath) + "close_beforeunload_opens_second_tab.html";
-const SECOND_TAB = getRootDirectory(gTestPath) + "close_beforeunload.html";
-
-add_task(function*() {
-  info("Opening first tab");
-  let firstTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, FIRST_TAB);
-  let secondTabLoadedPromise;
-  let secondTab;
-  let tabOpened = new Promise(resolve => {
-    info("Adding tabopen listener");
-    gBrowser.tabContainer.addEventListener("TabOpen", function tabOpenListener(e) {
-      info("Got tabopen, removing listener and waiting for load");
-      gBrowser.tabContainer.removeEventListener("TabOpen", tabOpenListener, false, false);
-      secondTab = e.target;
-      secondTabLoadedPromise = BrowserTestUtils.browserLoaded(secondTab.linkedBrowser, false, SECOND_TAB);
-      resolve();
-    }, false, false);
-  });
-  info("Opening second tab using a click");
-  yield ContentTask.spawn(firstTab.linkedBrowser, "", function*() {
-    content.document.getElementsByTagName("a")[0].click();
-  });
-  info("Waiting for the second tab to be opened");
-  yield tabOpened;
-  info("Waiting for the load in that tab to finish");
-  yield secondTabLoadedPromise;
-
-  let closeBtn = document.getAnonymousElementByAttribute(secondTab, "anonid", "close-button");
-  let closePromise = BrowserTestUtils.removeTab(secondTab, {dontRemove: true});
-  info("closing second tab (which will self-close in beforeunload)");
-  closeBtn.click();
-  ok(secondTab.closing, "Second tab should be marked as closing synchronously.");
-  yield closePromise;
-  ok(secondTab.closing, "Second tab should still be marked as closing");
-  ok(!secondTab.linkedBrowser, "Second tab's browser should be dead");
-  ok(!firstTab.closing, "First tab should not be closing");
-  ok(firstTab.linkedBrowser, "First tab's browser should be alive");
-  info("closing first tab");
-  yield BrowserTestUtils.removeTab(firstTab);
-
-  ok(firstTab.closing, "First tab should be marked as closing");
-  ok(!firstTab.linkedBrowser, "First tab's browser should be dead");
-});
+"use strict";
+
+SimpleTest.requestCompleteLog();
+
+SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
+
+const FIRST_TAB = getRootDirectory(gTestPath) + "close_beforeunload_opens_second_tab.html";
+const SECOND_TAB = getRootDirectory(gTestPath) + "close_beforeunload.html";
+
+add_task(function*() {
+  info("Opening first tab");
+  let firstTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, FIRST_TAB);
+  let secondTabLoadedPromise;
+  let secondTab;
+  let tabOpened = new Promise(resolve => {
+    info("Adding tabopen listener");
+    gBrowser.tabContainer.addEventListener("TabOpen", function tabOpenListener(e) {
+      info("Got tabopen, removing listener and waiting for load");
+      gBrowser.tabContainer.removeEventListener("TabOpen", tabOpenListener, false, false);
+      secondTab = e.target;
+      secondTabLoadedPromise = BrowserTestUtils.browserLoaded(secondTab.linkedBrowser, false, SECOND_TAB);
+      resolve();
+    }, false, false);
+  });
+  info("Opening second tab using a click");
+  yield ContentTask.spawn(firstTab.linkedBrowser, "", function*() {
+    content.document.getElementsByTagName("a")[0].click();
+  });
+  info("Waiting for the second tab to be opened");
+  yield tabOpened;
+  info("Waiting for the load in that tab to finish");
+  yield secondTabLoadedPromise;
+
+  let closeBtn = document.getAnonymousElementByAttribute(secondTab, "anonid", "close-button");
+  let closePromise = BrowserTestUtils.removeTab(secondTab, {dontRemove: true});
+  info("closing second tab (which will self-close in beforeunload)");
+  closeBtn.click();
+  ok(secondTab.closing, "Second tab should be marked as closing synchronously.");
+  yield closePromise;
+  ok(secondTab.closing, "Second tab should still be marked as closing");
+  ok(!secondTab.linkedBrowser, "Second tab's browser should be dead");
+  ok(!firstTab.closing, "First tab should not be closing");
+  ok(firstTab.linkedBrowser, "First tab's browser should be alive");
+  info("closing first tab");
+  yield BrowserTestUtils.removeTab(firstTab);
+
+  ok(firstTab.closing, "First tab should be marked as closing");
+  ok(!firstTab.linkedBrowser, "First tab's browser should be dead");
+});
--- a/browser/base/content/test/social/social_crash_content_helper.js
+++ b/browser/base/content/test/social/social_crash_content_helper.js
@@ -1,31 +1,31 @@
-/* Any copyright is dedicated to the Public Domain.
-* http://creativecommons.org/publicdomain/zero/1.0/ */
-
-var Cu = Components.utils;
-
-// Ideally we would use CrashTestUtils.jsm, but that's only available for
-// xpcshell tests - so we just copy a ctypes crasher from it.
-Cu.import("resource://gre/modules/ctypes.jsm");
-var crash = function() { // this will crash when called.
-  let zero = new ctypes.intptr_t(8);
-  let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
-  badptr.contents
-};
-
-
-var TestHelper = {
-  init: function() {
-    addMessageListener("social-test:crash", this);
-  },
-
-  receiveMessage: function(msg) {
-    switch (msg.name) {
-      case "social-test:crash":
-        privateNoteIntentionalCrash();
-        crash();
-      break;
-    }
-  },
-}
-
-TestHelper.init();
+/* Any copyright is dedicated to the Public Domain.
+* http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var Cu = Components.utils;
+
+// Ideally we would use CrashTestUtils.jsm, but that's only available for
+// xpcshell tests - so we just copy a ctypes crasher from it.
+Cu.import("resource://gre/modules/ctypes.jsm");
+var crash = function() { // this will crash when called.
+  let zero = new ctypes.intptr_t(8);
+  let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
+  badptr.contents
+};
+
+
+var TestHelper = {
+  init: function() {
+    addMessageListener("social-test:crash", this);
+  },
+
+  receiveMessage: function(msg) {
+    switch (msg.name) {
+      case "social-test:crash":
+        privateNoteIntentionalCrash();
+        crash();
+      break;
+    }
+  },
+}
+
+TestHelper.init();
--- a/browser/base/content/test/urlbar/browser_bug1025195_switchToTabHavingURI_aOpenParams.js
+++ b/browser/base/content/test/urlbar/browser_bug1025195_switchToTabHavingURI_aOpenParams.js
@@ -1,94 +1,94 @@
-/* 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/. */
-
-add_task(function test_ignoreFragment() {
-  let tabRefAboutHome = gBrowser.addTab("about:home#1");
-  yield promiseTabLoaded(tabRefAboutHome);
-  let tabRefAboutMozilla = gBrowser.addTab("about:mozilla");
-  yield promiseTabLoaded(tabRefAboutMozilla);
-
-  gBrowser.selectedTab = tabRefAboutMozilla;
-  let numTabsAtStart = gBrowser.tabs.length;
-
-  switchTab("about:home#1", true);
-  switchTab("about:mozilla", true);
-
-  let hashChangePromise = new Promise(resolve => {
-    tabRefAboutHome.linkedBrowser.contentWindow.addEventListener("hashchange", resolve, false);
-  });
-  switchTab("about:home#2", true, { ignoreFragment: true });
-  is(tabRefAboutHome, gBrowser.selectedTab, "The same about:home tab should be switched to");
-  yield hashChangePromise;
-  is(gBrowser.currentURI.ref, "2", "The ref should be updated to the new ref");
-  switchTab("about:mozilla", true);
-  switchTab("about:home#1", false);
-  isnot(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should not be initial about:blank tab");
-  is(gBrowser.tabs.length, numTabsAtStart + 1, "Should have one new tab opened");
-  switchTab("about:mozilla", true);
-  switchTab("about:home", true, {ignoreFragment: true});
-  yield promiseWaitForCondition(function() {
-    return tabRefAboutHome.linkedBrowser.currentURI.spec == "about:home";
-  });
-  is(tabRefAboutHome.linkedBrowser.currentURI.spec, "about:home", "about:home shouldn't have hash");
-  switchTab("about:about", false, { ignoreFragment: true });
-  cleanupTestTabs();
-});
-
-add_task(function test_ignoreQueryString() {
-  let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox");
-  yield promiseTabLoaded(tabRefAboutHome);
-  let tabRefAboutMozilla = gBrowser.addTab("about:mozilla");
-  yield promiseTabLoaded(tabRefAboutMozilla);
-  gBrowser.selectedTab = tabRefAboutMozilla;
-
-  switchTab("about:home?hello=firefox", true);
-  switchTab("about:home?hello=firefoxos", false);
-  // Remove the last opened tab to test ignoreQueryString option.
-  gBrowser.removeCurrentTab();
-  switchTab("about:home?hello=firefoxos", true, { ignoreQueryString: true });
-  is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab");
-  is(gBrowser.currentURI.spec, "about:home?hello=firefox", "The spec should NOT be updated to the new query string");
-  cleanupTestTabs();
-});
-
-add_task(function test_replaceQueryString() {
-  let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox");
-  yield promiseTabLoaded(tabRefAboutHome);
-  let tabRefAboutMozilla = gBrowser.addTab("about:mozilla");
-  yield promiseTabLoaded(tabRefAboutMozilla);
-  gBrowser.selectedTab = tabRefAboutMozilla;
-
-  switchTab("about:home", false);
-  switchTab("about:home?hello=firefox", true);
-  switchTab("about:home?hello=firefoxos", false);
-  // Remove the last opened tab to test replaceQueryString option.
-  gBrowser.removeCurrentTab();
-  switchTab("about:home?hello=firefoxos", true, { replaceQueryString: true });
-  is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab");
-  // Wait for the tab to load the new URI spec.
-  yield promiseTabLoaded(tabRefAboutHome);
-  is(gBrowser.currentURI.spec, "about:home?hello=firefoxos", "The spec should be updated to the new spec");
-  cleanupTestTabs();
-});
-
-// Begin helpers
-
-function cleanupTestTabs() {
-  while (gBrowser.tabs.length > 1)
-    gBrowser.removeCurrentTab();
-}
-
-function switchTab(aURI, aShouldFindExistingTab, aOpenParams = {}) {
-  // Build the description before switchToTabHavingURI deletes the object properties.
-  let msg = "Should switch to existing " + aURI + " tab if one existed, " +
-        (aOpenParams.ignoreFragment ? "ignoring" : "including") + " fragment portion, " +
-        (aOpenParams.ignoreQueryString || aOpenParams.replaceQueryString ?
-         (aOpenParams.replaceQueryString ? "replacing" : "ignoring") :
-         "including"
-        ) + " query string.";
-  let tabFound = switchToTabHavingURI(aURI, true, aOpenParams);
-  is(tabFound, aShouldFindExistingTab, msg);
-}
-
-registerCleanupFunction(cleanupTestTabs);
+/* 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/. */
+
+add_task(function test_ignoreFragment() {
+  let tabRefAboutHome = gBrowser.addTab("about:home#1");
+  yield promiseTabLoaded(tabRefAboutHome);
+  let tabRefAboutMozilla = gBrowser.addTab("about:mozilla");
+  yield promiseTabLoaded(tabRefAboutMozilla);
+
+  gBrowser.selectedTab = tabRefAboutMozilla;
+  let numTabsAtStart = gBrowser.tabs.length;
+
+  switchTab("about:home#1", true);
+  switchTab("about:mozilla", true);
+
+  let hashChangePromise = new Promise(resolve => {
+    tabRefAboutHome.linkedBrowser.contentWindow.addEventListener("hashchange", resolve, false);
+  });
+  switchTab("about:home#2", true, { ignoreFragment: true });
+  is(tabRefAboutHome, gBrowser.selectedTab, "The same about:home tab should be switched to");
+  yield hashChangePromise;
+  is(gBrowser.currentURI.ref, "2", "The ref should be updated to the new ref");
+  switchTab("about:mozilla", true);
+  switchTab("about:home#1", false);
+  isnot(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should not be initial about:blank tab");
+  is(gBrowser.tabs.length, numTabsAtStart + 1, "Should have one new tab opened");
+  switchTab("about:mozilla", true);
+  switchTab("about:home", true, {ignoreFragment: true});
+  yield promiseWaitForCondition(function() {
+    return tabRefAboutHome.linkedBrowser.currentURI.spec == "about:home";
+  });
+  is(tabRefAboutHome.linkedBrowser.currentURI.spec, "about:home", "about:home shouldn't have hash");
+  switchTab("about:about", false, { ignoreFragment: true });
+  cleanupTestTabs();
+});
+
+add_task(function test_ignoreQueryString() {
+  let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox");
+  yield promiseTabLoaded(tabRefAboutHome);
+  let tabRefAboutMozilla = gBrowser.addTab("about:mozilla");
+  yield promiseTabLoaded(tabRefAboutMozilla);
+  gBrowser.selectedTab = tabRefAboutMozilla;
+
+  switchTab("about:home?hello=firefox", true);
+  switchTab("about:home?hello=firefoxos", false);
+  // Remove the last opened tab to test ignoreQueryString option.
+  gBrowser.removeCurrentTab();
+  switchTab("about:home?hello=firefoxos", true, { ignoreQueryString: true });
+  is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab");
+  is(gBrowser.currentURI.spec, "about:home?hello=firefox", "The spec should NOT be updated to the new query string");
+  cleanupTestTabs();
+});
+
+add_task(function test_replaceQueryString() {
+  let tabRefAboutHome = gBrowser.addTab("about:home?hello=firefox");
+  yield promiseTabLoaded(tabRefAboutHome);
+  let tabRefAboutMozilla = gBrowser.addTab("about:mozilla");
+  yield promiseTabLoaded(tabRefAboutMozilla);
+  gBrowser.selectedTab = tabRefAboutMozilla;
+
+  switchTab("about:home", false);
+  switchTab("about:home?hello=firefox", true);
+  switchTab("about:home?hello=firefoxos", false);
+  // Remove the last opened tab to test replaceQueryString option.
+  gBrowser.removeCurrentTab();
+  switchTab("about:home?hello=firefoxos", true, { replaceQueryString: true });
+  is(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should be the initial about:home tab");
+  // Wait for the tab to load the new URI spec.
+  yield promiseTabLoaded(tabRefAboutHome);
+  is(gBrowser.currentURI.spec, "about:home?hello=firefoxos", "The spec should be updated to the new spec");
+  cleanupTestTabs();
+});
+
+// Begin helpers
+
+function cleanupTestTabs() {
+  while (gBrowser.tabs.length > 1)
+    gBrowser.removeCurrentTab();
+}
+
+function switchTab(aURI, aShouldFindExistingTab, aOpenParams = {}) {
+  // Build the description before switchToTabHavingURI deletes the object properties.
+  let msg = "Should switch to existing " + aURI + " tab if one existed, " +
+        (aOpenParams.ignoreFragment ? "ignoring" : "including") + " fragment portion, " +
+        (aOpenParams.ignoreQueryString || aOpenParams.replaceQueryString ?
+         (aOpenParams.replaceQueryString ? "replacing" : "ignoring") :
+         "including"
+        ) + " query string.";
+  let tabFound = switchToTabHavingURI(aURI, true, aOpenParams);
+  is(tabFound, aShouldFindExistingTab, msg);
+}
+
+registerCleanupFunction(cleanupTestTabs);
--- a/browser/components/preferences/in-content/tests/browser_bug1018066_resetScrollPosition.js
+++ b/browser/components/preferences/in-content/tests/browser_bug1018066_resetScrollPosition.js
@@ -1,24 +1,24 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-var originalWindowHeight;
-registerCleanupFunction(function() {
-  window.resizeTo(window.outerWidth, originalWindowHeight);
-  while (gBrowser.tabs[1])
-    gBrowser.removeTab(gBrowser.tabs[1]);
-});
-
-add_task(function() {
-  originalWindowHeight = window.outerHeight;
-  window.resizeTo(window.outerWidth, 300);
-  let prefs = yield openPreferencesViaOpenPreferencesAPI("paneApplications", undefined, {leaveOpen: true});
-  is(prefs.selectedPane, "paneApplications", "Applications pane was selected");
-  let mainContent = gBrowser.contentDocument.querySelector(".main-content");
-  mainContent.scrollTop = 50;
-  is(mainContent.scrollTop, 50, "main-content should be scrolled 50 pixels");
-
-  gBrowser.contentWindow.gotoPref("paneGeneral");
-  is(mainContent.scrollTop, 0,
-     "Switching to a different category should reset the scroll position");
-});
-
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var originalWindowHeight;
+registerCleanupFunction(function() {
+  window.resizeTo(window.outerWidth, originalWindowHeight);
+  while (gBrowser.tabs[1])
+    gBrowser.removeTab(gBrowser.tabs[1]);
+});
+
+add_task(function() {
+  originalWindowHeight = window.outerHeight;
+  window.resizeTo(window.outerWidth, 300);
+  let prefs = yield openPreferencesViaOpenPreferencesAPI("paneApplications", undefined, {leaveOpen: true});
+  is(prefs.selectedPane, "paneApplications", "Applications pane was selected");
+  let mainContent = gBrowser.contentDocument.querySelector(".main-content");
+  mainContent.scrollTop = 50;
+  is(mainContent.scrollTop, 50, "main-content should be scrolled 50 pixels");
+
+  gBrowser.contentWindow.gotoPref("paneGeneral");
+  is(mainContent.scrollTop, 0,
+     "Switching to a different category should reset the scroll position");
+});
+
--- a/browser/components/preferences/in-content/tests/browser_defaultbrowser_alwayscheck.js
+++ b/browser/components/preferences/in-content/tests/browser_defaultbrowser_alwayscheck.js
@@ -1,103 +1,103 @@
-"use strict";
-
-const CHECK_DEFAULT_INITIAL = Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser");
-
-add_task(function* clicking_make_default_checks_alwaysCheck_checkbox() {
-  yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
-
-  yield test_with_mock_shellservice({isDefault: false}, function*() {
-    let setDefaultPane = content.document.getElementById("setDefaultPane");
-    Assert.equal(setDefaultPane.selectedIndex, "0",
-      "The 'make default' pane should be visible when not default");
-    let alwaysCheck = content.document.getElementById("alwaysCheckDefault");
-    Assert.ok(!alwaysCheck.checked, "Always Check is unchecked by default");
-    Assert.ok(!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
-      "alwaysCheck pref should be false by default in test runs");
-
-    let setDefaultButton = content.document.getElementById("setDefaultButton");
-    setDefaultButton.click();
-    content.window.gMainPane.updateSetDefaultBrowser();
-
-    yield ContentTaskUtils.waitForCondition(() => alwaysCheck.checked,
-      "'Always Check' checkbox should get checked after clicking the 'Set Default' button");
-
-    Assert.ok(alwaysCheck.checked,
-      "Clicking 'Make Default' checks the 'Always Check' checkbox");
-    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
-      "Checking the checkbox should set the pref to true");
-    Assert.ok(alwaysCheck.disabled,
-      "'Always Check' checkbox is locked with default browser and alwaysCheck=true");
-    Assert.equal(setDefaultPane.selectedIndex, "1",
-      "The 'make default' pane should not be visible when default");
-    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
-      "checkDefaultBrowser pref is now enabled");
-  });
-
-  gBrowser.removeCurrentTab();
-  Services.prefs.clearUserPref("browser.shell.checkDefaultBrowser");
-});
-
-add_task(function* clicking_make_default_checks_alwaysCheck_checkbox() {
-  Services.prefs.lockPref("browser.shell.checkDefaultBrowser");
-  yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
-
-  yield test_with_mock_shellservice({isDefault: false}, function*() {
-    let setDefaultPane = content.document.getElementById("setDefaultPane");
-    Assert.equal(setDefaultPane.selectedIndex, "0",
-      "The 'make default' pane should be visible when not default");
-    let alwaysCheck = content.document.getElementById("alwaysCheckDefault");
-    Assert.ok(alwaysCheck.disabled, "Always Check is disabled when locked");
-    Assert.ok(alwaysCheck.checked,
-      "Always Check is checked because defaultPref is true and pref is locked");
-    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
-      "alwaysCheck pref should ship with 'true' by default");
-
-    let setDefaultButton = content.document.getElementById("setDefaultButton");
-    setDefaultButton.click();
-    content.window.gMainPane.updateSetDefaultBrowser();
-
-    yield ContentTaskUtils.waitForCondition(() => setDefaultPane.selectedIndex == "1",
-      "Browser is now default");
-
-    Assert.ok(alwaysCheck.checked,
-      "'Always Check' is still checked because it's locked");
-    Assert.ok(alwaysCheck.disabled,
-      "'Always Check is disabled because it's locked");
-    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
-      "The pref is locked and so doesn't get changed");
-  });
-
-  Services.prefs.unlockPref("browser.shell.checkDefaultBrowser");
-  gBrowser.removeCurrentTab();
-});
-
-registerCleanupFunction(function() {
-  Services.prefs.unlockPref("browser.shell.checkDefaultBrowser");
-  Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", CHECK_DEFAULT_INITIAL);
-});
-
-function* test_with_mock_shellservice(options, testFn) {
-  yield ContentTask.spawn(gBrowser.selectedBrowser, options, function*(options) {
-    let doc = content.document;
-    let win = doc.defaultView;
-    win.oldShellService = win.getShellService();
-    let mockShellService = {
-      _isDefault: false,
-      isDefaultBrowser() {
-        return this._isDefault;
-      },
-      setDefaultBrowser() {
-        this._isDefault = true;
-      },
-    };
-    win.getShellService = function() {
-      return mockShellService;
-    }
-    mockShellService._isDefault = options.isDefault;
-    win.gMainPane.updateSetDefaultBrowser();
-  });
-
-  yield ContentTask.spawn(gBrowser.selectedBrowser, null, testFn);
-
-  Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", CHECK_DEFAULT_INITIAL);
-}
+"use strict";
+
+const CHECK_DEFAULT_INITIAL = Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser");
+
+add_task(function* clicking_make_default_checks_alwaysCheck_checkbox() {
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
+
+  yield test_with_mock_shellservice({isDefault: false}, function*() {
+    let setDefaultPane = content.document.getElementById("setDefaultPane");
+    Assert.equal(setDefaultPane.selectedIndex, "0",
+      "The 'make default' pane should be visible when not default");
+    let alwaysCheck = content.document.getElementById("alwaysCheckDefault");
+    Assert.ok(!alwaysCheck.checked, "Always Check is unchecked by default");
+    Assert.ok(!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
+      "alwaysCheck pref should be false by default in test runs");
+
+    let setDefaultButton = content.document.getElementById("setDefaultButton");
+    setDefaultButton.click();
+    content.window.gMainPane.updateSetDefaultBrowser();
+
+    yield ContentTaskUtils.waitForCondition(() => alwaysCheck.checked,
+      "'Always Check' checkbox should get checked after clicking the 'Set Default' button");
+
+    Assert.ok(alwaysCheck.checked,
+      "Clicking 'Make Default' checks the 'Always Check' checkbox");
+    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
+      "Checking the checkbox should set the pref to true");
+    Assert.ok(alwaysCheck.disabled,
+      "'Always Check' checkbox is locked with default browser and alwaysCheck=true");
+    Assert.equal(setDefaultPane.selectedIndex, "1",
+      "The 'make default' pane should not be visible when default");
+    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
+      "checkDefaultBrowser pref is now enabled");
+  });
+
+  gBrowser.removeCurrentTab();
+  Services.prefs.clearUserPref("browser.shell.checkDefaultBrowser");
+});
+
+add_task(function* clicking_make_default_checks_alwaysCheck_checkbox() {
+  Services.prefs.lockPref("browser.shell.checkDefaultBrowser");
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences");
+
+  yield test_with_mock_shellservice({isDefault: false}, function*() {
+    let setDefaultPane = content.document.getElementById("setDefaultPane");
+    Assert.equal(setDefaultPane.selectedIndex, "0",
+      "The 'make default' pane should be visible when not default");
+    let alwaysCheck = content.document.getElementById("alwaysCheckDefault");
+    Assert.ok(alwaysCheck.disabled, "Always Check is disabled when locked");
+    Assert.ok(alwaysCheck.checked,
+      "Always Check is checked because defaultPref is true and pref is locked");
+    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
+      "alwaysCheck pref should ship with 'true' by default");
+
+    let setDefaultButton = content.document.getElementById("setDefaultButton");
+    setDefaultButton.click();
+    content.window.gMainPane.updateSetDefaultBrowser();
+
+    yield ContentTaskUtils.waitForCondition(() => setDefaultPane.selectedIndex == "1",
+      "Browser is now default");
+
+    Assert.ok(alwaysCheck.checked,
+      "'Always Check' is still checked because it's locked");
+    Assert.ok(alwaysCheck.disabled,
+      "'Always Check is disabled because it's locked");
+    Assert.ok(Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
+      "The pref is locked and so doesn't get changed");
+  });
+
+  Services.prefs.unlockPref("browser.shell.checkDefaultBrowser");
+  gBrowser.removeCurrentTab();
+});
+
+registerCleanupFunction(function() {
+  Services.prefs.unlockPref("browser.shell.checkDefaultBrowser");
+  Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", CHECK_DEFAULT_INITIAL);
+});
+
+function* test_with_mock_shellservice(options, testFn) {
+  yield ContentTask.spawn(gBrowser.selectedBrowser, options, function*(options) {
+    let doc = content.document;
+    let win = doc.defaultView;
+    win.oldShellService = win.getShellService();
+    let mockShellService = {
+      _isDefault: false,
+      isDefaultBrowser() {
+        return this._isDefault;
+      },
+      setDefaultBrowser() {
+        this._isDefault = true;
+      },
+    };
+    win.getShellService = function() {
+      return mockShellService;
+    }
+    mockShellService._isDefault = options.isDefault;
+    win.gMainPane.updateSetDefaultBrowser();
+  });
+
+  yield ContentTask.spawn(gBrowser.selectedBrowser, null, testFn);
+
+  Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", CHECK_DEFAULT_INITIAL);
+}
--- a/browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js
+++ b/browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js
@@ -1,20 +1,20 @@
-add_task(function() {
-  is(gBrowser.currentURI.spec, "about:blank", "Test starts with about:blank open");
-  yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home");
-  yield openPreferencesViaOpenPreferencesAPI("paneGeneral", null, {leaveOpen: true});
-  let doc = gBrowser.contentDocument;
-  is(gBrowser.currentURI.spec, "about:preferences#general",
-     "#general should be in the URI for about:preferences");
-  let oldHomepagePref = Services.prefs.getCharPref("browser.startup.homepage");
-
-  let useCurrent = doc.getElementById("useCurrent");
-  useCurrent.click();
-
-  is(gBrowser.tabs.length, 3, "Three tabs should be open");
-  is(Services.prefs.getCharPref("browser.startup.homepage"), "about:blank|about:home",
-     "about:blank and about:home should be the only homepages set");
-
-  Services.prefs.setCharPref("browser.startup.homepage", oldHomepagePref);
-  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
-  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
-});
+add_task(function() {
+  is(gBrowser.currentURI.spec, "about:blank", "Test starts with about:blank open");
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home");
+  yield openPreferencesViaOpenPreferencesAPI("paneGeneral", null, {leaveOpen: true});
+  let doc = gBrowser.contentDocument;
+  is(gBrowser.currentURI.spec, "about:preferences#general",
+     "#general should be in the URI for about:preferences");
+  let oldHomepagePref = Services.prefs.getCharPref("browser.startup.homepage");
+
+  let useCurrent = doc.getElementById("useCurrent");
+  useCurrent.click();
+
+  is(gBrowser.tabs.length, 3, "Three tabs should be open");
+  is(Services.prefs.getCharPref("browser.startup.homepage"), "about:blank|about:home",
+     "about:blank and about:home should be the only homepages set");
+
+  Services.prefs.setCharPref("browser.startup.homepage", oldHomepagePref);
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
--- a/browser/components/preferences/in-content/tests/browser_notifications_do_not_disturb.js
+++ b/browser/components/preferences/in-content/tests/browser_notifications_do_not_disturb.js
@@ -1,44 +1,44 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-
-registerCleanupFunction(function() {
-  while (gBrowser.tabs[1])
-    gBrowser.removeTab(gBrowser.tabs[1]);
-});
-
-add_task(function() {
-  let prefs = yield openPreferencesViaOpenPreferencesAPI("paneContent", undefined, {leaveOpen: true});
-  is(prefs.selectedPane, "paneContent", "Content pane was selected");
-
-  let doc = gBrowser.contentDocument;
-  let notificationsDoNotDisturbRow = doc.getElementById("notificationsDoNotDisturbRow");
-  if (notificationsDoNotDisturbRow.hidden) {
-    todo(false, "Do not disturb is not available on this platform");
-    return;
-  }
-
-  let alertService;
-  try {
-    alertService = Cc["@mozilla.org/alerts-service;1"]
-                     .getService(Ci.nsIAlertsService)
-                     .QueryInterface(Ci.nsIAlertsDoNotDisturb);
-  } catch (ex) {
-    ok(true, "Do not disturb is not available on this platform: " + ex.message);
-    return;
-  }
-
-  let checkbox = doc.getElementById("notificationsDoNotDisturb");
-  ok(!checkbox.checked, "Checkbox should not be checked by default");
-  ok(!alertService.manualDoNotDisturb, "Do not disturb should be off by default");
-
-  let checkboxChanged = waitForEvent(checkbox, "command")
-  checkbox.click();
-  yield checkboxChanged;
-  ok(alertService.manualDoNotDisturb, "Do not disturb should be enabled when checked");
-
-  checkboxChanged = waitForEvent(checkbox, "command")
-  checkbox.click();
-  yield checkboxChanged;
-  ok(!alertService.manualDoNotDisturb, "Do not disturb should be disabled when unchecked");
-});
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+registerCleanupFunction(function() {
+  while (gBrowser.tabs[1])
+    gBrowser.removeTab(gBrowser.tabs[1]);
+});
+
+add_task(function() {
+  let prefs = yield openPreferencesViaOpenPreferencesAPI("paneContent", undefined, {leaveOpen: true});
+  is(prefs.selectedPane, "paneContent", "Content pane was selected");
+
+  let doc = gBrowser.contentDocument;
+  let notificationsDoNotDisturbRow = doc.getElementById("notificationsDoNotDisturbRow");
+  if (notificationsDoNotDisturbRow.hidden) {
+    todo(false, "Do not disturb is not available on this platform");
+    return;
+  }
+
+  let alertService;
+  try {
+    alertService = Cc["@mozilla.org/alerts-service;1"]
+                     .getService(Ci.nsIAlertsService)
+                     .QueryInterface(Ci.nsIAlertsDoNotDisturb);
+  } catch (ex) {
+    ok(true, "Do not disturb is not available on this platform: " + ex.message);
+    return;
+  }
+
+  let checkbox = doc.getElementById("notificationsDoNotDisturb");
+  ok(!checkbox.checked, "Checkbox should not be checked by default");
+  ok(!alertService.manualDoNotDisturb, "Do not disturb should be off by default");
+
+  let checkboxChanged = waitForEvent(checkbox, "command")
+  checkbox.click();
+  yield checkboxChanged;
+  ok(alertService.manualDoNotDisturb, "Do not disturb should be enabled when checked");
+
+  checkboxChanged = waitForEvent(checkbox, "command")
+  checkbox.click();
+  yield checkboxChanged;
+  ok(!alertService.manualDoNotDisturb, "Do not disturb should be disabled when unchecked");
+});
--- a/browser/components/preferences/in-content/tests/browser_permissions_urlFieldHidden.js
+++ b/browser/components/preferences/in-content/tests/browser_permissions_urlFieldHidden.js
@@ -1,45 +1,45 @@
-"use strict";
-
-const PERMISSIONS_URL = "chrome://browser/content/preferences/permissions.xul";
-
-add_task(function* urlFieldVisibleForPopupPermissions(finish) {
-  yield openPreferencesViaOpenPreferencesAPI("paneContent", null, {leaveOpen: true});
-  let win = gBrowser.selectedBrowser.contentWindow;
-  let doc = win.document;
-  let popupPolicyCheckbox = doc.getElementById("popupPolicy");
-  ok(!popupPolicyCheckbox.checked, "popupPolicyCheckbox should be unchecked by default");
-  popupPolicyCheckbox.click();
-  let popupPolicyButton = doc.getElementById("popupPolicyButton");
-  ok(popupPolicyButton, "popupPolicyButton found");
-  let dialogPromise = promiseLoadSubDialog(PERMISSIONS_URL);
-  popupPolicyButton.click();
-  let dialog = yield dialogPromise;
-  ok(dialog, "dialog loaded");
-
-  let urlLabel = dialog.document.getElementById("urlLabel");
-  ok(!urlLabel.hidden, "urlLabel should be visible when one of block/session/allow visible");
-  let url = dialog.document.getElementById("url");
-  ok(!url.hidden, "url should be visible when one of block/session/allow visible");
-
-  popupPolicyCheckbox.click();
-  gBrowser.removeCurrentTab();
-});
-
-add_task(function* urlFieldHiddenForNotificationPermissions() {
-  yield openPreferencesViaOpenPreferencesAPI("paneContent", null, {leaveOpen: true});
-  let win = gBrowser.selectedBrowser.contentWindow;
-  let doc = win.document;
-  let notificationsPolicyButton = doc.getElementById("notificationsPolicyButton");
-  ok(notificationsPolicyButton, "notificationsPolicyButton found");
-  let dialogPromise = promiseLoadSubDialog(PERMISSIONS_URL);
-  notificationsPolicyButton.click();
-  let dialog = yield dialogPromise;
-  ok(dialog, "dialog loaded");
-
-  let urlLabel = dialog.document.getElementById("urlLabel");
-  ok(urlLabel.hidden, "urlLabel should be hidden as requested");
-  let url = dialog.document.getElementById("url");
-  ok(url.hidden, "url should be hidden as requested");
-
-  gBrowser.removeCurrentTab();
-});
+"use strict";
+
+const PERMISSIONS_URL = "chrome://browser/content/preferences/permissions.xul";
+
+add_task(function* urlFieldVisibleForPopupPermissions(finish) {
+  yield openPreferencesViaOpenPreferencesAPI("paneContent", null, {leaveOpen: true});
+  let win = gBrowser.selectedBrowser.contentWindow;
+  let doc = win.document;
+  let popupPolicyCheckbox = doc.getElementById("popupPolicy");
+  ok(!popupPolicyCheckbox.checked, "popupPolicyCheckbox should be unchecked by default");
+  popupPolicyCheckbox.click();
+  let popupPolicyButton = doc.getElementById("popupPolicyButton");
+  ok(popupPolicyButton, "popupPolicyButton found");
+  let dialogPromise = promiseLoadSubDialog(PERMISSIONS_URL);
+  popupPolicyButton.click();
+  let dialog = yield dialogPromise;
+  ok(dialog, "dialog loaded");
+
+  let urlLabel = dialog.document.getElementById("urlLabel");
+  ok(!urlLabel.hidden, "urlLabel should be visible when one of block/session/allow visible");
+  let url = dialog.document.getElementById("url");
+  ok(!url.hidden, "url should be visible when one of block/session/allow visible");
+
+  popupPolicyCheckbox.click();
+  gBrowser.removeCurrentTab();
+});
+
+add_task(function* urlFieldHiddenForNotificationPermissions() {
+  yield openPreferencesViaOpenPreferencesAPI("paneContent", null, {leaveOpen: true});
+  let win = gBrowser.selectedBrowser.contentWindow;
+  let doc = win.document;
+  let notificationsPolicyButton = doc.getElementById("notificationsPolicyButton");
+  ok(notificationsPolicyButton, "notificationsPolicyButton found");
+  let dialogPromise = promiseLoadSubDialog(PERMISSIONS_URL);
+  notificationsPolicyButton.click();
+  let dialog = yield dialogPromise;
+  ok(dialog, "dialog loaded");
+
+  let urlLabel = dialog.document.getElementById("urlLabel");
+  ok(urlLabel.hidden, "urlLabel should be hidden as requested");
+  let url = dialog.document.getElementById("url");
+  ok(url.hidden, "url should be hidden as requested");
+
+  gBrowser.removeCurrentTab();
+});
--- a/browser/components/preferences/in-content/tests/browser_sanitizeOnShutdown_prefLocked.js
+++ b/browser/components/preferences/in-content/tests/browser_sanitizeOnShutdown_prefLocked.js
@@ -1,37 +1,37 @@
-"use strict";
-
-function switchToCustomHistoryMode(doc) {
-  // Select the last item in the menulist.
-  let menulist = doc.getElementById("historyMode");
-  menulist.focus();
-  EventUtils.sendKey("UP");
-}
-
-function testPrefStateMatchesLockedState() {
-  let win = gBrowser.contentWindow;
-  let doc = win.document;
-  switchToCustomHistoryMode(doc);
-
-  let checkbox = doc.getElementById("alwaysClear");
-  let preference = doc.getElementById("privacy.sanitize.sanitizeOnShutdown");
-  is(checkbox.disabled, preference.locked, "Always Clear checkbox should be enabled when preference is not locked.");
-
-  gBrowser.removeCurrentTab();
-}
-
-add_task(function setup() {
-  registerCleanupFunction(function resetPreferences() {
-    Services.prefs.unlockPref("privacy.sanitize.sanitizeOnShutdown");
-  });
-});
-
-add_task(function test_preference_enabled_when_unlocked() {
-  yield openPreferencesViaOpenPreferencesAPI("panePrivacy", undefined, {leaveOpen: true});
-  testPrefStateMatchesLockedState();
-});
-
-add_task(function test_preference_disabled_when_locked() {
-  Services.prefs.lockPref("privacy.sanitize.sanitizeOnShutdown");
-  yield openPreferencesViaOpenPreferencesAPI("panePrivacy", undefined, {leaveOpen: true});
-  testPrefStateMatchesLockedState();
-});
+"use strict";
+
+function switchToCustomHistoryMode(doc) {
+  // Select the last item in the menulist.
+  let menulist = doc.getElementById("historyMode");
+  menulist.focus();
+  EventUtils.sendKey("UP");
+}
+
+function testPrefStateMatchesLockedState() {
+  let win = gBrowser.contentWindow;
+  let doc = win.document;
+  switchToCustomHistoryMode(doc);
+
+  let checkbox = doc.getElementById("alwaysClear");
+  let preference = doc.getElementById("privacy.sanitize.sanitizeOnShutdown");
+  is(checkbox.disabled, preference.locked, "Always Clear checkbox should be enabled when preference is not locked.");
+
+  gBrowser.removeCurrentTab();
+}
+
+add_task(function setup() {
+  registerCleanupFunction(function resetPreferences() {
+    Services.prefs.unlockPref("privacy.sanitize.sanitizeOnShutdown");
+  });
+});
+
+add_task(function test_preference_enabled_when_unlocked() {
+  yield openPreferencesViaOpenPreferencesAPI("panePrivacy", undefined, {leaveOpen: true});
+  testPrefStateMatchesLockedState();
+});
+
+add_task(function test_preference_disabled_when_locked() {
+  Services.prefs.lockPref("privacy.sanitize.sanitizeOnShutdown");
+  yield openPreferencesViaOpenPreferencesAPI("panePrivacy", undefined, {leaveOpen: true});
+  testPrefStateMatchesLockedState();
+});
--- a/browser/components/shell/ShellService.jsm
+++ b/browser/components/shell/ShellService.jsm
@@ -1,107 +1,107 @@
-/* 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/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["ShellService"];
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
-                                  "resource://gre/modules/WindowsRegistry.jsm");
-
-/**
- * Internal functionality to save and restore the docShell.allow* properties.
- */
-let ShellServiceInternal = {
-  /**
-   * Used to determine whether or not to offer "Set as desktop background"
-   * functionality. Even if shell service is available it is not
-   * guaranteed that it is able to set the background for every desktop
-   * which is especially true for Linux with its many different desktop
-   * environments.
-   */
-  get canSetDesktopBackground() {
-    if (AppConstants.platform == "win" ||
-        AppConstants.platform == "macosx") {
-      return true;
-    }
-
-    if (AppConstants.platform == "linux") {
-      if (this.shellService) {
-        let linuxShellService = this.shellService
-                                    .QueryInterface(Ci.nsIGNOMEShellService);
-        return linuxShellService.canSetDesktopBackground;
-      }
-    }
-
-    return false;
-  },
-
-  /**
-   * Used to determine whether or not to show a "Set Default Browser"
-   * query dialog. This attribute is true if the application is starting
-   * up and "browser.shell.checkDefaultBrowser" is true, otherwise it
-   * is false.
-   */
-  _checkedThisSession: false,
-  get shouldCheckDefaultBrowser() {
-    // If we've already checked, the browser has been started and this is a
-    // new window open, and we don't want to check again.
-    if (this._checkedThisSession) {
-      return false;
-    }
-
-    if (!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser")) {
-      return false;
-    }
-
-    if (AppConstants.platform == "win") {
-      let optOutValue = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                                                   "Software\\Mozilla\\Firefox",
-                                                   "DefaultBrowserOptOut");
-      WindowsRegistry.removeRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                                   "Software\\Mozilla\\Firefox",
-                                   "DefaultBrowserOptOut");
-      if (optOutValue == "True") {
-        Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", false);
-        return false;
-      }
-    }
-
-    return true;
-  },
-
-  set shouldCheckDefaultBrowser(shouldCheck) {
-    Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", !!shouldCheck);
-  },
-
-  isDefaultBrowser(startupCheck, forAllTypes) {
-    // If this is the first browser window, maintain internal state that we've
-    // checked this session (so that subsequent window opens don't show the
-    // default browser dialog).
-    if (startupCheck) {
-      this._checkedThisSession = true;
-    }
-    if (this.shellService) {
-      return this.shellService.isDefaultBrowser(startupCheck, forAllTypes);
-    }
-  }
-};
-
-XPCOMUtils.defineLazyServiceGetter(ShellServiceInternal, "shellService",
-  "@mozilla.org/browser/shell-service;1", Ci.nsIShellService);
-
-/**
- * The external API exported by this module.
- */
-this.ShellService = new Proxy(ShellServiceInternal, {
-  get(target, name) {
-    return name in target ? target[name] :
-                            target.shellService[name];
-  }
-});
+/* 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/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["ShellService"];
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
+                                  "resource://gre/modules/WindowsRegistry.jsm");
+
+/**
+ * Internal functionality to save and restore the docShell.allow* properties.
+ */
+let ShellServiceInternal = {
+  /**
+   * Used to determine whether or not to offer "Set as desktop background"
+   * functionality. Even if shell service is available it is not
+   * guaranteed that it is able to set the background for every desktop
+   * which is especially true for Linux with its many different desktop
+   * environments.
+   */
+  get canSetDesktopBackground() {
+    if (AppConstants.platform == "win" ||
+        AppConstants.platform == "macosx") {
+      return true;
+    }
+
+    if (AppConstants.platform == "linux") {
+      if (this.shellService) {
+        let linuxShellService = this.shellService
+                                    .QueryInterface(Ci.nsIGNOMEShellService);
+        return linuxShellService.canSetDesktopBackground;
+      }
+    }
+
+    return false;
+  },
+
+  /**
+   * Used to determine whether or not to show a "Set Default Browser"
+   * query dialog. This attribute is true if the application is starting
+   * up and "browser.shell.checkDefaultBrowser" is true, otherwise it
+   * is false.
+   */
+  _checkedThisSession: false,
+  get shouldCheckDefaultBrowser() {
+    // If we've already checked, the browser has been started and this is a
+    // new window open, and we don't want to check again.
+    if (this._checkedThisSession) {
+      return false;
+    }
+
+    if (!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser")) {
+      return false;
+    }
+
+    if (AppConstants.platform == "win") {
+      let optOutValue = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                                                   "Software\\Mozilla\\Firefox",
+                                                   "DefaultBrowserOptOut");
+      WindowsRegistry.removeRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                                   "Software\\Mozilla\\Firefox",
+                                   "DefaultBrowserOptOut");
+      if (optOutValue == "True") {
+        Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", false);
+        return false;
+      }
+    }
+
+    return true;
+  },
+
+  set shouldCheckDefaultBrowser(shouldCheck) {
+    Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", !!shouldCheck);
+  },
+
+  isDefaultBrowser(startupCheck, forAllTypes) {
+    // If this is the first browser window, maintain internal state that we've
+    // checked this session (so that subsequent window opens don't show the
+    // default browser dialog).
+    if (startupCheck) {
+      this._checkedThisSession = true;
+    }
+    if (this.shellService) {
+      return this.shellService.isDefaultBrowser(startupCheck, forAllTypes);
+    }
+  }
+};
+
+XPCOMUtils.defineLazyServiceGetter(ShellServiceInternal, "shellService",
+  "@mozilla.org/browser/shell-service;1", Ci.nsIShellService);
+
+/**
+ * The external API exported by this module.
+ */
+this.ShellService = new Proxy(ShellServiceInternal, {
+  get(target, name) {
+    return name in target ? target[name] :
+                            target.shellService[name];
+  }
+});
--- a/browser/extensions/pocket/content/panels/js/vendor/jquery.tokeninput.min.js
+++ b/browser/extensions/pocket/content/panels/js/vendor/jquery.tokeninput.min.js
@@ -1,944 +1,944 @@
-/*
- * jQuery Plugin: Tokenizing Autocomplete Text Entry
- * Version 1.6.0
- *
- * Copyright (c) 2009 James Smith (http://loopj.com)
- * Licensed jointly under the GPL and MIT licenses,
- * choose which one suits your project best!
- *
- * Licensed under MIT
- * With modifications
- *
- */
-
-(function ($) {
-// Default settings
-var DEFAULT_SETTINGS = {
- // Search settings
-    method: "GET",
-    contentType: "json",
-    queryParam: "q",
-    searchDelay: 300,
-    minChars: 1,
-    propertyToSearch: "name",
-    jsonContainer: null,
-    scrollKeyboard: false,
-
- // Display settings
-    hintText: null,
-    noResultsText: null,
-    noResultsHideDropdown: false,
-    searchingText: null,
-    deleteText: "&times;",
-    animateDropdown: true,
-    emptyInputLength: null,
-
- // Tokenization settings
-    tokenLimit: null,
-    tokenDelimiter: ",",
-    preventDuplicates: false,
-
- // Output settings
-    tokenValue: "id",
-
- // Prepopulation settings
-    prePopulate: null,
-    processPrePopulate: false,
-
- // Manipulation settings
-    idPrefix: "token-input-",
-
- // Formatters
-    resultsFormatter: function(item){ return "<li>" + item[this.propertyToSearch]+ "</li>" },
-    tokenFormatter: function(item) { return "<li><p>" + item[this.propertyToSearch] + "</p></li>" },
-
-    // Validations
-    validateItem: null,
-
-    // Force selections only on mouse click
-    noHoverSelect: false,
-
- // Callbacks
-    onResult: null,
-    onAdd: null,
-    onDelete: null,
-    onReady: null
-};
-
-// Default classes to use when theming
-var DEFAULT_CLASSES = {
-    tokenList: "token-input-list",
-    token: "token-input-token",
-    tokenDelete: "token-input-delete-token",
-    selectedToken: "token-input-selected-token",
-    highlightedToken: "token-input-highlighted-token",
-    dropdown: "token-input-dropdown",
-    dropdownItem: "token-input-dropdown-item",
-    dropdownItem2: "token-input-dropdown-item2",
-    selectedDropdownItem: "token-input-selected-dropdown-item",
-    inputToken: "token-input-input-token"
-};
-
-// Input box position "enum"
-var POSITION = {
-    BEFORE: 0,
-    AFTER: 1,
-    END: 2
-};
-
-// Keys "enum"
-var KEY = {
-    BACKSPACE: 8,
-    TAB: 9,
-    ENTER: 13,
-    ESCAPE: 27,
-    SPACE: 32,
-    PAGE_UP: 33,
-    PAGE_DOWN: 34,
-    END: 35,
-    HOME: 36,
-    LEFT: 37,
-    UP: 38,
-    RIGHT: 39,
-    DOWN: 40,
-    NUMPAD_ENTER: 108,
-    COMMA: 188
-};
-
-// Additional public (exposed) methods
-var methods = {
-    init: function(url_or_data_or_function, options) {
-        var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
-
-        return this.each(function () {
-            $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings));
-        });
-    },
-    clear: function() {
-        this.data("tokenInputObject").clear();
-        return this;
-    },
-    add: function(item) {
-        this.data("tokenInputObject").add(item);
-        return this;
-    },
-    remove: function(item) {
-        this.data("tokenInputObject").remove(item);
-        return this;
-    },
-    get: function() {
-     return this.data("tokenInputObject").getTokens();
-     }
-}
-
-// Expose the .tokenInput function to jQuery as a plugin
-$.fn.tokenInput = function (method) {
-    // Method calling and initialization logic
-    if(methods[method]) {
-        return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
-    } else {
-        return methods.init.apply(this, arguments);
-    }
-};
-
-// TokenList class for each input
-$.TokenList = function (input, url_or_data, settings) {
-    //
-    // Initialization
-    //
-
-    // Configure the data source
-    if($.type(url_or_data) === "string" || $.type(url_or_data) === "function") {
-        // Set the url to query against
-        settings.url = url_or_data;
-
-        // If the URL is a function, evaluate it here to do our initalization work
-        var url = computeURL();
-
-        // Make a smart guess about cross-domain if it wasn't explicitly specified
-        if(settings.crossDomain === undefined) {
-            if(url.indexOf("://") === -1) {
-                settings.crossDomain = false;
-            } else {
-                settings.crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]);
-            }
-        }
-    } else if(typeof(url_or_data) === "object") {
-        // Set the local data to search through
-        settings.local_data = url_or_data;
-    }
-
-    // Build class names
-    if(settings.classes) {
-        // Use custom class names
-        settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes);
-    } else if(settings.theme) {
-        // Use theme-suffixed default class names
-        settings.classes = {};
-        $.each(DEFAULT_CLASSES, function(key, value) {
-            settings.classes[key] = value + "-" + settings.theme;
-        });
-    } else {
-        settings.classes = DEFAULT_CLASSES;
-    }
-
-
-    // Save the tokens
-    var saved_tokens = [];
-
-    // Keep track of the number of tokens in the list
-    var token_count = 0;
-
-    // Basic cache to save on db hits
-    var cache = new $.TokenList.Cache();
-
-    // Keep track of the timeout, old vals
-    var timeout;
-    var input_val;
-
-    function tokenize(){
-        var item = $(selected_dropdown_item).data("tokeninput");
-        if(!item && settings.textToData){
-            item = settings.textToData(input_box.val());
-        }
-
-        if(item) {
-            add_token(item);
-            hidden_input.change();
-            return false;
-        }
-    }
-
-    // Create a new text input an attach keyup events
-    var input_box = $("<input type=\"text\"  autocomplete=\"off\">")
-        .css({
-            outline: "none"
-        })
-        .attr("id", settings.idPrefix + input.id)
-        .focus(function () {
-            if (settings.minChars == 0) {
-                setTimeout(function(){do_search();}, 5);
-            }
-            if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
-                show_dropdown_hint();
-            }
-        })
-        .blur(function () {
-            tokenize();
-            hide_dropdown();
-            $(this).val("");
-        })
-        .bind("keyup keydown blur update", resize_input)
-        .keydown(function (event) {
-            var previous_token;
-            var next_token;
-
-            switch(event.keyCode) {
-                case KEY.LEFT:
-                case KEY.RIGHT:
-                case KEY.UP:
-                case KEY.DOWN:
-                    if(!$(this).val()) {
-                        previous_token = input_token.prev();
-                        next_token = input_token.next();
-
-                        if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
-                            // Check if there is a previous/next token and it is selected
-                            if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
-                                deselect_token($(selected_token), POSITION.BEFORE);
-                            } else {
-                                deselect_token($(selected_token), POSITION.AFTER);
-                            }
-                        } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
-                            // We are moving left, select the previous token if it exists
-                            select_token($(previous_token.get(0)));
-                        } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
-                            // We are moving right, select the next token if it exists
-                            select_token($(next_token.get(0)));
-                        }
-                    } else {
-                        if (event.keyCode === KEY.UP || event.keyCode === KEY.DOWN) {
-                            var dropdown_item = null;
-                            if(!selected_dropdown_item && (event.keyCode === KEY.DOWN)) {
-                                dropdown_item = $('.token-input-dropdown li').first();
-                            }
-                            else if(event.keyCode === KEY.DOWN) {
-                                dropdown_item = $(selected_dropdown_item).next();
-                            } else {
-                                dropdown_item = $(selected_dropdown_item).prev();
-                            }
-
-                            if(dropdown_item.length) {
-                                select_dropdown_item(dropdown_item,true);
-                            }
-                            else if (!(event.keyCode === KEY.DOWN) && $(selected_dropdown_item).length) {
-                                deselect_dropdown_item($(selected_dropdown_item));
-                            }
-                            return false;
-                        }
-                    }
-                    break;
-
-                case KEY.BACKSPACE:
-                    previous_token = input_token.prev();
-
-                    if(!$(this).val().length) {
-                        if(selected_token) {
-                            delete_token($(selected_token));
-                            hidden_input.change();
-                        } else if(previous_token.length) {
-                            select_token($(previous_token.get(0)));
-                        }
-
-                        return false;
-                    } else if($(this).val().length === 1) {
-                        hide_dropdown();
-                    } else {
-                        // set a timeout just long enough to let this function finish.
-                        setTimeout(function(){do_search();}, 5);
-                    }
-                    break;
-
-                case KEY.TAB:
-                case KEY.ENTER:
-                case KEY.NUMPAD_ENTER:
-                case KEY.COMMA:
-                    if (event.keyCode != KEY.ENTER && event.keyCode != KEY.NUMPAD_ENTER)
-                    {
-                        event.preventDefault();
-                    }
-                    tokenize();
-                  break;
-
-                case KEY.ESCAPE:
-                  hide_dropdown();
-                  return true;
-
-                default:
-                    if(String.fromCharCode(event.which)) {
-                        // set a timeout just long enough to let this function finish.
-                        setTimeout(function(){do_search();}, 5);
-                    }
-                    break;
-            }
-        });
-
-    // Keep a reference to the original input box
-    var hidden_input = $(input)
-                           .hide()
-                           .val("")
-                           .focus(function () {
-                               input_box.focus();
-                           })
-                           .blur(function () {
-                               input_box.blur();
-                           });
-
-    // Keep a reference to the selected token and dropdown item
-    var selected_token = null;
-    var selected_token_index = 0;
-    var selected_dropdown_item = null;
-
-    // The list to store the token items in
-    var token_list = $("<ul />")
-        .addClass(settings.classes.tokenList)
-        .click(function (event) {
-            var li = $(event.target).closest("li");
-            if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
-                toggle_select_token(li);
-            } else {
-                // Deselect selected token
-                if(selected_token) {
-                    deselect_token($(selected_token), POSITION.END);
-                }
-
-                // Focus input box
-                input_box.focus();
-            }
-        })
-        .mouseover(function (event) {
-            var li = $(event.target).closest("li");
-            if(li && selected_token !== this) {
-                li.addClass(settings.classes.highlightedToken);
-            }
-        })
-        .mouseout(function (event) {
-            var li = $(event.target).closest("li");
-            if(li && selected_token !== this) {
-                li.removeClass(settings.classes.highlightedToken);
-            }
-        })
-        .insertBefore(hidden_input);
-
-    // The token holding the input box
-    var input_token = $("<li />")
-        .addClass(settings.classes.inputToken)
-        .appendTo(token_list)
-        .append(input_box);
-
-    // The list to store the dropdown items in
-    var dropdown = $("<div>")
-        .addClass(settings.classes.dropdown)
-        .appendTo("body")
-        .hide();
-
-    // Magic element to help us resize the text input
-    var input_resizer = $("<tester/>")
-        .insertAfter(input_box)
-        .css({
-            position: "absolute",
-            top: -9999,
-            left: -9999,
-            width: "auto",
-            fontSize: input_box.css("fontSize"),
-            fontFamily: input_box.css("fontFamily"),
-            fontWeight: input_box.css("fontWeight"),
-            letterSpacing: input_box.css("letterSpacing"),
-            whiteSpace: "nowrap"
-        });
-
-    // Pre-populate list if items exist
-    hidden_input.val("");
-    var li_data = settings.prePopulate || hidden_input.data("pre");
-    if(settings.processPrePopulate && $.isFunction(settings.onResult)) {
-        li_data = settings.onResult.call(hidden_input, li_data);
-    }
-    if(li_data && li_data.length) {
-        $.each(li_data, function (index, value) {
-            insert_token(value);
-            checkTokenLimit();
-        });
-    }
-
-    // Initialization is done
-    if($.isFunction(settings.onReady)) {
-        settings.onReady.call();
-        if (settings.minChars == 0)
-        {
-            setTimeout(function(){do_search();}, 5);
-        }
-    }
-
-    //
-    // Public functions
-    //
-
-    this.clear = function() {
-        token_list.children("li").each(function() {
-            if ($(this).children("input").length === 0) {
-                delete_token($(this));
-            }
-        });
-    }
-
-    this.add = function(item) {
-        add_token(item);
-    }
-
-    this.remove = function(item) {
-        token_list.children("li").each(function() {
-            if ($(this).children("input").length === 0) {
-                var currToken = $(this).data("tokeninput");
-                var match = true;
-                for (var prop in item) {
-                    if (item[prop] !== currToken[prop]) {
-                        match = false;
-                        break;
-                    }
-                }
-                if (match) {
-                    delete_token($(this));
-                }
-            }
-        });
-    }
-    
-    this.getTokens = function() {
-       return saved_tokens;
-     }
-
-    //
-    // Private functions
-    //
-
-    function checkTokenLimit() {
-        if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
-            input_box.hide();
-            hide_dropdown();
-            return;
-        }
-    }
-
-    function resize_input() {
-        if(input_val === (input_val = input_box.val())) {return;}
-
-        // Enter new content into resizer and resize input accordingly
-        var escaped = input_val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
-        input_resizer.html(escaped);
-        var minwidth = 30;
-        if (settings.emptyInputLength && token_list.children().length < 2) {
-            minwidth = settings.emptyInputLength;
-        }
-        input_box.width(input_resizer.width() + minwidth);
-    }
-
-    function is_printable_character(keycode) {
-        return ((keycode >= 48 && keycode <= 90) ||     // 0-1a-z
-                (keycode >= 96 && keycode <= 111) ||    // numpad 0-9 + - / * .
-                (keycode >= 186 && keycode <= 192) ||   // ; = , - . / ^
-                (keycode >= 219 && keycode <= 222));    // ( \ ) '
-    }
-
-    // Inner function to a token to the list
-    function insert_token(item) {
-        var this_token = settings.tokenFormatter(item);
-        this_token = $(this_token)
-          .addClass(settings.classes.token)
-          .insertBefore(input_token);
-
-        // The 'delete token' button
-        $("<span>" + settings.deleteText + "</span>")
-            .addClass(settings.classes.tokenDelete)
-            .appendTo(this_token)
-            .click(function () {
-                delete_token($(this).parent());
-                hidden_input.change();
-                return false;
-            });
-
-        // Store data on the token
-        var token_data = {"id": item.id};
-        token_data[settings.propertyToSearch] = item[settings.propertyToSearch];
-        token_data.item = item;
-        $.data(this_token.get(0), "tokeninput", item);
-
-        // Save this token for duplicate checking
-        saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
-        selected_token_index++;
-
-        // Update the hidden input
-        update_hidden_input(saved_tokens, hidden_input);
-
-        token_count += 1;
-
-        // Check the token limit
-        if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
-            input_box.hide();
-            hide_dropdown();
-        }
-
-        return this_token;
-    }
-
-    // Add a token to the token list based on user input
-    function add_token (item) {
-        if(!item) return;
-
-        // Check for item validation
-        if ($.isFunction(settings.validateItem) && !settings.validateItem(item)) {
-            return false;
-        }
-
-        var callback = settings.onAdd;
-
-        // See if the token already exists and select it if we don't want duplicates
-        if(token_count > 0 && settings.preventDuplicates) {
-            var found_existing_token = null;
-            token_list.children().each(function () {
-                var existing_token = $(this);
-                var existing_data = $.data(existing_token.get(0), "tokeninput");
-                if(existing_data && existing_data.id === item.id) {
-                    found_existing_token = existing_token;
-                    return false;
-                }
-            });
-
-            if(found_existing_token) {
-                select_token(found_existing_token);
-                input_token.insertAfter(found_existing_token);
-                input_box.focus();
-                return;
-            }
-        }
-
-        // Insert the new tokens
-        if(settings.tokenLimit == null || token_count < settings.tokenLimit) {
-            insert_token(item);
-            checkTokenLimit();
-        }
-
-        // Clear input box
-        input_box.val("");
-
-        // Don't show the help dropdown, they've got the idea
-        hide_dropdown();
-
-        // Execute the onAdd callback if defined
-        if($.isFunction(callback)) {
-            callback.call(hidden_input,item);
-        }
-    }
-
-    // Select a token in the token list
-    function select_token (token) {
-        token.addClass(settings.classes.selectedToken);
-        selected_token = token.get(0);
-
-        // Hide input box
-        input_box.val("");
-
-        // Hide dropdown if it is visible (eg if we clicked to select token)
-        hide_dropdown();
-    }
-
-    // Deselect a token in the token list
-    function deselect_token (token, position) {
-        token.removeClass(settings.classes.selectedToken);
-        selected_token = null;
-
-        if(position === POSITION.BEFORE) {
-            input_token.insertBefore(token);
-            selected_token_index--;
-        } else if(position === POSITION.AFTER) {
-            input_token.insertAfter(token);
-            selected_token_index++;
-        } else {
-            input_token.appendTo(token_list);
-            selected_token_index = token_count;
-        }
-
-        // Show the input box and give it focus again
-        input_box.focus();
-    }
-
-    // Toggle selection of a token in the token list
-    function toggle_select_token(token) {
-        var previous_selected_token = selected_token;
-
-        if(selected_token) {
-            deselect_token($(selected_token), POSITION.END);
-        }
-
-        if(previous_selected_token === token.get(0)) {
-            deselect_token(token, POSITION.END);
-        } else {
-            select_token(token);
-        }
-    }
-
-    // Delete a token from the token list
-    function delete_token (token) {
-        // Remove the id from the saved list
-        var token_data = $.data(token.get(0), "tokeninput");
-        var callback = settings.onDelete;
-
-        var index = token.prevAll().length;
-        if(index > selected_token_index) index--;
-
-        // Delete the token
-        token.remove();
-        selected_token = null;
-
-        // Show the input box and give it focus again
-        input_box.focus();
-
-        // Remove this token from the saved list
-        saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
-        if(index < selected_token_index) selected_token_index--;
-
-        // Update the hidden input
-        update_hidden_input(saved_tokens, hidden_input);
-
-        token_count -= 1;
-
-        if(settings.tokenLimit !== null) {
-            input_box
-                .show()
-                .val("")
-                .focus();
-        }
-
-        // Execute the onDelete callback if defined
-        if($.isFunction(callback)) {
-            callback.call(hidden_input,token_data);
-        }
-    }
-
-    // Update the hidden input box value
-    function update_hidden_input(saved_tokens, hidden_input) {
-        var token_values = $.map(saved_tokens, function (el) {
-            return el[settings.tokenValue];
-        });
-        hidden_input.val(token_values.join(settings.tokenDelimiter));
-
-    }
-
-    // Hide and clear the results dropdown
-    function hide_dropdown () {
-        dropdown.hide().empty();
-        selected_dropdown_item = null;
-        if (settings.onHideDropdown)
-         settings.onHideDropdown();
-    }
-
-    function show_dropdown() {
-        dropdown
-            .css({
-                position: "absolute",
-                top: $(token_list).offset().top + $(token_list).outerHeight(),
-                left: $(token_list).offset().left,
-                zindex: 999
-            })
-            .show();
-        if (settings.onShowDropdown)
-         settings.onShowDropdown();
-    }
-
-    function show_dropdown_searching () {
-        if(settings.searchingText) {
-            dropdown.html("<p>"+settings.searchingText+"</p>");
-            show_dropdown();
-        }
-    }
-
-    function show_dropdown_hint () {
-        if(settings.hintText) {
-            dropdown.html("<p>"+settings.hintText+"</p>");
-            show_dropdown();
-        }
-    }
-
-    // Highlight the query part of the search term
-    function highlight_term(value, term) {
-        return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
-    }
-    
-    function find_value_and_highlight_term(template, value, term) {
-        return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + value + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term));
-    }
-
-    // Populate the results dropdown with some results
-    function populate_dropdown (query, results) {
-        if(results && results.length) {
-            dropdown.empty();
-            var dropdown_ul = $("<ul>")
-                .appendTo(dropdown)
-                .mouseover(function (event) {
-                    select_dropdown_item($(event.target).closest("li"));
-                })
-                .mousedown(function (event) {
-                    add_token($(event.target).closest("li").data("tokeninput"));
-                    hidden_input.change();
-                    return false;
-                })
-                .hide();
-            if (settings.noHoverSelect) {
-                dropdown_ul.off('mouseover');
-                dropdown_ul.on('mouseover',function (event) {
-                    $(this).find("li").removeClass(settings.classes.selectedDropdownItem);
-                    $(event.target).closest("li").addClass(settings.classes.selectedDropdownItem);
-                });
-            }
-
-            $.each(results, function(index, value) {
-                var this_li = settings.resultsFormatter(value);
-                
-                // this_li = find_value_and_highlight_term(this_li ,value[settings.propertyToSearch], query);            
-                
-                this_li = $(this_li).appendTo(dropdown_ul);
-                
-                if(index % 2) {
-                    this_li.addClass(settings.classes.dropdownItem);
-                } else {
-                    this_li.addClass(settings.classes.dropdownItem2);
-                }
-
-                // if(index === 0) {
-                //     select_dropdown_item(this_li);
-                // }
-
-                $.data(this_li.get(0), "tokeninput", value);
-            });
-
-            show_dropdown();
-
-            if(settings.animateDropdown) {
-                dropdown_ul.slideDown("fast");
-            } else {
-                dropdown_ul.show();
-            }
-        } else {
-            if(settings.noResultsText) {
-                dropdown.html("<p>"+settings.noResultsText+"</p>");
-                show_dropdown();
-            }
-            if (settings.noResultsHideDropdown) {
-                hide_dropdown();
-            }
-        }
-    }
-
-    // Highlight an item in the results dropdown
-    function select_dropdown_item (item,withkeyboard) {
-        if(item) {
-            if(selected_dropdown_item) {
-                deselect_dropdown_item($(selected_dropdown_item));
-            }
-            if (settings.scrollKeyboard && withkeyboard) {
-                var list = $('.token-input-dropdown-tag ul');
-                var listheight = list.height();
-                var itemheight = item.outerHeight();
-                var itemtop = item.position().top;
-                if (itemtop > listheight) {
-                    var listscroll = list.scrollTop();
-                    list.scrollTop(listscroll + itemheight);
-                }
-                else if (itemtop < 0) {
-                    var listscroll = list.scrollTop();
-                    list.scrollTop(listscroll - itemheight);
-                }
-
-            }
-            item.addClass(settings.classes.selectedDropdownItem);
-            selected_dropdown_item = item.get(0);
-        }
-    }
-
-    // Remove highlighting from an item in the results dropdown
-    function deselect_dropdown_item (item) {
-        item.removeClass(settings.classes.selectedDropdownItem);
-        selected_dropdown_item = null;
-    }
-
-    // Do a search and show the "searching" dropdown if the input is longer
-    // than settings.minChars
-    function do_search() {
-        var query = input_box.val().toLowerCase();
-        if(query && query.length || settings.minChars == 0) {
-            if(selected_token) {
-                deselect_token($(selected_token), POSITION.AFTER);
-            }
-
-            if(query.length >= settings.minChars) {
-                show_dropdown_searching();
-                clearTimeout(timeout);
-
-                timeout = setTimeout(function(){
-                    run_search(query);
-                }, settings.searchDelay);
-            } else {
-                hide_dropdown();
-            }
-        }
-    }
-
-    // Do the actual search
-    function run_search(query) {
-        var cache_key = query + computeURL();
-        var cached_results = cache.get(cache_key);
-        if(cached_results) {
-            populate_dropdown(query, cached_results);
-        } else {
-            // Are we doing an ajax search or local data search?
-            if(settings.url) {
-                var url = computeURL();
-                // Extract exisiting get params
-                var ajax_params = {};
-                ajax_params.data = {};
-                if(url.indexOf("?") > -1) {
-                    var parts = url.split("?");
-                    ajax_params.url = parts[0];
-
-                    var param_array = parts[1].split("&");
-                    $.each(param_array, function (index, value) {
-                        var kv = value.split("=");
-                        ajax_params.data[kv[0]] = kv[1];
-                    });
-                } else {
-                    ajax_params.url = url;
-                }
-
-                // Prepare the request
-                ajax_params.data[settings.queryParam] = query;
-                ajax_params.type = settings.method;
-                ajax_params.dataType = settings.contentType;
-                if(settings.crossDomain) {
-                    ajax_params.dataType = "jsonp";
-                }
-
-                // Attach the success callback
-                ajax_params.success = function(results) {
-                  if($.isFunction(settings.onResult)) {
-                      results = settings.onResult.call(hidden_input, results);
-                  }
-                  cache.add(cache_key, settings.jsonContainer ? results[settings.jsonContainer] : results);
-
-                  // only populate the dropdown if the results are associated with the active search query
-                  if(input_box.val().toLowerCase() === query) {
-                      populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
-                  }
-                };
-
-                // Make the request
-                $.ajax(ajax_params);
-            } else if(settings.search_function){
-                settings.search_function(query, function(results){
-                    cache.add(cache_key, results);
-                    populate_dropdown(query, results);
-                });
-            } else if(settings.local_data) {
-                // Do the search through local data
-                var results = $.grep(settings.local_data, function (row) {
-                    return row[settings.propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1;
-                });
-
-                if($.isFunction(settings.onResult)) {
-                    results = settings.onResult.call(hidden_input, results);
-                }
-                cache.add(cache_key, results);
-                populate_dropdown(query, results);
-            }
-        }
-    }
-
-    // compute the dynamic URL
-    function computeURL() {
-        var url = settings.url;
-        if(typeof settings.url == 'function') {
-            url = settings.url.call();
-        }
-        return url;
-    }
-};
-
-// Really basic cache for the results
-$.TokenList.Cache = function (options) {
-    var settings = $.extend({
-        max_size: 500
-    }, options);
-
-    var data = {};
-    var size = 0;
-
-    var flush = function () {
-        data = {};
-        size = 0;
-    };
-
-    this.add = function (query, results) {
-        if(size > settings.max_size) {
-            flush();
-        }
-
-        if(!data[query]) {
-            size += 1;
-        }
-
-        data[query] = results;
-    };
-
-    this.get = function (query) {
-        return data[query];
-    };
-};
-}(jQuery));
+/*
+ * jQuery Plugin: Tokenizing Autocomplete Text Entry
+ * Version 1.6.0
+ *
+ * Copyright (c) 2009 James Smith (http://loopj.com)
+ * Licensed jointly under the GPL and MIT licenses,
+ * choose which one suits your project best!
+ *
+ * Licensed under MIT
+ * With modifications
+ *
+ */
+
+(function ($) {
+// Default settings
+var DEFAULT_SETTINGS = {
+ // Search settings
+    method: "GET",
+    contentType: "json",
+    queryParam: "q",
+    searchDelay: 300,
+    minChars: 1,
+    propertyToSearch: "name",
+    jsonContainer: null,
+    scrollKeyboard: false,
+
+ // Display settings
+    hintText: null,
+    noResultsText: null,
+    noResultsHideDropdown: false,
+    searchingText: null,
+    deleteText: "&times;",
+    animateDropdown: true,
+    emptyInputLength: null,
+
+ // Tokenization settings
+    tokenLimit: null,
+    tokenDelimiter: ",",
+    preventDuplicates: false,
+
+ // Output settings
+    tokenValue: "id",
+
+ // Prepopulation settings
+    prePopulate: null,
+    processPrePopulate: false,
+
+ // Manipulation settings
+    idPrefix: "token-input-",
+
+ // Formatters
+    resultsFormatter: function(item){ return "<li>" + item[this.propertyToSearch]+ "</li>" },
+    tokenFormatter: function(item) { return "<li><p>" + item[this.propertyToSearch] + "</p></li>" },
+
+    // Validations
+    validateItem: null,
+
+    // Force selections only on mouse click
+    noHoverSelect: false,
+
+ // Callbacks
+    onResult: null,
+    onAdd: null,
+    onDelete: null,
+    onReady: null
+};
+
+// Default classes to use when theming
+var DEFAULT_CLASSES = {
+    tokenList: "token-input-list",
+    token: "token-input-token",
+    tokenDelete: "token-input-delete-token",
+    selectedToken: "token-input-selected-token",
+    highlightedToken: "token-input-highlighted-token",
+    dropdown: "token-input-dropdown",
+    dropdownItem: "token-input-dropdown-item",
+    dropdownItem2: "token-input-dropdown-item2",
+    selectedDropdownItem: "token-input-selected-dropdown-item",
+    inputToken: "token-input-input-token"
+};
+
+// Input box position "enum"
+var POSITION = {
+    BEFORE: 0,
+    AFTER: 1,
+    END: 2
+};
+
+// Keys "enum"
+var KEY = {
+    BACKSPACE: 8,
+    TAB: 9,
+    ENTER: 13,
+    ESCAPE: 27,
+    SPACE: 32,
+    PAGE_UP: 33,
+    PAGE_DOWN: 34,
+    END: 35,
+    HOME: 36,
+    LEFT: 37,
+    UP: 38,
+    RIGHT: 39,
+    DOWN: 40,
+    NUMPAD_ENTER: 108,
+    COMMA: 188
+};
+
+// Additional public (exposed) methods
+var methods = {
+    init: function(url_or_data_or_function, options) {
+        var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
+
+        return this.each(function () {
+            $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings));
+        });
+    },
+    clear: function() {
+        this.data("tokenInputObject").clear();
+        return this;
+    },
+    add: function(item) {
+        this.data("tokenInputObject").add(item);
+        return this;
+    },
+    remove: function(item) {
+        this.data("tokenInputObject").remove(item);
+        return this;
+    },
+    get: function() {
+     return this.data("tokenInputObject").getTokens();
+     }
+}
+
+// Expose the .tokenInput function to jQuery as a plugin
+$.fn.tokenInput = function (method) {
+    // Method calling and initialization logic
+    if(methods[method]) {
+        return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+    } else {
+        return methods.init.apply(this, arguments);
+    }
+};
+
+// TokenList class for each input
+$.TokenList = function (input, url_or_data, settings) {
+    //
+    // Initialization
+    //
+
+    // Configure the data source
+    if($.type(url_or_data) === "string" || $.type(url_or_data) === "function") {
+        // Set the url to query against
+        settings.url = url_or_data;
+
+        // If the URL is a function, evaluate it here to do our initalization work
+        var url = computeURL();
+
+        // Make a smart guess about cross-domain if it wasn't explicitly specified
+        if(settings.crossDomain === undefined) {
+            if(url.indexOf("://") === -1) {
+                settings.crossDomain = false;
+            } else {
+                settings.crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]);
+            }
+        }
+    } else if(typeof(url_or_data) === "object") {
+        // Set the local data to search through
+        settings.local_data = url_or_data;
+    }
+
+    // Build class names
+    if(settings.classes) {
+        // Use custom class names
+        settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes);
+    } else if(settings.theme) {
+        // Use theme-suffixed default class names
+        settings.classes = {};
+        $.each(DEFAULT_CLASSES, function(key, value) {
+            settings.classes[key] = value + "-" + settings.theme;
+        });
+    } else {
+        settings.classes = DEFAULT_CLASSES;
+    }
+
+
+    // Save the tokens
+    var saved_tokens = [];
+
+    // Keep track of the number of tokens in the list
+    var token_count = 0;
+
+    // Basic cache to save on db hits
+    var cache = new $.TokenList.Cache();
+
+    // Keep track of the timeout, old vals
+    var timeout;
+    var input_val;
+
+    function tokenize(){
+        var item = $(selected_dropdown_item).data("tokeninput");
+        if(!item && settings.textToData){
+            item = settings.textToData(input_box.val());
+        }
+
+        if(item) {
+            add_token(item);
+            hidden_input.change();
+            return false;
+        }
+    }
+
+    // Create a new text input an attach keyup events
+    var input_box = $("<input type=\"text\"  autocomplete=\"off\">")
+        .css({
+            outline: "none"
+        })
+        .attr("id", settings.idPrefix + input.id)
+        .focus(function () {
+            if (settings.minChars == 0) {
+                setTimeout(function(){do_search();}, 5);
+            }
+            if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
+                show_dropdown_hint();
+            }
+        })
+        .blur(function () {
+            tokenize();
+            hide_dropdown();
+            $(this).val("");
+        })
+        .bind("keyup keydown blur update", resize_input)
+        .keydown(function (event) {
+            var previous_token;
+            var next_token;
+
+            switch(event.keyCode) {
+                case KEY.LEFT:
+                case KEY.RIGHT:
+                case KEY.UP:
+                case KEY.DOWN:
+                    if(!$(this).val()) {
+                        previous_token = input_token.prev();
+                        next_token = input_token.next();
+
+                        if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
+                            // Check if there is a previous/next token and it is selected
+                            if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
+                                deselect_token($(selected_token), POSITION.BEFORE);
+                            } else {
+                                deselect_token($(selected_token), POSITION.AFTER);
+                            }
+                        } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
+                            // We are moving left, select the previous token if it exists
+                            select_token($(previous_token.get(0)));
+                        } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
+                            // We are moving right, select the next token if it exists
+                            select_token($(next_token.get(0)));
+                        }
+                    } else {
+                        if (event.keyCode === KEY.UP || event.keyCode === KEY.DOWN) {
+                            var dropdown_item = null;
+                            if(!selected_dropdown_item && (event.keyCode === KEY.DOWN)) {
+                                dropdown_item = $('.token-input-dropdown li').first();
+                            }
+                            else if(event.keyCode === KEY.DOWN) {
+                                dropdown_item = $(selected_dropdown_item).next();
+                            } else {
+                                dropdown_item = $(selected_dropdown_item).prev();
+                            }
+
+                            if(dropdown_item.length) {
+                                select_dropdown_item(dropdown_item,true);
+                            }
+                            else if (!(event.keyCode === KEY.DOWN) && $(selected_dropdown_item).length) {
+                                deselect_dropdown_item($(selected_dropdown_item));
+                            }
+                            return false;
+                        }
+                    }
+                    break;
+
+                case KEY.BACKSPACE:
+                    previous_token = input_token.prev();
+
+                    if(!$(this).val().length) {
+                        if(selected_token) {
+                            delete_token($(selected_token));
+                            hidden_input.change();
+                        } else if(previous_token.length) {
+                            select_token($(previous_token.get(0)));
+                        }
+
+                        return false;
+                    } else if($(this).val().length === 1) {
+                        hide_dropdown();
+                    } else {
+                        // set a timeout just long enough to let this function finish.
+                        setTimeout(function(){do_search();}, 5);
+                    }
+                    break;
+
+                case KEY.TAB:
+                case KEY.ENTER:
+                case KEY.NUMPAD_ENTER:
+                case KEY.COMMA:
+                    if (event.keyCode != KEY.ENTER && event.keyCode != KEY.NUMPAD_ENTER)
+                    {
+                        event.preventDefault();
+                    }
+                    tokenize();
+                  break;
+
+                case KEY.ESCAPE:
+                  hide_dropdown();
+                  return true;
+
+                default:
+                    if(String.fromCharCode(event.which)) {
+                        // set a timeout just long enough to let this function finish.
+                        setTimeout(function(){do_search();}, 5);
+                    }
+                    break;
+            }
+        });
+
+    // Keep a reference to the original input box
+    var hidden_input = $(input)
+                           .hide()
+                           .val("")
+                           .focus(function () {
+                               input_box.focus();
+                           })
+                           .blur(function () {
+                               input_box.blur();
+                           });
+
+    // Keep a reference to the selected token and dropdown item
+    var selected_token = null;
+    var selected_token_index = 0;
+    var selected_dropdown_item = null;
+
+    // The list to store the token items in
+    var token_list = $("<ul />")
+        .addClass(settings.classes.tokenList)
+        .click(function (event) {
+            var li = $(event.target).closest("li");
+            if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
+                toggle_select_token(li);
+            } else {
+                // Deselect selected token
+                if(selected_token) {
+                    deselect_token($(selected_token), POSITION.END);
+                }
+
+                // Focus input box
+                input_box.focus();
+            }
+        })
+        .mouseover(function (event) {
+            var li = $(event.target).closest("li");
+            if(li && selected_token !== this) {
+                li.addClass(settings.classes.highlightedToken);
+            }
+        })
+        .mouseout(function (event) {
+            var li = $(event.target).closest("li");
+            if(li && selected_token !== this) {
+                li.removeClass(settings.classes.highlightedToken);
+            }
+        })
+        .insertBefore(hidden_input);
+
+    // The token holding the input box
+    var input_token = $("<li />")
+        .addClass(settings.classes.inputToken)
+        .appendTo(token_list)
+        .append(input_box);
+
+    // The list to store the dropdown items in
+    var dropdown = $("<div>")
+        .addClass(settings.classes.dropdown)
+        .appendTo("body")
+        .hide();
+
+    // Magic element to help us resize the text input
+    var input_resizer = $("<tester/>")
+        .insertAfter(input_box)
+        .css({
+            position: "absolute",
+            top: -9999,
+            left: -9999,
+            width: "auto",
+            fontSize: input_box.css("fontSize"),
+            fontFamily: input_box.css("fontFamily"),
+            fontWeight: input_box.css("fontWeight"),
+            letterSpacing: input_box.css("letterSpacing"),
+            whiteSpace: "nowrap"
+        });
+
+    // Pre-populate list if items exist
+    hidden_input.val("");
+    var li_data = settings.prePopulate || hidden_input.data("pre");
+    if(settings.processPrePopulate && $.isFunction(settings.onResult)) {
+        li_data = settings.onResult.call(hidden_input, li_data);
+    }
+    if(li_data && li_data.length) {
+        $.each(li_data, function (index, value) {
+            insert_token(value);
+            checkTokenLimit();
+        });
+    }
+
+    // Initialization is done
+    if($.isFunction(settings.onReady)) {
+        settings.onReady.call();
+        if (settings.minChars == 0)
+        {
+            setTimeout(function(){do_search();}, 5);
+        }
+    }
+
+    //
+    // Public functions
+    //
+
+    this.clear = function() {
+        token_list.children("li").each(function() {
+            if ($(this).children("input").length === 0) {
+                delete_token($(this));
+            }
+        });
+    }
+
+    this.add = function(item) {
+        add_token(item);
+    }
+
+    this.remove = function(item) {
+        token_list.children("li").each(function() {
+            if ($(this).children("input").length === 0) {
+                var currToken = $(this).data("tokeninput");
+                var match = true;
+                for (var prop in item) {
+                    if (item[prop] !== currToken[prop]) {
+                        match = false;
+                        break;
+                    }
+                }
+                if (match) {
+                    delete_token($(this));
+                }
+            }
+        });
+    }
+    
+    this.getTokens = function() {
+       return saved_tokens;
+     }
+
+    //
+    // Private functions
+    //
+
+    function checkTokenLimit() {
+        if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
+            input_box.hide();
+            hide_dropdown();
+            return;
+        }
+    }
+
+    function resize_input() {
+        if(input_val === (input_val = input_box.val())) {return;}
+
+        // Enter new content into resizer and resize input accordingly
+        var escaped = input_val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+        input_resizer.html(escaped);
+        var minwidth = 30;
+        if (settings.emptyInputLength && token_list.children().length < 2) {
+            minwidth = settings.emptyInputLength;
+        }
+        input_box.width(input_resizer.width() + minwidth);
+    }
+
+    function is_printable_character(keycode) {
+        return ((keycode >= 48 && keycode <= 90) ||     // 0-1a-z
+                (keycode >= 96 && keycode <= 111) ||    // numpad 0-9 + - / * .
+                (keycode >= 186 && keycode <= 192) ||   // ; = , - . / ^
+                (keycode >= 219 && keycode <= 222));    // ( \ ) '
+    }
+
+    // Inner function to a token to the list
+    function insert_token(item) {
+        var this_token = settings.tokenFormatter(item);
+        this_token = $(this_token)
+          .addClass(settings.classes.token)
+          .insertBefore(input_token);
+
+        // The 'delete token' button
+        $("<span>" + settings.deleteText + "</span>")
+            .addClass(settings.classes.tokenDelete)
+            .appendTo(this_token)
+            .click(function () {
+                delete_token($(this).parent());
+                hidden_input.change();
+                return false;
+            });
+
+        // Store data on the token
+        var token_data = {"id": item.id};
+        token_data[settings.propertyToSearch] = item[settings.propertyToSearch];
+        token_data.item = item;
+        $.data(this_token.get(0), "tokeninput", item);
+
+        // Save this token for duplicate checking
+        saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
+        selected_token_index++;
+
+        // Update the hidden input
+        update_hidden_input(saved_tokens, hidden_input);
+
+        token_count += 1;
+
+        // Check the token limit
+        if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
+            input_box.hide();
+            hide_dropdown();
+        }
+
+        return this_token;
+    }
+
+    // Add a token to the token list based on user input
+    function add_token (item) {
+        if(!item) return;
+
+        // Check for item validation
+        if ($.isFunction(settings.validateItem) && !settings.validateItem(item)) {
+            return false;
+        }
+
+        var callback = settings.onAdd;
+
+        // See if the token already exists and select it if we don't want duplicates
+        if(token_count > 0 && settings.preventDuplicates) {
+            var found_existing_token = null;
+            token_list.children().each(function () {
+                var existing_token = $(this);
+                var existing_data = $.data(existing_token.get(0), "tokeninput");
+                if(existing_data && existing_data.id === item.id) {
+                    found_existing_token = existing_token;
+                    return false;
+                }
+            });
+
+            if(found_existing_token) {
+                select_token(found_existing_token);
+                input_token.insertAfter(found_existing_token);
+                input_box.focus();
+                return;
+            }
+        }
+
+        // Insert the new tokens
+        if(settings.tokenLimit == null || token_count < settings.tokenLimit) {
+            insert_token(item);
+            checkTokenLimit();
+        }
+
+        // Clear input box
+        input_box.val("");
+
+        // Don't show the help dropdown, they've got the idea
+        hide_dropdown();
+
+        // Execute the onAdd callback if defined
+        if($.isFunction(callback)) {
+            callback.call(hidden_input,item);
+        }
+    }
+
+    // Select a token in the token list
+    function select_token (token) {
+        token.addClass(settings.classes.selectedToken);
+        selected_token = token.get(0);
+
+        // Hide input box
+        input_box.val("");
+
+        // Hide dropdown if it is visible (eg if we clicked to select token)
+        hide_dropdown();
+    }
+
+    // Deselect a token in the token list
+    function deselect_token (token, position) {
+        token.removeClass(settings.classes.selectedToken);
+        selected_token = null;
+
+        if(position === POSITION.BEFORE) {
+            input_token.insertBefore(token);
+            selected_token_index--;
+        } else if(position === POSITION.AFTER) {
+            input_token.insertAfter(token);
+            selected_token_index++;
+        } else {
+            input_token.appendTo(token_list);
+            selected_token_index = token_count;
+        }
+
+        // Show the input box and give it focus again
+        input_box.focus();
+    }
+
+    // Toggle selection of a token in the token list
+    function toggle_select_token(token) {
+        var previous_selected_token = selected_token;
+
+        if(selected_token) {
+            deselect_token($(selected_token), POSITION.END);
+        }
+
+        if(previous_selected_token === token.get(0)) {
+            deselect_token(token, POSITION.END);
+        } else {
+            select_token(token);
+        }
+    }
+
+    // Delete a token from the token list
+    function delete_token (token) {
+        // Remove the id from the saved list
+        var token_data = $.data(token.get(0), "tokeninput");
+        var callback = settings.onDelete;
+
+        var index = token.prevAll().length;
+        if(index > selected_token_index) index--;
+
+        // Delete the token
+        token.remove();
+        selected_token = null;
+
+        // Show the input box and give it focus again
+        input_box.focus();
+
+        // Remove this token from the saved list
+        saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
+        if(index < selected_token_index) selected_token_index--;
+
+        // Update the hidden input
+        update_hidden_input(saved_tokens, hidden_input);
+
+        token_count -= 1;
+
+        if(settings.tokenLimit !== null) {
+            input_box
+                .show()
+                .val("")
+                .focus();
+        }
+
+        // Execute the onDelete callback if defined
+        if($.isFunction(callback)) {
+            callback.call(hidden_input,token_data);
+        }
+    }
+
+    // Update the hidden input box value
+    function update_hidden_input(saved_tokens, hidden_input) {
+        var token_values = $.map(saved_tokens, function (el) {
+            return el[settings.tokenValue];
+        });
+        hidden_input.val(token_values.join(settings.tokenDelimiter));
+
+    }
+
+    // Hide and clear the results dropdown
+    function hide_dropdown () {
+        dropdown.hide().empty();
+        selected_dropdown_item = null;
+        if (settings.onHideDropdown)
+         settings.onHideDropdown();
+    }
+
+    function show_dropdown() {
+        dropdown
+            .css({
+                position: "absolute",
+                top: $(token_list).offset().top + $(token_list).outerHeight(),
+                left: $(token_list).offset().left,
+                zindex: 999
+            })
+            .show();
+        if (settings.onShowDropdown)
+         settings.onShowDropdown();
+    }
+
+    function show_dropdown_searching () {
+        if(settings.searchingText) {
+            dropdown.html("<p>"+settings.searchingText+"</p>");
+            show_dropdown();
+        }
+    }
+
+    function show_dropdown_hint () {
+        if(settings.hintText) {
+            dropdown.html("<p>"+settings.hintText+"</p>");
+            show_dropdown();
+        }
+    }
+
+    // Highlight the query part of the search term
+    function highlight_term(value, term) {
+        return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
+    }
+    
+    function find_value_and_highlight_term(template, value, term) {
+        return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + value + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term));
+    }
+
+    // Populate the results dropdown with some results
+    function populate_dropdown (query, results) {
+        if(results && results.length) {
+            dropdown.empty();
+            var dropdown_ul = $("<ul>")
+                .appendTo(dropdown)
+                .mouseover(function (event) {
+                    select_dropdown_item($(event.target).closest("li"));
+                })
+                .mousedown(function (event) {
+                    add_token($(event.target).closest("li").data("tokeninput"));
+                    hidden_input.change();
+                    return false;
+                })
+                .hide();
+            if (settings.noHoverSelect) {
+                dropdown_ul.off('mouseover');
+                dropdown_ul.on('mouseover',function (event) {
+                    $(this).find("li").removeClass(settings.classes.selectedDropdownItem);
+                    $(event.target).closest("li").addClass(settings.classes.selectedDropdownItem);
+                });
+            }
+
+            $.each(results, function(index, value) {
+                var this_li = settings.resultsFormatter(value);
+                
+                // this_li = find_value_and_highlight_term(this_li ,value[settings.propertyToSearch], query);            
+                
+                this_li = $(this_li).appendTo(dropdown_ul);
+                
+                if(index % 2) {
+                    this_li.addClass(settings.classes.dropdownItem);
+                } else {
+                    this_li.addClass(settings.classes.dropdownItem2);
+                }
+
+                // if(index === 0) {
+                //     select_dropdown_item(this_li);
+                // }
+
+                $.data(this_li.get(0), "tokeninput", value);
+            });
+
+            show_dropdown();
+
+            if(settings.animateDropdown) {
+                dropdown_ul.slideDown("fast");
+            } else {
+                dropdown_ul.show();
+            }
+        } else {
+            if(settings.noResultsText) {
+                dropdown.html("<p>"+settings.noResultsText+"</p>");
+                show_dropdown();
+            }
+            if (settings.noResultsHideDropdown) {
+                hide_dropdown();
+            }
+        }
+    }
+
+    // Highlight an item in the results dropdown
+    function select_dropdown_item (item,withkeyboard) {
+        if(item) {
+            if(selected_dropdown_item) {
+                deselect_dropdown_item($(selected_dropdown_item));
+            }
+            if (settings.scrollKeyboard && withkeyboard) {
+                var list = $('.token-input-dropdown-tag ul');
+                var listheight = list.height();
+                var itemheight = item.outerHeight();
+                var itemtop = item.position().top;
+                if (itemtop > listheight) {
+                    var listscroll = list.scrollTop();
+                    list.scrollTop(listscroll + itemheight);
+                }
+                else if (itemtop < 0) {
+                    var listscroll = list.scrollTop();
+                    list.scrollTop(listscroll - itemheight);
+                }
+
+            }
+            item.addClass(settings.classes.selectedDropdownItem);
+            selected_dropdown_item = item.get(0);
+        }
+    }
+
+    // Remove highlighting from an item in the results dropdown
+    function deselect_dropdown_item (item) {
+        item.removeClass(settings.classes.selectedDropdownItem);
+        selected_dropdown_item = null;
+    }
+
+    // Do a search and show the "searching" dropdown if the input is longer
+    // than settings.minChars
+    function do_search() {
+        var query = input_box.val().toLowerCase();
+        if(query && query.length || settings.minChars == 0) {
+            if(selected_token) {
+                deselect_token($(selected_token), POSITION.AFTER);
+            }
+
+            if(query.length >= settings.minChars) {
+                show_dropdown_searching();
+                clearTimeout(timeout);
+
+                timeout = setTimeout(function(){
+                    run_search(query);
+                }, settings.searchDelay);
+            } else {
+                hide_dropdown();
+            }
+        }
+    }
+
+    // Do the actual search
+    function run_search(query) {
+        var cache_key = query + computeURL();
+        var cached_results = cache.get(cache_key);
+        if(cached_results) {
+            populate_dropdown(query, cached_results);
+        } else {
+            // Are we doing an ajax search or local data search?
+            if(settings.url) {
+                var url = computeURL();
+                // Extract exisiting get params
+                var ajax_params = {};
+                ajax_params.data = {};
+                if(url.indexOf("?") > -1) {
+                    var parts = url.split("?");
+                    ajax_params.url = parts[0];
+
+                    var param_array = parts[1].split("&");
+                    $.each(param_array, function (index, value) {
+                        var kv = value.split("=");
+                        ajax_params.data[kv[0]] = kv[1];
+                    });
+                } else {
+                    ajax_params.url = url;
+                }
+
+                // Prepare the request
+                ajax_params.data[settings.queryParam] = query;
+                ajax_params.type = settings.method;
+                ajax_params.dataType = settings.contentType;
+                if(settings.crossDomain) {
+                    ajax_params.dataType = "jsonp";
+                }
+
+                // Attach the success callback
+                ajax_params.success = function(results) {
+                  if($.isFunction(settings.onResult)) {
+                      results = settings.onResult.call(hidden_input, results);
+                  }
+                  cache.add(cache_key, settings.jsonContainer ? results[settings.jsonContainer] : results);
+
+                  // only populate the dropdown if the results are associated with the active search query
+                  if(input_box.val().toLowerCase() === query) {
+                      populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
+                  }
+                };
+
+                // Make the request
+                $.ajax(ajax_params);
+            } else if(settings.search_function){
+                settings.search_function(query, function(results){
+                    cache.add(cache_key, results);
+                    populate_dropdown(query, results);
+                });
+            } else if(settings.local_data) {
+                // Do the search through local data
+                var results = $.grep(settings.local_data, function (row) {
+                    return row[settings.propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1;
+                });
+
+                if($.isFunction(settings.onResult)) {
+                    results = settings.onResult.call(hidden_input, results);
+                }
+                cache.add(cache_key, results);
+                populate_dropdown(query, results);
+            }
+        }
+    }
+
+    // compute the dynamic URL
+    function computeURL() {
+        var url = settings.url;
+        if(typeof settings.url == 'function') {
+            url = settings.url.call();
+        }
+        return url;
+    }
+};
+
+// Really basic cache for the results
+$.TokenList.Cache = function (options) {
+    var settings = $.extend({
+        max_size: 500
+    }, options);
+
+    var data = {};
+    var size = 0;
+
+    var flush = function () {
+        data = {};
+        size = 0;
+    };
+
+    this.add = function (query, results) {
+        if(size > settings.max_size) {
+            flush();
+        }
+
+        if(!data[query]) {
+            size += 1;
+        }
+
+        data[query] = results;
+    };
+
+    this.get = function (query) {
+        return data[query];
+    };
+};
+}(jQuery));