Merge m-c to inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sat, 02 Apr 2016 13:55:51 -0400
changeset 291486 f5842faffb354576520aec05920c67dd01933a1b
parent 291485 39744d63ae76320b2b6bb75e2e4472d33394cf3a (current diff)
parent 291474 55d557f4d73ee58664bdf2fa85aaab555224722e (diff)
child 291488 9adc763aae7608252bb3e9df1ef7888da4f9f2ba
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.0a1
Merge m-c to inbound. a=merge
browser/themes/shared/fxa/default-avatar.png
browser/themes/shared/fxa/default-avatar@2x.png
toolkit/content/tests/chrome/bug451286_window.xul
toolkit/content/tests/chrome/test_bug451286.xul
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -342,16 +342,17 @@ skip-if = buildapp == 'mulet'
 skip-if = toolkit == "windows" # Disabled on Windows due to frequent failures (bug 969405)
 [browser_locationBarCommand.js]
 skip-if = os == "linux" # Linux: Intermittent failures, bug 917535
 [browser_locationBarExternalLoad.js]
 [browser_menuButtonFitts.js]
 skip-if = os != "win" # The Fitts Law menu button is only supported on Windows (bug 969376)
 [browser_middleMouse_noJSPaste.js]
 [browser_minimize.js]
+[browser_misused_characters_in_strings.js]
 [browser_mixedcontent_securityflags.js]
 tags = mcb
 [browser_offlineQuotaNotification.js]
 skip-if = buildapp == 'mulet'
 [browser_feed_discovery.js]
 support-files = feed_discovery.html
 [browser_gZipOfflineChild.js]
 skip-if = buildapp == 'mulet' # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_misused_characters_in_strings.js
@@ -0,0 +1,147 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* This list allows pre-existing or 'unfixable' issues to remain, while we
+ * detect newly occurring issues in shipping files. It is a list of objects
+ * specifying conditions under which an error should be ignored.
+ *
+ * As each issue is found in the whitelist, it is removed from the list. At
+ * the end of the test, there is an assertion that all items have been
+ * removed from the whitelist, thus ensuring there are no stale entries. */
+let gWhitelist = [{
+    file: "search.properties",
+    key: "searchForSomethingWith",
+    type: "single-quote"
+  }, {
+    file: "aboutCertError.dtd",
+    key: "certerror.introPara",
+    type: "single-quote"
+  }, {
+    file: "browser.dtd",
+    key: "social.activated.description",
+    type: "single-quote"
+  }, {
+    file: "netError.dtd",
+    key: "weakCryptoAdvanced.longDesc",
+    type: "single-quote"
+  }, {
+    file: "netError.dtd",
+    key: "weakCryptoAdvanced.override",
+    type: "single-quote"
+  }, {
+    file: "phishing-afterload-warning-message.dtd",
+    key: "safeb.blocked.malwarePage.shortDesc",
+    type: "single-quote"
+  }, {
+    file: "phishing-afterload-warning-message.dtd",
+    key: "safeb.blocked.unwantedPage.shortDesc",
+    type: "single-quote"
+  }, {
+    file: "phishing-afterload-warning-message.dtd",
+    key: "safeb.blocked.phishingPage.shortDesc2",
+    type: "single-quote"
+  }, {
+    file: "phishing-afterload-warning-message.dtd",
+    key: "safeb.blocked.forbiddenPage.shortDesc2",
+    type: "single-quote"
+  }
+];
+
+var moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");
+var {generateURIsFromDirTree} = Cu.import(moduleLocation, {});
+
+/**
+ * Check if an error should be ignored due to matching one of the whitelist
+ * objects defined in gWhitelist.
+ *
+ * @param filepath The URI spec of the locale file
+ * @param key The key of the entity that is being checked
+ * @param type The type of error that has been found
+ * @return true if the error should be ignored, false otherwise.
+ */
+function ignoredError(filepath, key, type) {
+  for (let index in gWhitelist) {
+    let whitelistItem = gWhitelist[index];
+    if (filepath.endsWith(whitelistItem.file) &&
+        key == whitelistItem.key &&
+        type == whitelistItem.type) {
+      gWhitelist.splice(index, 1);
+      return true;
+    }
+  }
+  return false;
+}
+
+function fetchFile(uri) {
+  return new Promise((resolve, reject) => {
+    let xhr = new XMLHttpRequest();
+    xhr.open("GET", uri, true);
+    xhr.onreadystatechange = function() {
+      if (this.readyState != this.DONE) {
+        return;
+      }
+      try {
+        resolve(this.responseText);
+      } catch (ex) {
+        ok(false, `Script error reading ${uri}: ${ex}`);
+        resolve("");
+      }
+    };
+    xhr.onerror = error => {
+      ok(false, `XHR error reading ${uri}: ${error}`);
+      resolve("");
+    };
+    xhr.send(null);
+  });
+}
+
+function testForError(filepath, key, str, pattern, type, helpText) {
+  if (str.match(pattern) &&
+      !ignoredError(filepath, key, type)) {
+    ok(false, `${filepath} with key=${key} has a misused ${type}. ${helpText}`);
+  }
+}
+
+function testForErrors(filepath, key, str) {
+  testForError(filepath, key, str, /\w'\w/, "apostrophe", "Strings with apostrophes should use foo\u2019s instead of foo's.");
+  testForError(filepath, key, str, /\w\u2018\w/, "incorrect-apostrophe", "Strings with apostrophes should use foo\u2019s instead of foo\u2018s.");
+  testForError(filepath, key, str, /'.+'/, "single-quote", "Single-quoted strings should use Unicode \u2018foo\u2019 instead of 'foo'.");
+  testForError(filepath, key, str, /"/, "double-quote", "Double-quoted strings should use Unicode \u201cfoo\u201d instead of \"foo\".");
+  testForError(filepath, key, str, /\.\.\./, "ellipsis", "Strings with an ellipsis should use the Unicode \u2026 character instead of three periods.");
+}
+
+add_task(function* checkAllTheProperties() {
+  let appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
+  // This asynchronously produces a list of URLs (sadly, mostly sync on our
+  // test infrastructure because it runs against jarfiles there, and
+  // our zipreader APIs are all sync)
+  let uris = yield generateURIsFromDirTree(appDir, [".properties"]);
+  ok(uris.length, `Found ${uris.length} .properties files to scan for misused characters`);
+
+  for (let uri of uris) {
+    let bundle = new StringBundle(uri.spec);
+    let entities = bundle.getAll();
+    for (let entity of entities) {
+      testForErrors(uri.spec, entity.key, entity.value);
+    }
+  }
+});
+
+add_task(function* checkAllTheDTDs() {
+  let appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
+  let uris = yield generateURIsFromDirTree(appDir, [".dtd"]);
+  ok(uris.length, `Found ${uris.length} .dtd files to scan for misused characters`);
+
+  for (let uri of uris) {
+    let rawContents = yield fetchFile(uri.spec);
+    let entities = rawContents.match(/ENTITY\s+([\w\.]*)\s+["'](.*)["']/g);
+    for (let entity of entities) {
+      let [, key, str] = entity.match(/ENTITY\s+([\w\.]*)\s+["'](.*)["']/);
+      testForErrors(uri.spec, key, str);
+    }
+  }
+});
+
+add_task(function* ensureWhiteListIsEmpty() {
+  is(gWhitelist.length, 0, "No remaining whitelist entries exist");
+});
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -60,16 +60,17 @@ var gDraggingInToolbars;
 var gTab;
 
 function closeGlobalTab() {
   let win = gTab.ownerGlobal;
   if (win.gBrowser.browsers.length == 1) {
     win.BrowserOpenTab();
   }
   win.gBrowser.removeTab(gTab);
+  gTab = null;
 }
 
 function unregisterGlobalTab() {
   gTab.removeEventListener("TabClose", unregisterGlobalTab);
   gTab.ownerGlobal.removeEventListener("unload", unregisterGlobalTab);
   gTab.removeAttribute("customizemode");
   gTab = null;
 }
@@ -205,17 +206,20 @@ CustomizeMode.prototype = {
     if (!gTab) {
       this.setTab(this.browser.loadOneTab("about:blank",
                                           { inBackground: false,
                                             forceNotRemote: true,
                                             skipAnimation: true }));
       return;
     }
     if (!gTab.selected) {
+      // This will force another .enter() to be called via the
+      // onlocationchange handler of the tabbrowser, so we return early.
       gTab.ownerGlobal.gBrowser.selectedTab = gTab;
+      return;
     }
     gTab.ownerGlobal.focus();
     if (gTab.ownerDocument != this.document) {
       return;
     }
 
     let window = this.window;
     let document = this.document;
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -142,8 +142,9 @@ tags = fullscreen
 skip-if = os == "mac"
 [browser_1087303_button_preferences.js]
 [browser_1089591_still_customizable_after_reset.js]
 [browser_1096763_seen_widgets_post_reset.js]
 [browser_1161838_inserted_new_default_buttons.js]
 [browser_bootstrapped_custom_toolbar.js]
 [browser_customizemode_contextmenu_menubuttonstate.js]
 [browser_panel_toggle.js]
+[browser_switch_to_customize_mode.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_switch_to_customize_mode.js
@@ -0,0 +1,34 @@
+"use strict";
+
+add_task(function*() {
+  yield startCustomizing();
+  is(gBrowser.tabs.length, 2, "Should have 2 tabs");
+
+  let paletteKidCount = document.getElementById("customization-palette").childElementCount;
+  let nonCustomizingTab = gBrowser.tabContainer.querySelector("tab:not([customizemode=true])");
+  let finishedCustomizing = BrowserTestUtils.waitForEvent(gNavToolbox, "aftercustomization");
+  yield BrowserTestUtils.switchTab(gBrowser, nonCustomizingTab);
+  yield finishedCustomizing;
+
+  let startedCount = 0;
+  let handler = e => startedCount++;
+  gNavToolbox.addEventListener("customizationstarting", handler);
+  yield startCustomizing();
+  CustomizableUI.removeWidgetFromArea("home-button");
+  yield gCustomizeMode.reset().catch(e => {
+    ok(false, "Threw an exception trying to reset after making modifications in customize mode: " + e);
+  });
+
+  let newKidCount = document.getElementById("customization-palette").childElementCount;
+  is(newKidCount, paletteKidCount, "Should have just as many items in the palette as before.");
+  yield endCustomizing();
+  is(startedCount, 1, "Should have only started once");
+  gNavToolbox.removeEventListener("customizationstarting", handler);
+  let customizableToolbars = document.querySelectorAll("toolbar[customizable=true]:not([autohide=true])");
+  for (let toolbar of customizableToolbars) {
+    ok(!toolbar.hasAttribute("customizing"), "Toolbar " + toolbar.id + " is no longer customizing");
+  }
+  let menuitem = document.getElementById("PanelUI-customize");
+  isnot(menuitem.getAttribute("label"), menuitem.getAttribute("exitLabel"), "Should have exited successfully");
+});
+
--- a/browser/components/customizableui/test/head.js
+++ b/browser/components/customizableui/test/head.js
@@ -186,37 +186,17 @@ function endCustomizing(aWindow=window) 
   function onCustomizationEnds() {
     Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", false);
     aWindow.gNavToolbox.removeEventListener("aftercustomization", onCustomizationEnds);
     deferredEndCustomizing.resolve();
   }
   aWindow.gNavToolbox.addEventListener("aftercustomization", onCustomizationEnds);
   aWindow.gCustomizeMode.exit();
 
-  return deferredEndCustomizing.promise.then(function() {
-    let deferredLoadNewTab = Promise.defer();
-
-    //XXXgijs so some tests depend on this tab being about:blank. Make it so.
-    let newTabBrowser = aWindow.gBrowser.selectedBrowser;
-    newTabBrowser.stop();
-
-    // If we stop early enough, this might actually be about:blank.
-    if (newTabBrowser.currentURI.spec == "about:blank") {
-      return null;
-    }
-
-    // Otherwise, make it be about:blank, and wait for that to be done.
-    function onNewTabLoaded(e) {
-      newTabBrowser.removeEventListener("load", onNewTabLoaded, true);
-      deferredLoadNewTab.resolve();
-    }
-    newTabBrowser.addEventListener("load", onNewTabLoaded, true);
-    newTabBrowser.loadURI("about:blank");
-    return deferredLoadNewTab.promise;
-  });
+  return deferredEndCustomizing.promise;
 }
 
 function startCustomizing(aWindow=window) {
   if (aWindow.document.documentElement.getAttribute("customizing") == "true") {
     return null;
   }
   Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", true);
   let deferred = Promise.defer();
--- a/browser/components/migration/MSMigrationUtils.jsm
+++ b/browser/components/migration/MSMigrationUtils.jsm
@@ -705,18 +705,22 @@ function getTypedURLs(registryKeyPath) {
           }
         }
       }
       typedURLs.set(url, timeTyped);
     }
   } catch (ex) {
     Cu.reportError("Error reading typed URL history: " + ex);
   } finally {
-    typedURLKey.close();
-    typedURLTimeKey.close();
+    if (typedURLKey) {
+      typedURLKey.close();
+    }
+    if (typedURLTimeKey) {
+      typedURLTimeKey.close();
+    }
     cTypes.finalize();
   }
   return typedURLs;
 }
 
 
 // Migrator for form passwords on Windows 8 and higher.
 function WindowsVaultFormPasswords () {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1789,17 +1789,17 @@ BrowserGlue.prototype = {
     var notifyBox = win.gBrowser.getNotificationBox();
     var notification = notifyBox.appendNotification(text, title, null,
                                                     notifyBox.PRIORITY_CRITICAL_MEDIUM,
                                                     buttons);
     notification.persistence = -1; // Until user closes it
   },
 
   _migrateUI: function BG__migrateUI() {
-    const UI_VERSION = 36;
+    const UI_VERSION = 37;
     const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
 
     let currentUIVersion;
     if (Services.prefs.prefHasUserValue("browser.migration.version")) {
       currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
     } else {
       // This is a new profile, nothing to migrate.
       Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
@@ -2151,16 +2151,20 @@ BrowserGlue.prototype = {
     }
 
     if (currentUIVersion < 36) {
       xulStore.removeValue("chrome://passwordmgr/content/passwordManager.xul",
                            "passwordCol",
                            "hidden");
     }
 
+    if (currentUIVersion < 37) {
+      Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
+    }
+
     // Update the migration version.
     Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
   },
 
   _hasExistingNotificationPermission: function BG__hasExistingNotificationPermission() {
     let enumerator = Services.perms.enumerator;
     while (enumerator.hasMoreElements()) {
       let permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -369,37 +369,42 @@ var gSyncPane = {
         // If the account is verified the next promise in the chain will
         // fetch profile data.
         return data.verified;
       }).then(isVerified => {
         if (isVerified) {
           return fxAccounts.getSignedInUserProfile();
         }
       }).then(data => {
+        let fxaLoginStatus = document.getElementById("fxaLoginStatus");
         if (data && profileInfoEnabled) {
           if (data.displayName) {
-            fxaEmailAddress1Label.hidden = true;
+            fxaLoginStatus.setAttribute("hasName", true);
             displayNameLabel.hidden = false;
             displayNameLabel.textContent = data.displayName;
+          } else {
+            fxaLoginStatus.removeAttribute("hasName");
           }
           if (data.avatar) {
             let bgImage = "url(\"" + data.avatar + "\")";
             let profileImageElement = document.getElementById("fxaProfileImage");
             profileImageElement.style.listStyleImage = bgImage;
 
             let img = new Image();
             img.onerror = () => {
               // Clear the image if it has trouble loading. Since this callback is asynchronous
               // we check to make sure the image is still the same before we clear it.
               if (profileImageElement.style.listStyleImage === bgImage) {
                 profileImageElement.style.removeProperty("list-style-image");
               }
             };
             img.src = data.avatar;
           }
+        } else {
+          fxaLoginStatus.removeAttribute("hasName");
         }
       }, err => {
         FxAccountsCommon.log.error(err);
       }).catch(err => {
         // If we get here something's really busted
         Cu.reportError(String(err));
       });
 
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -228,26 +228,26 @@
     <hbox>
       <vbox id="fxaContentWrapper">
         <groupbox id="fxaGroup">
           <caption><label>&syncBrand.fxAccount.label;</label></caption>
           <deck id="fxaLoginStatus">
 
             <!-- logged in and verified and all is good -->
             <hbox id="fxaLoginVerified" class="fxaAccountBox">
-              <vbox>
+              <vbox align="center" pack="center">
                 <image id="fxaProfileImage" class="actionable"
                     role="button"
                     onclick="gSyncPane.openChangeProfileImage(event);" hidden="true"
                     onkeypress="gSyncPane.openChangeProfileImage(event);"
                     tooltiptext="&profilePicture.tooltip;"/>
               </vbox>
-              <vbox flex="1">
+              <vbox flex="1" pack="center">
+                <label id="fxaDisplayName" hidden="true"/>
                 <label id="fxaEmailAddress1"/>
-                <label id="fxaDisplayName" hidden="true"/>
                 <hbox class="fxaAccountBoxButtons" align="center">
                   <button id="fxaUnlinkButton" label="&disconnect.label;"/>
                   <label id="verifiedManage" class="text-link"
                          onclick="gSyncPane.openManageFirefoxAccount(event);"
                          onkeypress="gSyncPane.openManageFirefoxAccount(event);"><!--
                   -->&verifiedManage.label;</label>
                 </hbox>
               </vbox>
--- a/browser/components/search/test/browser_contextmenu.js
+++ b/browser/components/search/test/browser_contextmenu.js
@@ -71,17 +71,17 @@ add_task(function* () {
 
   let popupPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
   BrowserTestUtils.synthesizeMouseAtCenter("body", eventDetails, gBrowser.selectedBrowser);
   yield popupPromise;
 
   info("checkContextMenu");
   var searchItem = contextMenu.getElementsByAttribute("id", "context-searchselect")[0];
   ok(searchItem, "Got search context menu item");
-  is(searchItem.label, 'Search ' + ENGINE_NAME + ' for "test search"', "Check context menu label");
+  is(searchItem.label, 'Search ' + ENGINE_NAME + ' for \u201ctest search\u201d', "Check context menu label");
   is(searchItem.disabled, false, "Check that search context menu item is enabled");
 
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     searchItem.click();
   });
 
   is(gBrowser.currentURI.spec,
      "http://mochi.test:8888/browser/browser/components/search/test/?test=test+search&ie=utf-8&channel=contextsearch",
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -45,16 +45,19 @@
 #undef NTDDI_VERSION
 #define NTDDI_VERSION NTDDI_WIN8
 // Needed for access to IApplicationActivationManager
 #include <shlobj.h>
 
 #include <mbstring.h>
 #include <shlwapi.h>
 
+#include <lm.h>
+#undef ACCESS_READ
+
 #ifndef MAX_BUF
 #define MAX_BUF 4096
 #endif
 
 #define REG_SUCCEEDED(val) \
   (val == ERROR_SUCCESS)
 
 #define REG_FAILED(val) \
@@ -638,18 +641,105 @@ nsWindowsShellService::LaunchControlPane
   if (SUCCEEDED(hr)) {
     hr = pAARUI->LaunchAdvancedAssociationUI(APP_REG_NAME);
     pAARUI->Release();
   }
   return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
+nsWindowsShellService::LaunchControlPanelDefaultPrograms()
+{
+  // Default Programs is a Vista+ feature
+  if (!IsVistaOrLater()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Build the path control.exe path safely
+  WCHAR controlEXEPath[MAX_PATH + 1] = { '\0' };
+  if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) {
+    return NS_ERROR_FAILURE;
+  }
+  LPCWSTR controlEXE = L"control.exe";
+  if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) {
+    return NS_ERROR_FAILURE;
+  }
+  if (!PathAppendW(controlEXEPath, controlEXE)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  WCHAR params[] = L"control.exe /name Microsoft.DefaultPrograms /page pageDefaultProgram";
+  STARTUPINFOW si = {sizeof(si), 0};
+  si.dwFlags = STARTF_USESHOWWINDOW;
+  si.wShowWindow = SW_SHOWDEFAULT;
+  PROCESS_INFORMATION pi = {0};
+  if (!CreateProcessW(controlEXEPath, params, nullptr, nullptr, FALSE,
+                      0, nullptr, nullptr, &si, &pi)) {
+    return NS_ERROR_FAILURE;
+  }
+  CloseHandle(pi.hProcess);
+  CloseHandle(pi.hThread);
+
+  return NS_OK;
+}
+
+static bool
+IsWindowsLogonConnected()
+{
+  WCHAR userName[UNLEN + 1];
+  DWORD size = ArrayLength(userName);
+  if (!GetUserNameW(userName, &size)) {
+    return false;
+  }
+
+  LPUSER_INFO_24 info;
+  if (NetUserGetInfo(nullptr, userName, 24, (LPBYTE *)&info)
+      != NERR_Success) {
+    return false;
+  }
+  bool connected = info->usri24_internet_identity;
+  NetApiBufferFree(info);
+
+  return connected;
+}
+
+static bool
+SettingsAppBelievesConnected()
+{
+  nsresult rv;
+  nsCOMPtr<nsIWindowsRegKey> regKey =
+    do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+                    NS_LITERAL_STRING("SOFTWARE\\Microsoft\\Windows\\Shell\\Associations"),
+                    nsIWindowsRegKey::ACCESS_READ);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  uint32_t value;
+  rv = regKey->ReadIntValue(NS_LITERAL_STRING("IsConnectedAtLogon"), &value);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  return !!value;
+}
+
+nsresult
 nsWindowsShellService::LaunchModernSettingsDialogDefaultApps()
 {
+  if (!IsWindowsLogonConnected() && SettingsAppBelievesConnected()) {
+    // Use the classic Control Panel to work around a bug of Windows 10.
+    return LaunchControlPanelDefaultPrograms();
+  }
+
   IApplicationActivationManager* pActivator;
   HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager,
                                 nullptr,
                                 CLSCTX_INPROC,
                                 IID_IApplicationActivationManager,
                                 (void**)&pActivator);
 
   if (SUCCEEDED(hr)) {
--- a/browser/components/shell/nsWindowsShellService.h
+++ b/browser/components/shell/nsWindowsShellService.h
@@ -23,14 +23,15 @@ public:
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISHELLSERVICE
   NS_DECL_NSIWINDOWSSHELLSERVICE
 
 protected:
   bool IsDefaultBrowserVista(bool aCheckAllTypes, bool* aIsDefaultBrowser);
   nsresult LaunchControlPanelDefaultsSelectionUI();
+  nsresult LaunchControlPanelDefaultPrograms();
   nsresult LaunchModernSettingsDialogDefaultApps();
   nsresult InvokeHTTPOpenAsVerb();
   nsresult LaunchHTTPHandlerPane();
 };
 
 #endif // nswindowsshellservice_h____
--- a/browser/extensions/loop/chrome/locale/en-US/loop.properties
+++ b/browser/extensions/loop/chrome/locale/en-US/loop.properties
@@ -151,17 +151,17 @@ contact_offline_title=This person is not
 ## when the call didn't go through.
 call_timeout_notification_text=Your call did not go through.
 
 ## LOCALIZATION NOTE (cancel_button):
 ## This button is displayed when a call has failed.
 cancel_button=Cancel
 rejoin_button=Rejoin Conversation
 
-cannot_start_call_session_not_ready=Can't start call, session is not ready.
+cannot_start_call_session_not_ready=Can’t start call, session is not ready.
 network_disconnected=The network connection terminated abruptly.
 connection_error_see_console_notification=Call failed; see console for details.
 no_media_failure_message=No camera or microphone found.
 ice_failure_message=Connection failed. Your firewall may be blocking calls.
 
 ## LOCALIZATION NOTE (legal_text_and_links3): In this item, don't translate the
 ## parts between {{..}} because these will be replaced with links with the labels
 ## from legal_text_tos and legal_text_privacy. clientShortname will be replaced
@@ -189,17 +189,17 @@ tour_label=Tour
 
 rooms_list_recently_browsed2=Recently browsed
 rooms_list_currently_browsing2=Currently browsing
 rooms_signout_alert=Open conversations will be closed
 room_name_untitled_page=Untitled Page
 
 ## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
 door_hanger_return=See you later! You can return to this shared session at any time through the Hello panel.
-door_hanger_prompt_name=Would you like to give it a name that's easier to remember? Current name:
+door_hanger_prompt_name=Would you like to give it a name that’s easier to remember? Current name:
 door_hanger_button=OK
 
 # Infobar strings
 
 infobar_screenshare_no_guest_message=As soon as your friend joins, they will be able to see any tab you click on.
 infobar_screenshare_browser_message2=You are sharing your tabs. Any tab you click on can be seen by your friends
 infobar_screenshare_browser_message3=You are now sharing your tabs. Your friend will see any tab you click on.
 infobar_screenshare_stop_sharing_message=You are no longer sharing your tabs
@@ -208,32 +208,32 @@ infobar_button_restart_accesskey=R
 infobar_button_stop_label2=Stop sharing
 infobar_button_stop_accesskey=S
 infobar_button_disconnect_label=Disconnect
 infobar_button_disconnect_accesskey=D
 
 # E10s not supported strings
 
 e10s_not_supported_button_label=Launch New Window
-e10s_not_supported_subheading={{brandShortname}} doesn't work in a multi-process window.
+e10s_not_supported_subheading={{brandShortname}} doesn’t work in a multi-process window.
 # 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/.
 
 ## LOCALIZATION NOTE: In this file, don't translate the part between {{..}}
 
 # Text chat strings
 chat_textbox_placeholder=Type here…
 
 ## LOCALIZATION NOTE(clientShortname2): This should not be localized and
 ## should remain "Firefox Hello" for all locales.
 clientShortname2=Firefox Hello
 
 conversation_has_ended=Your conversation has ended.
-generic_failure_message=We're having technical difficulties…
+generic_failure_message=We’re having technical difficulties…
 
 generic_failure_no_reason2=Would you like to try again?
 
 help_label=Help
 
 mute_local_audio_button_title=Mute your audio
 unmute_local_audio_button_title=Unmute your audio
 mute_local_video_button_title2=Disable video
--- a/browser/extensions/pocket/bootstrap.js
+++ b/browser/extensions/pocket/bootstrap.js
@@ -378,22 +378,18 @@ var PocketOverlay = {
                                                        this._sheetType);
     AboutSaved.register();
     AboutSignup.register();
     PocketReader.startup();
     CustomizableUI.addListener(this);
     CreatePocketWidget(reason);
     PocketContextMenu.init();
 
-    if (reason != APP_STARTUP) {
-      for (let win of allBrowserWindows()) {
-        this.setWindowScripts(win);
-        this.addStyles(win);
-        this.updateWindow(win);
-      }
+    for (let win of allBrowserWindows()) {
+      this.onWindowOpened(win);
     }
   },
   shutdown: function(reason) {
     AboutSaved.unregister();
     AboutSignup.unregister();
     CustomizableUI.removeListener(this);
     for (let window of allBrowserWindows()) {
       for (let id of ["panelMenu_pocket", "menu_pocket", "BMB_pocket",
@@ -409,16 +405,18 @@ var PocketOverlay = {
       delete window.pktUI;
       delete window.pktUIMessaging;
     }
     CustomizableUI.destroyWidget("pocket-button");
     PocketContextMenu.shutdown();
     PocketReader.shutdown();
   },
   onWindowOpened: function(window) {
+    if (window.hasOwnProperty("pktUI"))
+      return;
     this.setWindowScripts(window);
     this.addStyles(window);
     this.updateWindow(window);
   },
   setWindowScripts: function(window) {
     XPCOMUtils.defineLazyModuleGetter(window, "Pocket",
                                       "chrome://pocket/content/Pocket.jsm");
     // Can't use XPCOMUtils for these because the scripts try to define the variables
--- a/browser/extensions/pocket/test/browser.ini
+++ b/browser/extensions/pocket/test/browser.ini
@@ -1,1 +1,6 @@
+[DEFAULT]
+support-files =
+  head.js
+  test.html
+
 [browser_pocket_ui_check.js]
--- a/browser/extensions/pocket/test/browser_pocket_ui_check.js
+++ b/browser/extensions/pocket/test/browser_pocket_ui_check.js
@@ -1,21 +1,54 @@
 "use strict";
 
+function checkWindowProperties(expectPresent, l) {
+  for (let name of l) {
+    is(!!window.hasOwnProperty(name), expectPresent, "property " + name + (expectPresent ? " is" : " is not") + " present");
+  }
+}
+function checkElements(expectPresent, l) {
+  for (let id of l) {
+    is(!!document.getElementById(id), expectPresent, "element " + id + (expectPresent ? " is" : " is not") + " present");
+  }
+}
+
 add_task(function*() {
-  let pocketAddon = yield new Promise(resolve => {
-    AddonManager.getAddonByID("firefox@getpocket.com", resolve);
+  let enabledOnStartup = yield promisePocketEnabled();
+  registerCleanupFunction(() => {
+    // Extra insurance that this is disabled again, but it should have been set
+    // in promisePocketReset.
+    Services.prefs.setBoolPref("extensions.pocket.enabled", enabledOnStartup);
   });
-  if (!pocketAddon) {
-    ok(true, "Pocket is not installed");
-    return;
-  }
-  if (!Services.prefs.getBoolPref("extensions.pocket.enabled")) {
-    ok(true, "Pocket add-on is not enabled");
-    return;
-  }
+
+  checkWindowProperties(true, ["Pocket", "pktUI", "pktUIMessaging"]);
+  checkElements(true, ["pocket-button", "panelMenu_pocket", "menu_pocket", "BMB_pocket",
+                       "panelMenu_pocketSeparator", "menu_pocketSeparator",
+                       "BMB_pocketSeparator"]);
+
+  // check context menu exists
+  info("checking content context menu");
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/browser/browser/extensions/pocket/test/test.html");
 
-  for (let id of ["panelMenu_pocket", "menu_pocket", "BMB_pocket",
-                  "panelMenu_pocketSeparator", "menu_pocketSeparator",
-                  "BMB_pocketSeparator"]) {
-    ok(document.getElementById(id), "Should see element with id " + id);
-  }
+  let contextMenu = document.getElementById("contentAreaContextMenu");
+  let popupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+  let popupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+  yield BrowserTestUtils.synthesizeMouseAtCenter("body", {
+    type: "contextmenu",
+    button: 2
+  }, tab.linkedBrowser);
+  yield popupShown;
+
+  checkElements(true, ["context-pocket", "context-savelinktopocket"]);
+
+  contextMenu.hidePopup();
+  yield popupHidden;
+  yield BrowserTestUtils.removeTab(tab);
+
+  yield promisePocketDisabled();
+
+  checkWindowProperties(false, ["Pocket", "pktUI", "pktUIMessaging"]);
+  checkElements(false, ["pocket-button", "panelMenu_pocket", "menu_pocket", "BMB_pocket",
+                       "panelMenu_pocketSeparator", "menu_pocketSeparator",
+                       "BMB_pocketSeparator", "context-pocket", "context-savelinktopocket"]);
+
+  yield promisePocketReset();
 });
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pocket/test/head.js
@@ -0,0 +1,68 @@
+// Currently Pocket is disabled in tests.  We want these tests to work under
+// either case that Pocket is disabled or enabled on startup of the browser,
+// and that at the end we're reset to the correct state.
+let enabledOnStartup = false;
+
+// PocketEnabled/Disabled promises return true if it was already
+// Enabled/Disabled, and false if it need to Enable/Disable.
+function promisePocketEnabled() {
+  if (Services.prefs.getPrefType("extensions.pocket.enabled") != Services.prefs.PREF_INVALID &&
+      Services.prefs.getBoolPref("extensions.pocket.enabled")) {
+    info( "pocket was already enabled, assuming enabled by default for tests");
+    enabledOnStartup = true;
+    return Promise.resolve(true);
+  }
+  info( "pocket is not enabled");
+  return new Promise((resolve, reject) => {
+    let listener = {
+      onWidgetAfterCreation(widgetid) {
+        if (widgetid == "pocket-button") {
+          info("pocket-button created");
+          CustomizableUI.removeListener(listener);
+          resolve(false);
+        }
+      }
+    }
+    CustomizableUI.addListener(listener);
+    Services.prefs.setBoolPref("extensions.pocket.enabled", true);
+  });
+}
+
+function promisePocketDisabled() {
+  if (Services.prefs.getPrefType("extensions.pocket.enabled") == Services.prefs.PREF_INVALID ||
+      !Services.prefs.getBoolPref("extensions.pocket.enabled")) {
+    info("pocket-button already disabled");
+    return Promise.resolve(true);
+  }
+  return new Promise((resolve, reject) => {
+    let listener = {
+      onWidgetDestroyed: function(widgetid) {
+        if (widgetid == "pocket-button") {
+          CustomizableUI.removeListener(listener);
+          info( "pocket-button destroyed");
+          // wait for a full unload of pocket
+          BrowserTestUtils.waitForCondition(() => {
+            return !window.hasOwnProperty("pktUI");
+          }, "pocket properties removed from window").then(() => {
+            resolve(false);
+          })
+        }
+      }
+    }
+    CustomizableUI.addListener(listener);
+    info("reset pocket enabled pref");
+    // testing/profiles/prefs_general.js uses user_pref to disable pocket, set
+    // back to false.
+    Services.prefs.setBoolPref("extensions.pocket.enabled", false);
+  });
+}
+
+function promisePocketReset() {
+  if (enabledOnStartup) {
+    info("reset is enabling pocket addon");
+    return promisePocketEnabled();
+  } else {
+    info("reset is disabling pocket addon");
+    return promisePocketDisabled();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pocket/test/test.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+    <title>Page Title</title>
+    <meta charset="utf-8" />
+</head>
+
+<body>
+</body>
+</html>
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -7,17 +7,17 @@ openFile=Open File
 
 droponhometitle=Set Home Page
 droponhomemsg=Do you want this document to be your new home page?
 
 # context menu strings
 
 # LOCALIZATION NOTE (contextMenuSearch): %1$S is the search engine,
 # %2$S is the selection string.
-contextMenuSearch=Search %1$S for "%2$S"
+contextMenuSearch=Search %1$S for “%2$S”
 contextMenuSearch.accesskey=S
 
 # bookmark dialog strings
 
 bookmarkAllTabsDefault=[Folder Name]
 
 xpinstallPromptMessage=%S prevented this site from asking you to install software on your computer.
 xpinstallPromptAllowButton=Allow
@@ -130,17 +130,17 @@ popupWarning.message=#1 prevented this s
 popupWarningButton=Options
 popupWarningButton.accesskey=O
 popupWarningButtonUnix=Preferences
 popupWarningButtonUnix.accesskey=P
 popupAllow=Allow pop-ups for %S
 popupBlock=Block pop-ups for %S
 popupWarningDontShowFromMessage=Don’t show this message when pop-ups are blocked
 popupWarningDontShowFromLocationbar=Don’t show info bar when pop-ups are blocked
-popupShowPopupPrefix=Show '%S'
+popupShowPopupPrefix=Show ‘%S’
 
 # Bad Content Blocker Doorhanger Notification
 # %S is brandShortName
 badContentBlocked.blocked.message=%S is blocking content on this page.
 badContentBlocked.notblocked.message=%S is not blocking any content on this page.
 
 crashedpluginsMessage.title=The %S plugin has crashed.
 crashedpluginsMessage.reloadButton.label=Reload page
@@ -159,40 +159,40 @@ keywordURIFixup.goTo=Yes, take me to %S
 keywordURIFixup.goTo.accesskey=Y
 keywordURIFixup.dismiss=No thanks
 keywordURIFixup.dismiss.accesskey=N
 
 ## Plugin doorhanger strings
 # LOCALIZATION NOTE (pluginActivateNew.message): Used for newly-installed
 # plugins which are not known to be unsafe. %1$S is the plugin name and %2$S
 # is the site domain.
-pluginActivateNew.message=Allow %2$S to run "%1$S"?
+pluginActivateNew.message=Allow %2$S to run “%1$S”?
 pluginActivateMultiple.message=Allow %S to run plugins?
 pluginActivate.learnMore=Learn More…
 # LOCALIZATION NOTE (pluginActivateOutdated.message, pluginActivateOutdated.label):
 # These strings are used when an unsafe plugin has an update available.
 # %1$S is the plugin name, %2$S is the domain, and %3$S is brandShortName.
-pluginActivateOutdated.message=%3$S has prevented the outdated plugin "%1$S" from running on %2$S.
+pluginActivateOutdated.message=%3$S has prevented the outdated plugin “%1$S” from running on %2$S.
 pluginActivateOutdated.label=Outdated plugin
 pluginActivate.updateLabel=Update now…
 # LOCALIZATION NOTE (pluginActivateVulnerable.message, pluginActivateVulnerable.label):
 # These strings are used when an unsafe plugin has no update available.
 # %1$S is the plugin name, %2$S is the domain, and %3$S is brandShortName.
-pluginActivateVulnerable.message=%3$S has prevented the unsafe plugin "%1$S" from running on %2$S.
+pluginActivateVulnerable.message=%3$S has prevented the unsafe plugin “%1$S” from running on %2$S.
 pluginActivateVulnerable.label=Vulnerable plugin!
 pluginActivate.riskLabel=What’s the risk?
 # LOCALIZATION NOTE (pluginActivateBlocked.message): %1$S is the plugin name, %2$S is brandShortName
-pluginActivateBlocked.message=%2$S has blocked "%1$S" for your protection.
+pluginActivateBlocked.message=%2$S has blocked “%1$S” for your protection.
 pluginActivateBlocked.label=Blocked for your protection
-pluginActivateDisabled.message="%S" is disabled.
+pluginActivateDisabled.message=“%S” is disabled.
 pluginActivateDisabled.label=Disabled
 pluginActivateDisabled.manage=Manage plugins…
-pluginEnabled.message="%S" is enabled on %S.
-pluginEnabledOutdated.message=Outdated plugin "%S" is enabled on %S.
-pluginEnabledVulnerable.message=Insecure plugin "%S" is enabled on %S.
+pluginEnabled.message=“%S” is enabled on %S.
+pluginEnabledOutdated.message=Outdated plugin “%S” is enabled on %S.
+pluginEnabledVulnerable.message=Insecure plugin “%S” is enabled on %S.
 pluginInfo.unknownPlugin=Unknown
 
 # LOCALIZATION NOTE (pluginActivateNow.label, pluginActivateAlways.label, pluginBlockNow.label): These should be the same as the matching strings in browser.dtd
 # LOCALIZATION NOTE (pluginActivateNow.label): This button will enable the
 # plugin in the current session for an short time (about an hour), auto-renewed
 # if the site keeps using the plugin.
 pluginActivateNow.label=Allow Now
 pluginActivateNow.accesskey=N
--- a/browser/locales/en-US/chrome/browser/downloads/downloads.properties
+++ b/browser/locales/en-US/chrome/browser/downloads/downloads.properties
@@ -89,17 +89,17 @@ shortTimeLeftDays=%1$Sd
 # that we use a wider space after the separator when it is followed by a number,
 # just to avoid visually confusing it with with a minus sign with some fonts.
 # If you use a different separator, this might not be necessary.  However, there
 # is usually no need to change the separator or the order of the substitutions,
 # even for right-to-left languages, unless the defaults are not suitable.
 statusSeparator=%1$S \u2014 %2$S
 statusSeparatorBeforeNumber=%1$S \u2014  %2$S
 
-fileExecutableSecurityWarning="%S" is an executable file. Executable files may contain viruses or other malicious code that could harm your computer. Use caution when opening this file. Are you sure you want to launch "%S"?
+fileExecutableSecurityWarning=“%S” is an executable file. Executable files may contain viruses or other malicious code that could harm your computer. Use caution when opening this file. Are you sure you want to launch “%S”?
 fileExecutableSecurityWarningTitle=Open Executable File?
 fileExecutableSecurityWarningDontAsk=Don’t ask me this again
 
 # LOCALIZATION NOTE (otherDownloads2):
 # This is displayed in an item at the bottom of the Downloads Panel when
 # there are more downloads than can fit in the list in the panel. Use a
 # semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/Localization_and_Plurals
--- a/browser/locales/en-US/chrome/browser/engineManager.properties
+++ b/browser/locales/en-US/chrome/browser/engineManager.properties
@@ -1,7 +1,7 @@
 # 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/.
 
 duplicateTitle=Duplicate Keyword
-duplicateEngineMsg=You have chosen a keyword that is currently in use by "%S". Please select another.
+duplicateEngineMsg=You have chosen a keyword that is currently in use by “%S”. Please select another.
 duplicateBookmarkMsg=You have chosen a keyword that is currently in use by a bookmark. Please select another.
--- a/browser/locales/en-US/chrome/browser/feeds/subscribe.properties
+++ b/browser/locales/en-US/chrome/browser/feeds/subscribe.properties
@@ -1,17 +1,17 @@
 # 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/.
 
 linkTitleTextFormat=Go to %S
-addHandler=Add "%S" (%S) as a Feed Reader?
+addHandler=Add “%S” (%S) as a Feed Reader?
 addHandlerAddButton=Add Feed Reader
 addHandlerAddButtonAccesskey=A
-handlerRegistered="%S" is already registered as a Feed Reader
+handlerRegistered=“%S” is already registered as a Feed Reader
 liveBookmarks=Live Bookmarks
 subscribeNow=Subscribe Now
 chooseApplicationMenuItem=Choose Application…
 chooseApplicationDialogTitle=Choose Application
 alwaysUse=Always use %S to subscribe to feeds
 mediaLabel=Media files
 
 # LOCALIZATION NOTE: The next string is for the size of the enclosed media.
@@ -32,19 +32,19 @@ gigabyte=GB
 alwaysUseForFeeds=Always use %S to subscribe to feeds.
 alwaysUseForAudioPodcasts=Always use %S to subscribe to podcasts.
 alwaysUseForVideoPodcasts=Always use %S to subscribe to video podcasts.
 
 subscribeFeedUsing=Subscribe to this feed using 
 subscribeAudioPodcastUsing=Subscribe to this podcast using 
 subscribeVideoPodcastUsing=Subscribe to this video podcast using 
 
-feedSubscriptionFeed1=This is a "feed" of frequently changing content on this site.
-feedSubscriptionAudioPodcast1=This is a "podcast" of frequently changing content on this site.
-feedSubscriptionVideoPodcast1=This is a "video podcast" of frequently changing content on this site.
+feedSubscriptionFeed1=This is a “feed” of frequently changing content on this site.
+feedSubscriptionAudioPodcast1=This is a “podcast” of frequently changing content on this site.
+feedSubscriptionVideoPodcast1=This is a “video podcast” of frequently changing content on this site.
 
 feedSubscriptionFeed2=You can subscribe to this feed to receive updates when this content changes.
 feedSubscriptionAudioPodcast2=You can subscribe to this podcast to receive updates when this content changes.
 feedSubscriptionVideoPodcast2=You can subscribe to this video podcast to receive updates when this content changes.
 
 # Protocol Handling
 # "Add %appName (%appDomain) as an application for %protocolType links?"
 addProtocolHandler=Add %S (%S) as an application for %S links?
--- a/browser/locales/en-US/chrome/browser/places/bookmarkProperties.properties
+++ b/browser/locales/en-US/chrome/browser/places/bookmarkProperties.properties
@@ -6,14 +6,14 @@ dialogAcceptLabelAddItem=Add
 dialogAcceptLabelSaveItem=Save
 dialogAcceptLabelAddLivemark=Subscribe
 dialogAcceptLabelAddMulti=Add Bookmarks
 dialogAcceptLabelEdit=Save
 dialogTitleAddBookmark=New Bookmark
 dialogTitleAddLivemark=Subscribe with Live Bookmark
 dialogTitleAddFolder=New Folder
 dialogTitleAddMulti=New Bookmarks
-dialogTitleEdit=Properties for "%S"
+dialogTitleEdit=Properties for “%S”
 
 bookmarkAllTabsDefault=[Folder Name]
 newFolderDefault=New Folder
 newBookmarkDefault=New Bookmark
 newLivemarkDefault=New Live Bookmark
--- a/browser/locales/en-US/chrome/browser/places/places.properties
+++ b/browser/locales/en-US/chrome/browser/places/places.properties
@@ -15,19 +15,19 @@ bookmarksRestoreTitle=Select a bookmarks
 bookmarksRestoreFilterName=JSON
 
 bookmarksRestoreFormatError=Unsupported file type.
 bookmarksRestoreParseError=Unable to process the backup file.
 
 bookmarksLivemarkLoading=Live Bookmark loading…
 bookmarksLivemarkFailed=Live Bookmark feed failed to load.
 
-menuOpenLivemarkOrigin.label=Open "%S"
+menuOpenLivemarkOrigin.label=Open “%S”
 
-sortByName=Sort '%S' by Name
+sortByName=Sort ‘%S’ by Name
 sortByNameGeneric=Sort by Name
 # LOCALIZATION NOTE (view.sortBy.1.name.label): sortBy properties are versioned.
 # When any of these changes, all of the properties must be bumped, and the
 # change must be annotated here.  Both label and accesskey must be updated.
 # - version 1: changed view.sortBy.1.date.
 view.sortBy.1.name.label=Sort by Name
 view.sortBy.1.name.accesskey=N
 view.sortBy.1.url.label=Sort by Location
--- a/browser/locales/en-US/chrome/browser/search.properties
+++ b/browser/locales/en-US/chrome/browser/search.properties
@@ -21,17 +21,17 @@ cmd_clearHistory=Clear Search History
 cmd_clearHistory_accesskey=H
 
 cmd_showSuggestions=Show Suggestions
 cmd_showSuggestions_accesskey=S
 
 # LOCALIZATION NOTE (cmd_addFoundEngine): %S is replaced by the name of
 # a search engine offered by a web page. Each engine is displayed as a
 # menuitem at the bottom of the search panel.
-cmd_addFoundEngine=Add "%S"
+cmd_addFoundEngine=Add “%S”
 # LOCALIZATION NOTE (cmd_addFoundEngineMenu): When more than 5 engines
 # are offered by a web page, instead of listing all of them in the
 # search panel using the cmd_addFoundEngine string, they will be
 # grouped in a submenu using cmd_addFoundEngineMenu as a label.
 cmd_addFoundEngineMenu=Add search engine
 
 # LOCALIZATION NOTE (searchForSomethingWith):
 # This string is used to build the header above the list of one-click
--- a/browser/locales/en-US/chrome/browser/webrtcIndicator.properties
+++ b/browser/locales/en-US/chrome/browser/webrtcIndicator.properties
@@ -18,22 +18,22 @@ webrtcIndicator.sharingWindow.tooltip = 
 webrtcIndicator.sharingBrowser.tooltip = A tab is being shared. Click to control sharing.
 
 
 # LOCALIZATION NOTE : The following strings are only used on Mac for
 # menus attached to icons near the clock on the mac menubar.
 
 # LOCALIZATION NOTE (webrtcIndicator.sharing*With.menuitem):
 # %S is the title of the tab using the share.
-webrtcIndicator.sharingCameraWith.menuitem = Sharing Camera with "%S"
-webrtcIndicator.sharingMicrophoneWith.menuitem = Sharing Microphone with "%S"
-webrtcIndicator.sharingApplicationWith.menuitem = Sharing an Application with "%S"
-webrtcIndicator.sharingScreenWith.menuitem = Sharing Screen with "%S"
-webrtcIndicator.sharingWindowWith.menuitem = Sharing a Window with "%S"
-webrtcIndicator.sharingBrowserWith.menuitem = Sharing a Tab with "%S"
+webrtcIndicator.sharingCameraWith.menuitem = Sharing Camera with “%S”
+webrtcIndicator.sharingMicrophoneWith.menuitem = Sharing Microphone with “%S”
+webrtcIndicator.sharingApplicationWith.menuitem = Sharing an Application with “%S”
+webrtcIndicator.sharingScreenWith.menuitem = Sharing Screen with “%S”
+webrtcIndicator.sharingWindowWith.menuitem = Sharing a Window with “%S”
+webrtcIndicator.sharingBrowserWith.menuitem = Sharing a Tab with “%S”
 webrtcIndicator.controlSharing.menuitem = Control Sharing
 # LOCALIZATION NOTE (webrtcIndicator.sharingCameraWithNTabs.menuitem):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 webrtcIndicator.sharingCameraWithNTabs.menuitem = Sharing Camera with #1 tab;Sharing Camera with #1 tabs
 # LOCALIZATION NOTE (webrtcIndicator.sharingMicrophoneWithNTabs.menuitem):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
@@ -53,9 +53,9 @@ webrtcIndicator.sharingWindowWithNTabs.m
 # LOCALIZATION NOTE (webrtcIndicator.sharingBrowserWithNTabs.menuitem):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # This message is shown when the contents of a tab is shared during a WebRTC
 # session, which currently is only possible with Loop/Hello.
 webrtcIndicator.sharingBrowserWithNTabs.menuitem = Sharing a Tab with #1 tab;Sharing Tabs with #1 tabs
 # LOCALIZATION NOTE (webrtcIndicator.controlSharingOn.menuitem):
 # %S is the title of the tab using the share.
-webrtcIndicator.controlSharingOn.menuitem = Control Sharing on "%S"
+webrtcIndicator.controlSharingOn.menuitem = Control Sharing on “%S”
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -165,9 +165,9 @@ text_annotation_type.alt=[{{type}} Annot
 password_label=Enter the password to open this PDF file.
 password_invalid=Invalid password. Please try again.
 password_ok=OK
 password_cancel=Cancel
 
 printing_not_supported=Warning: Printing is not fully supported by this browser.
 printing_not_ready=Warning: The PDF is not fully loaded for printing.
 web_fonts_disabled=Web fonts are disabled: unable to use embedded PDF fonts.
-document_colors_not_allowed=PDF documents are not allowed to use their own colors: 'Allow pages to choose their own colors' is deactivated in the browser.
+document_colors_not_allowed=PDF documents are not allowed to use their own colors: “Allow pages to choose their own colors” is deactivated in the browser.
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -820,17 +820,17 @@ toolbarpaletteitem[place="palette"] > to
   align-self: center;
   margin: 0px 7px;
   padding: 0px;
   border: 0px none;
   -moz-margin-end: 0;
 }
 
 #PanelUI-footer-fxa[fxaprofileimage="enabled"] > #PanelUI-fxa-status > #PanelUI-fxa-avatar {
-  list-style-image: url(chrome://browser/skin/fxa/default-avatar.png)
+  list-style-image: url(chrome://browser/skin/fxa/default-avatar.svg);
 }
 
 #PanelUI-customize:hover,
 #PanelUI-help:not([disabled]):hover,
 #PanelUI-quit:not([disabled]):hover {
   -moz-image-region: rect(0, 32px, 16px, 16px);
 }
 
@@ -1613,20 +1613,16 @@ menuitem[checked="true"].subviewbutton >
     background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted-rtl@2x.png),
                       linear-gradient(rgba(255,255,255,0.3), transparent);
   }
 
   #PanelUI-update-status {
     list-style-image: url(chrome://branding/content/icon32.png);
   }
 
-  #PanelUI-footer-fxa[fxaprofileimage="enabled"] > #PanelUI-fxa-status > #PanelUI-fxa-avatar {
-    list-style-image: url(chrome://browser/skin/fxa/default-avatar@2x.png)
-  }
-
   #PanelUI-fxa-label,
   #PanelUI-fxa-icon {
     list-style-image: url(chrome://browser/skin/sync-horizontalbar@2x.png);
   }
 
   #PanelUI-fxa-icon[syncstatus="active"] {
     list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar@2x.png);
   }
deleted file mode 100644
index 8faca35e757140d585b7e33ead3c29e870f8a1f2..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/fxa/default-avatar.svg
@@ -0,0 +1,8 @@
+<!-- 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/. -->
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="194 -104 1000 1000" width="80" height="80">
+  <path fill="#c3cfd8" d="M694-104.3c276.1 0 500 223.9 500 500s-223.9 500-500 500-500-223.9-500-500c0-276.2 223.9-500 500-500z"/>
+  <path fill="#fff" fill-rule="evenodd" clip-rule="evenodd" d="M892.4 585.9c10 3.1 19.1 5.7 27.5 8.2 34.5 10 44.8 54.6 17.5 78.1-65.4 56.5-150.7 90.8-244 90.8-92.8 0-177.6-33.8-242.9-89.8-27.4-23.5-17.3-68.2 17.4-78.3 9.2-2.7 19.2-5.5 30.2-9 62.6-19.5 92.6-43.7 98.2-68.7 0-.1 0-.2.1-.2 3.6-16.1-2.8-32.9-15.5-43.5-26.4-22.1-37.1-59.8-44.1-87.5-.8-3.2-1.7-6.5-2.5-9.8-12.1-2.1-25.4-17.3-32.2-38.5-8.2-25.5-3.9-49.8 9.6-54.1 1.3-.4 2.6-.4 3.9-.5-3.1-18.2-6.9-45.4-7.3-69.3-.1-5.2-.2-10.9-.2-16.9 0-3 .1-6.1.1-9.3 0-1.6.1-3.2.2-4.8.1-1.6.2-3.2.3-4.9.9-13.1 2.9-26.8 7-40 7.4-23.7 21.6-45.4 47.4-57.3 5.8-2.7 11-6.4 15.1-11.3 22.4-26.4 49.1-39.6 74.2-45.4 6.9-1.6 13.6-2.6 20.1-3.2 3.2-.3 6.4-.5 9.5-.6 1.6-.1 3.1-.1 4.6-.1h4.5c11.7.3 22 1.8 29.6 3.7 50 12.3 89.2 38 116.4 69.5 13.5 15.8 23.9 33 30.7 50.7 3.4 8.9 5.9 17.9 7.4 26.9.8 4.5 1.3 9 1.6 13.5.3 4.5.3 8.9.1 13.4-1.5 27.1-4.4 45.9-7.3 60.1-2.3 11.1.1 22.2 5 32.4 4.9 10.3 5.3 26.7.2 43.9-6.1 20.3-18.3 35.3-29.8 38.7-2.2 8.1-3.8 13.5-3.9 13.5-3.8 29-10.7 59.8-35.3 82.9-10.5 9.8-15 24.5-13.1 38.7.5 3.5 1 6.6 1.6 9.2 5.6 25.1 35.5 49.3 98.1 68.8z"/>
+</svg>
deleted file mode 100644
index 1428e230b32b05824f2c0e9acfc9802708eb9661..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -375,18 +375,23 @@ description > html|a {
 
 /**
  * Sync
  */
 
 #fxaProfileImage {
   max-width: 60px;
   border-radius: 50%;
-  list-style-image: url(chrome://browser/skin/fxa/default-avatar.png);
+  list-style-image: url(chrome://browser/skin/fxa/default-avatar.svg);
   margin-inline-end: 15px;
+  image-rendering: -moz-crisp-edges;
+}
+
+#fxaLoginStatus[hasName] #fxaProfileImage {
+  max-width: 80px;
 }
 
 #fxaProfileImage.actionable {
   cursor: pointer;
 }
 
 #fxaProfileImage.actionable:hover {
   box-shadow: 0px 0px 0px 1px #0095DD;
@@ -545,16 +550,20 @@ description > html|a {
   margin-right: 14px !important;
 }
 
 .fxaSyncIllustration {
   width: 231px;
   list-style-image: url(chrome://browser/skin/fxa/sync-illustration.png)
 }
 
+#fxaLoginStatus[hasName] #fxaEmailAddress1 {
+  font-size: 1.1rem;
+}
+
 #fxaEmailAddress1,
 #fxaEmailAddress2,
 #fxaEmailAddress3 {
   word-break: break-all;
 }
 
 .fxaFirefoxLogo {
   list-style-image: url(chrome://browser/skin/fxa/logo.png);
@@ -608,12 +617,9 @@ description > html|a {
     background-image: url("chrome://browser/skin/fxa/ios@2x.png");
   }
   .fxaSyncIllustration {
     list-style-image: url(chrome://browser/skin/fxa/sync-illustration@2x.png)
   }
   .fxaFirefoxLogo {
     list-style-image: url(chrome://browser/skin/fxa/logo@2x.png);
   }
-  #fxaProfileImage {
-    list-style-image: url(chrome://browser/skin/fxa/default-avatar@2x.png);
-  }
 }
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -67,18 +67,17 @@
   skin/classic/browser/tracking-protection-16.svg              (../shared/identity-block/tracking-protection-16.svg)
   skin/classic/browser/tracking-protection-disabled-16.svg     (../shared/identity-block/tracking-protection-disabled-16.svg)
   skin/classic/browser/newtab/close.png                        (../shared/newtab/close.png)
   skin/classic/browser/newtab/controls.svg                     (../shared/newtab/controls.svg)
   skin/classic/browser/newtab/whimsycorn.png                   (../shared/newtab/whimsycorn.png)
   skin/classic/browser/preferences/in-content/favicon.ico      (../shared/incontentprefs/favicon.ico)
   skin/classic/browser/preferences/in-content/icons.svg        (../shared/incontentprefs/icons.svg)
   skin/classic/browser/preferences/in-content/search.css       (../shared/incontentprefs/search.css)
-  skin/classic/browser/fxa/default-avatar.png                  (../shared/fxa/default-avatar.png)
-  skin/classic/browser/fxa/default-avatar@2x.png               (../shared/fxa/default-avatar@2x.png)
+  skin/classic/browser/fxa/default-avatar.svg                  (../shared/fxa/default-avatar.svg)
   skin/classic/browser/fxa/logo.png                            (../shared/fxa/logo.png)
   skin/classic/browser/fxa/logo@2x.png                         (../shared/fxa/logo@2x.png)
   skin/classic/browser/fxa/sync-illustration.png               (../shared/fxa/sync-illustration.png)
   skin/classic/browser/fxa/sync-illustration@2x.png            (../shared/fxa/sync-illustration@2x.png)
   skin/classic/browser/fxa/sync-illustration.svg               (../shared/fxa/sync-illustration.svg)
   skin/classic/browser/fxa/android.png                         (../shared/fxa/android.png)
   skin/classic/browser/fxa/android@2x.png                      (../shared/fxa/android@2x.png)
   skin/classic/browser/fxa/ios.png                             (../shared/fxa/ios.png)
--- a/devtools/client/commandline/test/browser_cmd_appcache_invalid.js
+++ b/devtools/client/commandline/test/browser_cmd_appcache_invalid.js
@@ -10,18 +10,18 @@ const TEST_URI = "http://sub1.test1.exam
 function test() {
   return Task.spawn(spawnTest).then(finish, helpers.handleError);
 }
 
 function* spawnTest() {
   let lines = [
     'Manifest has a character encoding of ISO-8859-1. Manifests must have the ' +
       'utf-8 character encoding.',
-    'The first line of the manifest must be "CACHE MANIFEST" at line 1.',
-    '"CACHE MANIFEST" is only valid on the first line but was found at line 3.',
+    'The first line of the manifest must be \u201cCACHE MANIFEST\u201d at line 1.',
+    '\u201cCACHE MANIFEST\u201d is only valid on the first line but was found at line 3.',
     'images/sound-icon.png points to a resource that is not available at line 9.',
     'images/background.png points to a resource that is not available at line 10.',
     '/checking.cgi points to a resource that is not available at line 13.',
     'Asterisk (*) incorrectly used in the NETWORK section at line 14. If a line ' +
       'in the NETWORK section contains only a single asterisk character, then any ' +
       'URI not listed in the manifest will be treated as if the URI was listed in ' +
       'the NETWORK section. Otherwise such URIs will be treated as unavailable. ' +
     'Other uses of the * character are prohibited',
@@ -56,17 +56,17 @@ function* spawnTest() {
     'http://example.com/check.png points to a resource that is not available at line 41.',
     'Spaces in URIs need to be replaced with % at line 42.',
     'http://example.com/cr oss.png points to a resource that is not available at line 42.',
     'Asterisk (*) incorrectly used in the CACHE section at line 43. If a line ' +
       'in the NETWORK section contains only a single asterisk character, then ' +
       'any URI not listed in the manifest will be treated as if the URI was ' +
       'listed in the NETWORK section. Otherwise such URIs will be treated as ' +
       'unavailable. Other uses of the * character are prohibited',
-    'The SETTINGS section may only contain a single value, "prefer-online" or "fast" at line 47.',
+    'The SETTINGS section may only contain a single value, \u201cprefer-online\u201d or \u201cfast\u201d at line 47.',
     'FALLBACK section line 50 (/section1/ /offline1.html) prevents caching of ' +
       'line 30 (/section1/blockedbyfallback.html) in the CACHE section.',
     '/offline1.html points to a resource that is not available at line 50.',
     'FALLBACK section line 51 (/section2/ offline2.html) prevents caching of ' +
       'line 32 (/section2/blockedbyfallback.html) in the CACHE section.',
     'offline2.html points to a resource that is not available at line 51.',
     'Only two URIs separated by spaces are allowed in the FALLBACK section at line 52.',
     'Asterisk (*) incorrectly used in the FALLBACK section at line 53. URIs ' +
--- a/devtools/client/commandline/test/browser_cmd_highlight_01.js
+++ b/devtools/client/commandline/test/browser_cmd_highlight_01.js
@@ -125,17 +125,17 @@ function* spawnTest() {
       check: {
         input:  'highlight .item',
         hints:                 ' [options]',
         markup: 'VVVVVVVVVVVVVVV',
         status: 'VALID'
       },
       exec: {
         output: '101 nodes matched, but only 100 nodes highlighted. Use ' +
-          '\'--showall\' to show all'
+          '\u2018--showall\u2019 to show all'
       }
     },
     {
       setup: 'highlight .item --showall',
       check: {
         input:  'highlight .item --showall',
         hints:                           ' [options]',
         markup: 'VVVVVVVVVVVVVVVVVVVVVVVVV',
--- a/devtools/client/commandline/test/browser_cmd_highlight_03.js
+++ b/devtools/client/commandline/test/browser_cmd_highlight_03.js
@@ -69,17 +69,17 @@ function* spawnTest() {
       setup: 'highlight body --region',
       check: {
         input:  'highlight body --region',
         hints:                         ' <selection> [options]',
         markup: 'VVVVVVVVVVVVVVVIIIIIIII',
         status: 'ERROR'
       },
       exec: {
-        output: 'Error: Value required for \'region\'.'
+        output: 'Error: Value required for \u2018region\u2019.'
       }
     },
     {
       setup: 'highlight body --fi',
       check: {
         input:  'highlight body --fi',
         hints:                     'll [options]',
         markup: 'VVVVVVVVVVVVVVVIIII',
@@ -93,17 +93,17 @@ function* spawnTest() {
       setup: 'highlight body --fill',
       check: {
         input:  'highlight body --fill',
         hints:                       ' <string> [options]',
         markup: 'VVVVVVVVVVVVVVVIIIIII',
         status: 'ERROR'
       },
       exec: {
-        output: 'Error: Value required for \'fill\'.'
+        output: 'Error: Value required for \u2018fill\u2019.'
       }
     },
     {
       setup: 'highlight body --ke',
       check: {
         input:  'highlight body --ke',
         hints:                     'ep [options]',
         markup: 'VVVVVVVVVVVVVVVIIII',
--- a/devtools/client/commandline/test/browser_cmd_pref1.js
+++ b/devtools/client/commandline/test/browser_cmd_pref1.js
@@ -100,17 +100,17 @@ function* spawnTest() {
       setup: 'pref set devtools.netmonitor.enabled 4',
       check: {
         input:  'pref set devtools.netmonitor.enabled 4',
         hints:                                  '',
         markup: 'VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVE',
         status: 'ERROR',
         args: {
           setting: { arg: ' devtools.netmonitor.enabled' },
-          value: { status: 'ERROR', message: 'Can\'t use \'4\'.' },
+          value: { status: 'ERROR', message: 'Can\u2019t use \u20184\u2019.' },
         }
       },
     },
     {
       setup: 'pref set devtools.editor.tabsize 4',
       check: {
         input:  'pref set devtools.editor.tabsize 4',
         hints:                                    '',
--- a/devtools/client/commandline/test/browser_gcli_cli1.js
+++ b/devtools/client/commandline/test/browser_gcli_cli1.js
@@ -207,23 +207,23 @@ exports.testTsv = function(options) {
         unassigned: [ ],
         tooltipState: 'true:importantFieldFlag',
         args: {
           command: { name: 'tsv' },
           optionType: {
             value: undefined,
             arg: ' o',
             status: 'INCOMPLETE',
-            message: 'Value required for \'optionType\'.'
+            message: 'Value required for \u2018optionType\u2019.'
           },
           optionValue: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'optionValue\'.'
+            message: 'Value required for \u2018optionValue\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsv option',
       check: {
         input:  'tsv option',
@@ -236,23 +236,23 @@ exports.testTsv = function(options) {
         unassigned: [ ],
         tooltipState: 'true:importantFieldFlag',
         args: {
           command: { name: 'tsv' },
           optionType: {
             value: undefined,
             arg: ' option',
             status: 'INCOMPLETE',
-            message: 'Value required for \'optionType\'.'
+            message: 'Value required for \u2018optionType\u2019.'
           },
           optionValue: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'optionValue\'.'
+            message: 'Value required for \u2018optionValue\u2019.'
           }
         }
       }
     },
     {
       name: '|tsv option',
       setup: function() {
         return helpers.setInput(options, 'tsv option', 0);
@@ -267,23 +267,23 @@ exports.testTsv = function(options) {
         predictions: [ ],
         unassigned: [ ],
         args: {
           command: { name: 'tsv' },
           optionType: {
             value: undefined,
             arg: ' option',
             status: 'INCOMPLETE',
-            message: 'Value required for \'optionType\'.'
+            message: 'Value required for \u2018optionType\u2019.'
           },
           optionValue: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'optionValue\'.'
+            message: 'Value required for \u2018optionValue\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsv option ',
       check: {
         input:  'tsv option ',
@@ -296,23 +296,23 @@ exports.testTsv = function(options) {
         unassigned: [ ],
         tooltipState: 'false:default',
         args: {
           command: { name: 'tsv' },
           optionType: {
             value: undefined,
             arg: ' option ',
             status: 'ERROR',
-            message: 'Can\'t use \'option\'.'
+            message: 'Can\u2019t use \u2018option\u2019.'
           },
           optionValue: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'optionValue\'.'
+            message: 'Value required for \u2018optionValue\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsv option1',
       check: {
         input:  'tsv option1',
@@ -331,17 +331,17 @@ exports.testTsv = function(options) {
             arg: ' option1',
             status: 'VALID',
             message: ''
           },
           optionValue: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'optionValue\'.'
+            message: 'Value required for \u2018optionValue\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsv option1 ',
       check: {
         input:  'tsv option1 ',
@@ -359,17 +359,17 @@ exports.testTsv = function(options) {
             arg: ' option1 ',
             status: 'VALID',
             message: ''
           },
           optionValue: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'optionValue\'.'
+            message: 'Value required for \u2018optionValue\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsv option2',
       check: {
         input:  'tsv option2',
@@ -388,17 +388,17 @@ exports.testTsv = function(options) {
             arg: ' option2',
             status: 'VALID',
             message: ''
           },
           optionValue: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'optionValue\'.'
+            message: 'Value required for \u2018optionValue\u2019.'
           }
         }
       }
     }
   ]);
 };
 
 exports.testTsvValues = function(options) {
--- a/devtools/client/commandline/test/browser_gcli_cli2.js
+++ b/devtools/client/commandline/test/browser_gcli_cli2.js
@@ -40,17 +40,17 @@ exports.testSingleString = function(opti
         status: 'ERROR',
         unassigned: [ ],
         args: {
           command: { name: 'tsr' },
           text: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'text\'.'
+            message: 'Value required for \u2018text\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsr ',
       check: {
         input:  'tsr ',
@@ -62,17 +62,17 @@ exports.testSingleString = function(opti
         predictions: [ ],
         unassigned: [ ],
         args: {
           command: { name: 'tsr' },
           text: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'text\'.'
+            message: 'Value required for \u2018text\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsr h',
       check: {
         input:  'tsr h',
@@ -155,17 +155,17 @@ exports.testSingleNumber = function(opti
         predictions: [ ],
         unassigned: [ ],
         args: {
           command: { name: 'tsu' },
           num: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'num\'.'
+            message: 'Value required for \u2018num\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsu ',
       check: {
         input:  'tsu ',
@@ -177,17 +177,17 @@ exports.testSingleNumber = function(opti
         predictions: [ ],
         unassigned: [ ],
         args: {
           command: { name: 'tsu' },
           num: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'num\'.'
+            message: 'Value required for \u2018num\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsu 1',
       check: {
         input:  'tsu 1',
@@ -217,17 +217,17 @@ exports.testSingleNumber = function(opti
         unassigned: [ ],
         tooltipState: 'true:isError',
         args: {
           command: { name: 'tsu' },
           num: {
             value: undefined,
             arg: ' x',
             status: 'ERROR',
-            message: 'Can\'t convert "x" to a number.'
+            message: 'Can\u2019t convert \u201cx\u201d to a number.'
           }
         }
       }
     },
     {
       setup:    'tsu 1.5',
       check: {
         input:  'tsu 1.5',
@@ -239,17 +239,17 @@ exports.testSingleNumber = function(opti
         predictions: [ ],
         unassigned: [ ],
         args: {
           command: { name: 'tsu' },
           num: {
             value: undefined,
             arg: ' 1.5',
             status: 'ERROR',
-            message: 'Can\'t convert "1.5" to an integer.'
+            message: 'Can\u2019t convert \u201c1.5\u201d to an integer.'
           }
         }
       }
     }
   ]);
 };
 
 exports.testSingleFloat = function(options) {
@@ -266,17 +266,17 @@ exports.testSingleFloat = function(optio
         error: '',
         unassigned: [ ],
         args: {
           command: { name: 'tsf' },
           num: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'num\'.'
+            message: 'Value required for \u2018num\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsf 1',
       check: {
         input:  'tsf 1',
@@ -355,26 +355,26 @@ exports.testSingleFloat = function(optio
       },
       check: {
         input:  'tsf x',
         hints:       '',
         markup: 'VVVVE',
         cursor: 4,
         current: 'num',
         status: 'ERROR',
-        error: 'Can\'t convert "x" to a number.',
+        error: 'Can\u2019t convert \u201cx\u201d to a number.',
         predictions: [ ],
         unassigned: [ ],
         args: {
           command: { name: 'tsf' },
           num: {
             value: undefined,
             arg: ' x',
             status: 'ERROR',
-            message: 'Can\'t convert "x" to a number.'
+            message: 'Can\u2019t convert \u201cx\u201d to a number.'
           }
         }
       }
     }
   ]);
 };
 
 exports.testElementWeb = function(options) {
@@ -593,17 +593,17 @@ exports.testNestedCommand = function(opt
         predictions: [ ],
         unassigned: [ ],
         args: {
           command: { name: 'tsn dif' },
           text: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'text\'.'
+            message: 'Value required for \u2018text\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsn dif ',
       check: {
         input:  'tsn dif ',
@@ -615,17 +615,17 @@ exports.testNestedCommand = function(opt
         predictions: [ ],
         unassigned: [ ],
         args: {
           command: { name: 'tsn dif' },
           text: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'text\'.'
+            message: 'Value required for \u2018text\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsn dif x',
       check: {
         input:  'tsn dif x',
@@ -654,17 +654,17 @@ exports.testNestedCommand = function(opt
         predictions: [ 'tsn ext', 'tsn exte', 'tsn exten', 'tsn extend' ],
         unassigned: [ ],
         args: {
           command: { name: 'tsn ext' },
           text: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'text\'.'
+            message: 'Value required for \u2018text\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsn exte',
       check: {
         input:  'tsn exte',
@@ -676,17 +676,17 @@ exports.testNestedCommand = function(opt
         predictions: [ 'tsn exte', 'tsn exten', 'tsn extend' ],
         unassigned: [ ],
         args: {
           command: { name: 'tsn exte' },
           text: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'text\'.'
+            message: 'Value required for \u2018text\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsn exten',
       check: {
         input:  'tsn exten',
@@ -698,17 +698,17 @@ exports.testNestedCommand = function(opt
         predictions: [ 'tsn exten', 'tsn extend' ],
         unassigned: [ ],
         args: {
           command: { name: 'tsn exten' },
           text: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'text\'.'
+            message: 'Value required for \u2018text\u2019.'
           }
         }
       }
     },
     {
       setup:    'tsn extend',
       check: {
         input:  'tsn extend',
@@ -720,17 +720,17 @@ exports.testNestedCommand = function(opt
         predictions: [ ],
         unassigned: [ ],
         args: {
           command: { name: 'tsn extend' },
           text: {
             value: undefined,
             arg: '',
             status: 'INCOMPLETE',
-            message: 'Value required for \'text\'.'
+            message: 'Value required for \u2018text\u2019.'
           }
         }
       }
     },
     {
       setup:    'ts ',
       check: {
         input:  'ts ',
--- a/devtools/client/commandline/test/browser_gcli_union.js
+++ b/devtools/client/commandline/test/browser_gcli_union.js
@@ -159,15 +159,15 @@ exports.testDefault = function(options) 
           'https://on/'
         ],
         args: {
           command: { name: 'unionc2' },
           first: {
             value: undefined,
             arg: ' on',
             status: 'INCOMPLETE',
-            message: 'Can\'t use \'on\'.'
+            message: 'Can\u2019t use \u2018on\u2019.'
           },
         }
       }
     }
   ]);
 };
--- a/devtools/client/debugger/test/mochitest/browser_dbg_cmd-break.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_cmd-break.js
@@ -144,17 +144,17 @@ function test() {
         check: {
           input:  'break del 14',
           hints:              ' -> doc_cmd-break.html:14',
           markup: 'VVVVVVVVVVII',
           status: 'ERROR',
           args: {
             breakpoint: {
               status: 'INCOMPLETE',
-              message: 'Value required for \'breakpoint\'.'
+              message: 'Value required for \u2018breakpoint\u2019.'
             }
           }
         }
       }]);
 
       yield helpers.audit(aOptions, [{
         setup: 'break del doc_cmd-break.html:14',
         check: {
--- a/devtools/client/framework/about-devtools-toolbox.js
+++ b/devtools/client/framework/about-devtools-toolbox.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // Register about:devtools-toolbox which allows to open a devtools toolbox
 // in a Firefox tab or a custom html iframe in browser.html
 
 const { Ci, Cu, Cm, components } = require("chrome");
+const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
 const Services = require("Services");
 const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
 const { nsIAboutModule } = Ci;
 
 function AboutURL() {}
 
 AboutURL.prototype = {
   uri: Services.io.newURI("chrome://devtools/content/framework/toolbox.xul",
@@ -37,24 +38,24 @@ AboutURL.prototype = {
 AboutURL.createInstance = function(outer, iid) {
   if (outer) {
     throw Cr.NS_ERROR_NO_AGGREGATION;
   }
   return new AboutURL();
 };
 
 exports.register = function () {
-  if (Cm.isCIDRegistered(AboutURL.prototype.classID)) {
+  if (registrar.isCIDRegistered(AboutURL.prototype.classID)) {
     console.error("Trying to register " + AboutURL.prototype.classDescription +
                   " more than once.");
   } else {
-    Cm.registerFactory(AboutURL.prototype.classID,
+    registrar.registerFactory(AboutURL.prototype.classID,
                        AboutURL.prototype.classDescription,
                        AboutURL.prototype.contractID,
                        AboutURL);
   }
 }
 
 exports.unregister = function () {
-  if (Cm.isCIDRegistered(AboutURL.prototype.classID)) {
-    Cm.unregisterFactory(AboutURL.prototype.classID, AboutURL);
+  if (registrar.isCIDRegistered(AboutURL.prototype.classID)) {
+    registrar.unregisterFactory(AboutURL.prototype.classID, AboutURL);
   }
 }
--- a/devtools/client/inspector/test/browser_inspector_gcli-inspect-command.js
+++ b/devtools/client/inspector/test/browser_inspector_gcli-inspect-command.js
@@ -14,17 +14,17 @@ add_task(function* () {
         setup: "inspect",
         check: {
           input:  'inspect',
           hints:         ' <selector>',
           markup: 'VVVVVVV',
           status: 'ERROR',
           args: {
             selector: {
-              message: 'Value required for \'selector\'.'
+              message: 'Value required for \u2018selector\u2019.'
             },
           }
         },
       },
       {
         setup: "inspect h1",
         check: {
           input:  'inspect h1',
--- a/devtools/client/locales/en-US/app-manager.properties
+++ b/devtools/client/locales/en-US/app-manager.properties
@@ -1,29 +1,29 @@
 # 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/.
 
-validator.nonExistingFolder=The project folder doesn't exists
+validator.nonExistingFolder=The project folder doesn’t exists
 validator.expectProjectFolder=The project folder ends up being a file
-validator.noManifestFile=A manifest file is required at project root folder, named either 'manifest.webapp' for packaged apps or 'manifest.json' for add-ons.
-validator.invalidManifestURL=Invalid manifest URL '%S'
+validator.noManifestFile=A manifest file is required at project root folder, named either ‘manifest.webapp’ for packaged apps or ‘manifest.json’ for add-ons.
+validator.invalidManifestURL=Invalid manifest URL ‘%S’
 # LOCALIZATION NOTE (validator.invalidManifestJSON, validator.noAccessManifestURL):
 # %1$S is the error message, %2$S is the URI of the manifest.
-validator.invalidManifestJSON=The webapp manifest isn't a valid JSON file: %1$S at: %2$S
+validator.invalidManifestJSON=The webapp manifest isn’t a valid JSON file: %1$S at: %2$S
 validator.noAccessManifestURL=Unable to read manifest file: %1$S at: %2$S
 # LOCALIZATION NOTE (validator.invalidHostedManifestURL): %1$S is the URI of
 # the manifest, %2$S is the error message.
-validator.invalidHostedManifestURL=Invalid hosted manifest URL '%1$S': %2$S
-validator.invalidProjectType=Unknown project type '%S'
+validator.invalidHostedManifestURL=Invalid hosted manifest URL ‘%1$S’: %2$S
+validator.invalidProjectType=Unknown project type ‘%S’
 # LOCALIZATION NOTE (validator.missNameManifestProperty, validator.missIconsManifestProperty):
 # don't translate 'icons' and 'name'.
-validator.missNameManifestProperty=Missing mandatory 'name' in Manifest.
-validator.missIconsManifestProperty=Missing 'icons' in Manifest.
+validator.missNameManifestProperty=Missing mandatory ‘name’ in Manifest.
+validator.missIconsManifestProperty=Missing ‘icons’ in Manifest.
 validator.missIconMarketplace2=app submission to the Marketplace requires a 128px icon
-validator.invalidAppType=Unknown app type: '%S'.
-validator.invalidHostedPriviledges=Hosted App can't be type '%S'.
-validator.noCertifiedSupport='certified' apps are not fully supported on the App manager.
-validator.nonAbsoluteLaunchPath=Launch path has to be an absolute path starting with '/': '%S'
-validator.accessFailedLaunchPath=Unable to access the app starting document '%S'
+validator.invalidAppType=Unknown app type: ‘%S’.
+validator.invalidHostedPriviledges=Hosted App can’t be type ‘%S’.
+validator.noCertifiedSupport=‘certified’ apps are not fully supported on the App manager.
+validator.nonAbsoluteLaunchPath=Launch path has to be an absolute path starting with ‘/’: ‘%S’
+validator.accessFailedLaunchPath=Unable to access the app starting document ‘%S’
 # LOCALIZATION NOTE (validator.accessFailedLaunchPathBadHttpCode): %1$S is the URI of
 # the launch document, %2$S is the http error code.
-validator.accessFailedLaunchPathBadHttpCode=Unable to access the app starting document '%1$S', got HTTP code %2$S
+validator.accessFailedLaunchPathBadHttpCode=Unable to access the app starting document ‘%1$S’, got HTTP code %2$S
--- a/devtools/client/locales/en-US/appcacheutils.properties
+++ b/devtools/client/locales/en-US/appcacheutils.properties
@@ -63,22 +63,22 @@ noResults=Your search returned no result
 
 # LOCALIZATION NOTE (cacheDisabled): it's used when the cache is disabled and
 # an attempt is made to view offline data.
 cacheDisabled=Your disk cache is disabled. Please set browser.cache.disk.enable to true in about:config and try again.
 
 # LOCALIZATION NOTE (firstLineMustBeCacheManifest): the associated cache
 # manifest has a first line that is not "CACHE MANIFEST". Parameters: %S is
 # the line number.
-firstLineMustBeCacheManifest=The first line of the manifest must be "CACHE MANIFEST" at line %S.
+firstLineMustBeCacheManifest=The first line of the manifest must be “CACHE MANIFEST” at line %S.
 
 # LOCALIZATION NOTE (cacheManifestOnlyFirstLine2): the associated cache
 # manifest has "CACHE MANIFEST" on a line other than the first line.
 # Parameters: %S is the line number where "CACHE MANIFEST" appears.
-cacheManifestOnlyFirstLine2="CACHE MANIFEST" is only valid on the first line but was found at line %S.
+cacheManifestOnlyFirstLine2=“CACHE MANIFEST” is only valid on the first line but was found at line %S.
 
 # LOCALIZATION NOTE (asteriskInWrongSection2): the associated cache manifest
 # has an asterisk (*) in a section other than the NETWORK section. Parameters:
 # %1$S is the section name, %2$S is the line number.
 asteriskInWrongSection2=Asterisk (*) incorrectly used in the %1$S section at line %2$S. If a line in the NETWORK section contains only a single asterisk character, then any URI not listed in the manifest will be treated as if the URI was listed in the NETWORK section. Otherwise such URIs will be treated as unavailable. Other uses of the * character are prohibited.
 
 # LOCALIZATION NOTE (escapeSpaces): the associated cache manifest has a space
 # in a URI. Spaces must be replaced with %20. Parameters: %S is the line
@@ -106,17 +106,17 @@ fallbackUseSpaces=Only two URIs separate
 # FALLBACK section that attempts to use an asterisk (*) as a wildcard. In this
 # section the URI is simply a path prefix. Parameters: %S is the line number
 # where this error occurs.
 fallbackAsterisk2=Asterisk (*) incorrectly used in the FALLBACK section at line %S. URIs in the FALLBACK section simply need to match a prefix of the request URI.
 
 # LOCALIZATION NOTE (settingsBadValue): the associated cache manifest has a
 # SETTINGS section containing something other than the valid "prefer-online"
 # or "fast". Parameters: %S is the line number where this error occurs.
-settingsBadValue=The SETTINGS section may only contain a single value, "prefer-online" or "fast" at line %S.
+settingsBadValue=The SETTINGS section may only contain a single value, “prefer-online” or “fast” at line %S.
 
 # LOCALIZATION NOTE (invalidSectionName): the associated cache manifest
 # contains an invalid section name. Parameters: %1$S is the section name, %2$S
 # is the line number.
 invalidSectionName=Invalid section name (%1$S) at line %2$S.
 
 # LOCALIZATION NOTE (entryNotFound): the requested cache entry that does not
 # exist.
--- a/devtools/client/locales/en-US/canvasdebugger.dtd
+++ b/devtools/client/locales/en-US/canvasdebugger.dtd
@@ -17,17 +17,17 @@
 
 <!-- LOCALIZATION NOTE (canvasDebuggerUI.reloadNotice2): This is the label shown
   -  along with the button that triggers a page refresh. -->
 <!ENTITY canvasDebuggerUI.reloadNotice2   "the page to be able to debug &lt;canvas&gt; contexts.">
 
 <!-- LOCALIZATION NOTE (canvasDebuggerUI.emptyNotice1/2): This is the label shown
   -  in the call list view when empty. -->
 <!ENTITY canvasDebuggerUI.emptyNotice1    "Click on the">
-<!ENTITY canvasDebuggerUI.emptyNotice2    "button to record an animation frame's call stack.">
+<!ENTITY canvasDebuggerUI.emptyNotice2    "button to record an animation frame’s call stack.">
 
 <!-- LOCALIZATION NOTE (canvasDebuggerUI.waitingNotice): This is the label shown
   -  in the call list view while recording a snapshot. -->
 <!ENTITY canvasDebuggerUI.waitingNotice   "Recording an animation cycle…">
 
 <!-- LOCALIZATION NOTE (canvasDebuggerUI.recordSnapshot): This string is displayed
   -  on a button that starts a new snapshot. -->
 <!ENTITY canvasDebuggerUI.recordSnapshot.tooltip "Record the next frame in the animation loop.">
--- a/devtools/client/locales/en-US/connection-screen.dtd
+++ b/devtools/client/locales/en-US/connection-screen.dtd
@@ -19,12 +19,12 @@
 <!ENTITY connectionError "Error:">
 <!ENTITY errorTimeout "Error: connection timeout.">
 <!ENTITY errorRefused "Error: connection refused.">
 <!ENTITY errorUnexpected "Unexpected error.">
 
 <!-- LOCALIZATION NOTE (remoteHelp, remoteDocumentation, remoteHelpSuffix):
 these strings will be concatenated in a single label, remoteDocumentation will
 be used as text for a link to MDN. -->
-<!ENTITY remoteHelp "Firefox Developer Tools can debug remote devices (Firefox for Android and Firefox OS, for example). Make sure that you have turned on the 'Remote debugging' option in the remote device. For more, see the ">
+<!ENTITY remoteHelp "Firefox Developer Tools can debug remote devices (Firefox for Android and Firefox OS, for example). Make sure that you have turned on the ‘Remote debugging’ option in the remote device. For more, see the ">
 <!ENTITY remoteDocumentation "documentation">
 <!ENTITY remoteHelpSuffix ".">
 
--- a/devtools/client/locales/en-US/filterwidget.properties
+++ b/devtools/client/locales/en-US/filterwidget.properties
@@ -8,17 +8,17 @@
 
 # LOCALIZATION NOTE (emptyFilterList):
 # This string is displayed when filter's list is empty
 # (no filter specified / all removed)
 emptyFilterList=No filter specified
 
 # LOCALIZATION NOTE (emptyPresetList):
 # This string is displayed when preset's list is empty
-emptyPresetList=You don't have any saved presets. \
+emptyPresetList=You don’t have any saved presets. \
 You can store filter presets by choosing a name and saving them. \
 Presets are quickly accessible and you can re-use them with ease.
 
 # LOCALIZATION NOTE (addUsingList):
 # This string is displayed under [emptyFilterList] when filter's
 # list is empty, guiding user to add a filter using the list below it
 addUsingList=Add a filter using the list below
 
--- a/devtools/client/locales/en-US/jsonview.properties
+++ b/devtools/client/locales/en-US/jsonview.properties
@@ -30,17 +30,17 @@ jsonViewer.Save=Save
 jsonViewer.Copy=Copy
 
 # LOCALIZATION NOTE (jsonViewer.PrettyPrint): Label for JSON
 # pretty print action button.
 jsonViewer.PrettyPrint=Pretty Print
 
 # LOCALIZATION NOTE (jsonViewer.reps.more): Label used in arrays
 # that have more items than displayed.
-jsonViewer.reps.more=more...
+jsonViewer.reps.more=more…
 
 # LOCALIZATION NOTE (jsonViewer.filterJSON): Label used in search box
 # at the top right cornder of the JSON Viewer.
 jsonViewer.filterJSON=Filter JSON
 
 # LOCALIZATION NOTE (jsonViewer.reps.reference): Label used for cycle
 # references in an array.
 jsonViewer.reps.reference=Cycle Reference
--- a/devtools/client/locales/en-US/memory.properties
+++ b/devtools/client/locales/en-US/memory.properties
@@ -114,17 +114,17 @@ toolbar.view=View:
 toolbar.view.tooltip=Change the view of the heap snapshot
 
 # LOCALIZATION NOTE (toolbar.view.census): The label for the census view option
 # in the toolbar.
 toolbar.view.census=Aggregate
 
 # LOCALIZATION NOTE (toolbar.view.census.tooltip): The tooltip for the label for
 # the census view option in the toolbar.
-toolbar.view.census.tooltip=View a summary of the heap snapshot's contents by aggregating objects into groups
+toolbar.view.census.tooltip=View a summary of the heap snapshot’s contents by aggregating objects into groups
 
 # LOCALIZATION NOTE (toolbar.view.dominators): The label for the dominators view
 # option in the toolbar.
 toolbar.view.dominators=Dominators
 
 # LOCALIZATION NOTE (toolbar.view.dominators.tooltip): The tooltip for the label
 # for the dominators view option in the toolbar.
 toolbar.view.dominators.tooltip=View the dominator tree and surface the largest structures in the heap snapshot
--- a/devtools/client/locales/en-US/scratchpad.properties
+++ b/devtools/client/locales/en-US/scratchpad.properties
@@ -112,14 +112,14 @@ scratchpad.panelLabel=Scratchpad Panel
 # LOCALIZATION NOTE (scratchpad.tooltip):  This string is displayed in the
 # tooltip of the tab when the Scratchpad is displayed inside the developer tools
 # window.
 scratchpad.tooltip=Scratchpad
 
 # LOCALIZATION NOTE (selfxss.msg): the text that is displayed when
 # a new user of the developer tools pastes code into the console
 # %1 is the text of selfxss.okstring
-selfxss.msg=Scam Warning: Take care when pasting things you don't understand. This could allow attackers to steal your identity or take control of your computer. Please type '%S' in the scratchpad below to allow pasting.
+selfxss.msg=Scam Warning: Take care when pasting things you don’t understand. This could allow attackers to steal your identity or take control of your computer. Please type ‘%S’ in the scratchpad below to allow pasting.
 
 # LOCALIZATION NOTE (selfxss.msg): the string to be typed
 # in by a new user of the developer tools when they receive the sefxss.msg prompt.
 # Please avoid using non-keyboard characters here
 selfxss.okstring=allow pasting
--- a/devtools/client/locales/en-US/styleeditor.properties
+++ b/devtools/client/locales/en-US/styleeditor.properties
@@ -35,17 +35,17 @@ ruleCount.label=#1 rule.;#1 rules.
 # LOCALIZATION NOTE  (error-load): This is shown when loading fails.
 error-load=Style sheet could not be loaded.
 
 # LOCALIZATION NOTE  (error-save): This is shown when saving fails.
 error-save=Style sheet could not be saved.
 
 # LOCALIZATION NOTE  (error-compressed): This is shown when we can't show
 # coverage information because the css source is compressed.
-error-compressed=Can't show coverage information for compressed stylesheets
+error-compressed=Can’t show coverage information for compressed stylesheets
 
 # LOCALIZATION NOTE  (importStyleSheet.title): This is the file picker title,
 # when you import a style sheet into the Style Editor.
 importStyleSheet.title=Import style sheet
 
 # LOCALIZATION NOTE  (importStyleSheet.filter): This is the *.css filter title
 importStyleSheet.filter=CSS files
 
--- a/devtools/client/locales/en-US/webconsole.properties
+++ b/devtools/client/locales/en-US/webconsole.properties
@@ -34,17 +34,17 @@ update.button=Update
 update.accesskey=U
 cmd.commandkey=K
 webConsoleCmd.accesskey=W
 
 # LOCALIZATION NOTE (timestampFormat): %1$02S = hours (24-hour clock),
 # %2$02S = minutes, %3$02S = seconds, %4$03S = milliseconds.
 timestampFormat=%02S:%02S:%02S.%03S
 
-helperFuncUnsupportedTypeError=Can't call pprint on this type of object.
+helperFuncUnsupportedTypeError=Can’t call pprint on this type of object.
 
 # LOCALIZATION NOTE (NetworkPanel.deltaDurationMS): this string is used to
 # show the duration between two network events (e.g request and response
 # header or response header and response body). Parameters: %S is the duration.
 NetworkPanel.durationMS=%Sms
 
 ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.
 
@@ -214,17 +214,17 @@ openNodeInInspector=Click to select the 
 
 # LOCALIZATION NOTE (cdFunctionInvalidArgument): the text that is displayed when
 # cd() is invoked with an invalid argument.
 cdFunctionInvalidArgument=Cannot cd() to the given window. Invalid argument.
 
 # LOCALIZATION NOTE (selfxss.msg): the text that is displayed when
 # a new user of the developer tools pastes code into the console
 # %1 is the text of selfxss.okstring
-selfxss.msg=Scam Warning: Take care when pasting things you don't understand. This could allow attackers to steal your identity or take control of your computer. Please type '%S' below (no need to press enter) to allow pasting.
+selfxss.msg=Scam Warning: Take care when pasting things you don’t understand. This could allow attackers to steal your identity or take control of your computer. Please type ‘%S’ below (no need to press enter) to allow pasting.
 
 # LOCALIZATION NOTE (selfxss.msg): the string to be typed
 # in by a new user of the developer tools when they receive the sefxss.msg prompt.
 # Please avoid using non-keyboard characters here
 selfxss.okstring=allow pasting
 
 # LOCALIZATION NOTE (messageToggleDetails): the text that is displayed when
 # you hover the arrow for expanding/collapsing the message details. For
--- a/devtools/client/locales/en-US/webide.dtd
+++ b/devtools/client/locales/en-US/webide.dtd
@@ -80,17 +80,17 @@
 <!ENTITY projectPanel_runtimeApps "Runtime Apps">
 <!ENTITY projectPanel_tabs "Tabs">
 <!ENTITY runtimePanel_usb "USB Devices">
 <!ENTITY runtimePanel_wifi "Wi-Fi Devices">
 <!ENTITY runtimePanel_simulator "Simulators">
 <!ENTITY runtimePanel_other "Other">
 <!ENTITY runtimePanel_installsimulator "Install Simulator">
 <!ENTITY runtimePanel_noadbhelper "Install ADB Helper">
-<!ENTITY runtimePanel_nousbdevice "Can't see your device?">
+<!ENTITY runtimePanel_nousbdevice "Can’t see your device?">
 <!ENTITY runtimePanel_refreshDevices_label "Refresh Devices">
 
 <!-- Lense -->
 <!ENTITY details_valid_header "valid">
 <!ENTITY details_warning_header "warnings">
 <!ENTITY details_error_header "errors">
 <!ENTITY details_description "Description">
 <!ENTITY details_location "Location">
--- a/devtools/client/locales/en-US/webide.properties
+++ b/devtools/client/locales/en-US/webide.properties
@@ -26,48 +26,48 @@ notification_showTroubleShooting_label=T
 notification_showTroubleShooting_accesskey=T
 
 # LOCALIZATION NOTE (project_tab_loading): This is shown as a temporary tab
 # title for browser tab projects when the tab is still loading.
 project_tab_loading=Loading…
 
 # These messages appear in a notification box when an error occur.
 
-error_cantInstallNotFullyConnected=Can't install project. Not fully connected.
-error_cantInstallValidationErrors=Can't install project. Validation errors.
-error_listRunningApps=Can't get app list from device
+error_cantInstallNotFullyConnected=Can’t install project. Not fully connected.
+error_cantInstallValidationErrors=Can’t install project. Validation errors.
+error_listRunningApps=Can’t get app list from device
 
 # Variable: name of the operation (in english)
 error_operationTimeout=Operation timed out: %1$S
 error_operationFail=Operation failed: %1$S
 
 # Variable: app name
-error_cantConnectToApp=Can't connect to app: %1$S
+error_cantConnectToApp=Can’t connect to app: %1$S
 
 # Variable: error message (in english)
-error_cantFetchAddonsJSON=Can't fetch the add-on list: %S
+error_cantFetchAddonsJSON=Can’t fetch the add-on list: %S
 
-error_appProjectsLoadFailed=Unable to load project list. This can occur if you've used this profile with a newer version of Firefox.
+error_appProjectsLoadFailed=Unable to load project list. This can occur if you’ve used this profile with a newer version of Firefox.
 error_folderCreationFailed=Unable to create project folder in the selected directory.
 
 # Variable: runtime app build ID (looks like this %Y%M%D format) and firefox build ID (same format)
 error_runtimeVersionTooRecent=The connected runtime has a more recent build date (%1$S) than your desktop Firefox (%2$S) does. This is an unsupported setup and may cause DevTools to fail. Please update Firefox.
 
 addons_stable=stable
 addons_unstable=unstable
 # LOCALIZATION NOTE (addons_simulator_label): This label is shown as the name of
 # a given simulator version in the "Manage Simulators" pane.  %1$S: Firefox OS
 # version in the simulator, ex. 1.3.  %2$S: Simulator stability label, ex.
 # "stable" or "unstable".
 addons_simulator_label=Firefox OS %1$S Simulator (%2$S)
 addons_install_button=install
 addons_uninstall_button=uninstall
 addons_adb_label=ADB Helper Add-on
 addons_adapters_label=Tools Adapters Add-on
-addons_adb_warning=USB devices won't be detected without this add-on
+addons_adb_warning=USB devices won’t be detected without this add-on
 addons_status_unknown=?
 addons_status_installed=Installed
 addons_status_uninstalled=Not Installed
 addons_status_preparing=preparing
 addons_status_downloading=downloading
 addons_status_installing=installing
 
 runtimedetails_checkno=no
--- a/devtools/client/shared/components/tree.js
+++ b/devtools/client/shared/components/tree.js
@@ -374,26 +374,26 @@ const Tree = module.exports = createClas
     }
   }),
 
   /**
    * Sets the passed in item to be the focused item.
    *
    * @param {Object} item
    */
-  _focus: function (item) {
+  _focus(item) {
     if (this.props.onFocus) {
       this.props.onFocus(item);
     }
   },
 
   /**
    * Sets the state to have no focused item.
    */
-  _onBlur: function () {
+  _onBlur() {
     this._focus(undefined);
   },
 
   /**
    * Fired on a scroll within the tree's container, updates
    * the stored position of the view port to handle virtual view rendering.
    *
    * @param {Event} e
--- a/devtools/client/styleeditor/test/browser_styleeditor_cmd_edit.js
+++ b/devtools/client/styleeditor/test/browser_styleeditor_cmd_edit.js
@@ -65,17 +65,17 @@ add_task(function* () {
         hints: "://example.com/browser/devtools/client/styleeditor/test/" +
                "resources_inpage1.css [line]",
         markup: "VVVVVIIII",
         status: "ERROR",
         args: {
           resource: {
             arg: " http",
             status: "INCOMPLETE",
-            message: "Value required for \'resource\'."
+            message: "Value required for \u2018resource\u2019."
           },
           line: { status: "VALID" },
         }
       },
     },
     {
       setup: "edit page1",
       check: {
@@ -83,17 +83,17 @@ add_task(function* () {
         hints: " [line] -> http://example.com/browser/devtools/client/" +
                "styleeditor/test/resources_inpage1.css",
         markup: "VVVVVIIIII",
         status: "ERROR",
         args: {
           resource: {
             arg: " page1",
             status: "INCOMPLETE",
-            message: "Value required for \'resource\'."
+            message: "Value required for \u2018resource\u2019."
           },
           line: { status: "VALID" },
         }
       },
     },
     {
       setup: "edit page2",
       check: {
@@ -101,33 +101,33 @@ add_task(function* () {
         hints: " [line] -> http://example.com/browser/devtools/client/" +
                "styleeditor/test/resources_inpage2.css",
         markup: "VVVVVIIIII",
         status: "ERROR",
         args: {
           resource: {
             arg: " page2",
             status: "INCOMPLETE",
-            message: "Value required for \'resource\'."
+            message: "Value required for \u2018resource\u2019."
           },
           line: { status: "VALID" },
         }
       },
     },
     {
       setup: "edit stylez",
       check: {
         input: "edit stylez",
         hints: " [line]",
         markup: "VVVVVEEEEEE",
         status: "ERROR",
         args: {
           resource: {
             arg: " stylez",
-            status: "ERROR", message: "Can\'t use \'stylez\'." },
+            status: "ERROR", message: "Can\u2019t use \u2018stylez\u2019." },
           line: { status: "VALID" },
         }
       },
     },
     {
       setup: "edit css#style2",
       check: {
         input: "edit css#style2",
--- a/devtools/shared/gcli/commands/cmd.js
+++ b/devtools/shared/gcli/commands/cmd.js
@@ -144,17 +144,17 @@ exports.items = [
       return l10n.lookupFormat("cmdStatus3", [ dirName ]);
     }
   },
   {
     item: "command",
     runAt: "client",
     name: "cmd setdir",
     description: l10n.lookup("cmdSetdirDesc"),
-    manual: l10n.lookup("cmdSetdirManual2"),
+    manual: l10n.lookup("cmdSetdirManual3"),
     params: [
       {
         name: "directory",
         description: l10n.lookup("cmdSetdirDirectoryDesc"),
         type: {
           name: "file",
           filetype: "directory",
           existing: "yes"
--- a/devtools/shared/locales/en-US/csscoverage.dtd
+++ b/devtools/shared/locales/en-US/csscoverage.dtd
@@ -26,17 +26,17 @@
 <!ENTITY csscoverage.optimize.header "Optimizable Pages">
 
 <!-- LOCALIZATION NOTE (csscoverage.preload1, csscoverage.preload2,
   -  csscoverage.preload3): These 3 are part of a paragraph with 1 and 2
   -  separated by a styled <link> tag and 2 and 3 separated by a styled
   -  <style> tag -->
 <!ENTITY csscoverage.optimize.body1 "You can sometimes speed up loading by moving">
 <!ENTITY csscoverage.optimize.body2 "tags to the bottom of the page and creating a new inline">
-<!ENTITY csscoverage.optimize.body3 "element with the styles needed before the 'load' event to the top. Here are the style blocks you need:">
+<!ENTITY csscoverage.optimize.body3 "element with the styles needed before the ‘load’ event to the top. Here are the style blocks you need:">
 
 <!-- LOCALIZATION NOTE (csscoverage.optimize.bodyX):
   -  This is what we say when we have no optimization suggestions -->
 <!ENTITY csscoverage.optimize.bodyX "All rules are inlined.">
 
 <!-- LOCALIZATION NOTE (csscoverage.footer1, csscoverage.footer2a,
   -  csscoverage.footer3, csscoverage.footer4): The text displayed at the
   -  bottom of the page, with 2a being the URL opened when the link text in 3
--- a/devtools/shared/locales/en-US/csscoverage.properties
+++ b/devtools/shared/locales/en-US/csscoverage.properties
@@ -9,24 +9,24 @@
 # csscoverageStopDesc2, csscoverageOneShotDesc2, csscoverageToggleDesc2,
 # csscoverageReportDesc2): Short descriptions of the csscoverage commands
 csscoverageDesc=Control CSS coverage analysis
 csscoverageStartDesc2=Begin collecting CSS coverage data
 csscoverageStopDesc2=Stop collecting CSS coverage data
 csscoverageOneShotDesc2=Collect instantaneous CSS coverage data
 csscoverageToggleDesc2=Toggle collecting CSS coverage data
 csscoverageReportDesc2=Show CSS coverage report
-csscoverageStartNoReloadDesc=Don't start with a page reload
-csscoverageStartNoReloadManual=It's best if we start by reloading the current page because that starts the test at a known point, but there could be reasons why we don't want to do that (e.g. the page contains state that will be lost across a reload)
+csscoverageStartNoReloadDesc=Don’t start with a page reload
+csscoverageStartNoReloadManual=It’s best if we start by reloading the current page because that starts the test at a known point, but there could be reasons why we don’t want to do that (e.g. the page contains state that will be lost across a reload)
 
 # LOCALIZATION NOTE (csscoverageRunningReply, csscoverageDoneReply): Text that
 # describes the current state of the css coverage system
 csscoverageRunningReply=Running CSS coverage analysis
 csscoverageDoneReply=CSS Coverage analysis completed
 
 # LOCALIZATION NOTE (csscoverageRunningError, csscoverageNotRunningError,
 # csscoverageNotRunError): Error message that describe things that can go wrong
 # with the css coverage system
 csscoverageRunningError=CSS coverage analysis already running
 csscoverageNotRunningError=CSS coverage analysis not running
 csscoverageNotRunError=CSS coverage analysis has not been run
 csscoverageNoRemoteError=Target does not support CSS Coverage
-csscoverageOneShotReportError=CSS coverage report is not available for 'oneshot' data. Please use start/stop.
+csscoverageOneShotReportError=CSS coverage report is not available for ‘oneshot’ data. Please use start/stop.
--- a/devtools/shared/locales/en-US/gcli.properties
+++ b/devtools/shared/locales/en-US/gcli.properties
@@ -30,103 +30,103 @@ canonDefaultGroupName=Options
 # LOCALIZATION NOTE (canonProxyDesc, canonProxyManual): These commands are
 # used to execute commands on a remote system (using a proxy). Parameters: %S
 # is the name of the remote system.
 canonProxyDesc=Execute a command on %S
 canonProxyManual=A set of commands that are executed on a remote system. The remote system is reached via %S
 
 # LOCALIZATION NOTE: This error message is displayed when we try to add a new
 # command (using a proxy) where one already exists with the same name.
-canonProxyExists=There is already a command called '%S'
+canonProxyExists=There is already a command called ‘%S’
 
 # LOCALIZATION NOTE: This message describes the '{' command, which allows
 # entry of JavaScript like traditional developer tool command lines.
 cliEvalJavascript=Enter JavaScript directly
 
 # LOCALIZATION NOTE: This message is displayed when the command line has more
 # arguments than the current command can understand.
 cliUnusedArg=Too many arguments
 
 # LOCALIZATION NOTE: The title of the dialog which displays the options that
 # are available to the current command.
 cliOptions=Available Options
 
 # LOCALIZATION NOTE: The error message when the user types a command that
 # isn't registered
-cliUnknownCommand2=Invalid Command: '%1$S'.
+cliUnknownCommand2=Invalid Command: ‘%1$S’.
 
 # LOCALIZATION NOTE: A parameter should have a value, but doesn't
-cliIncompleteParam=Value required for '%1$S'.
+cliIncompleteParam=Value required for ‘%1$S’.
 
 # LOCALIZATION NOTE: Error message given when a file argument points to a file
 # that does not exist, but should (e.g. for use with File->Open) %1$S is a
 # filename
-fileErrNotExists='%1$S' doesn't exist
+fileErrNotExists=‘%1$S’ doesn’t exist
 
 # LOCALIZATION NOTE: Error message given when a file argument points to a file
 # that exists, but should not (e.g. for use with File->Save As) %1$S is a
 # filename
-fileErrExists='%1$S' already exists
+fileErrExists=‘%1$S’ already exists
 
 # LOCALIZATION NOTE: Error message given when a file argument points to a
 # non-file, when a file is needed. %1$S is a filename
-fileErrIsNotFile='%1$S' is not a file
+fileErrIsNotFile=‘%1$S’ is not a file
 
 # LOCALIZATION NOTE: Error message given when a file argument points to a
 # non-directory, when a directory is needed (e.g. for use with 'cd') %1$S is a
 # filename
-fileErrIsNotDirectory='%1$S' is not a directory
+fileErrIsNotDirectory=‘%1$S’ is not a directory
 
 # LOCALIZATION NOTE: Error message given when a file argument does not match
 # the specified regular expression %1$S is a filename %2$S is a regular
 # expression
-fileErrDoesntMatch='%1$S' does not match '%2$S'
+fileErrDoesntMatch=‘%1$S’ does not match ‘%2$S’
 
 # LOCALIZATION NOTE: When the menu has displayed all the matches that it
 # should (i.e. about 10 items) then we display this to alert the user that
 # more matches are available.
 fieldMenuMore=More matches, keep typing
 
 # LOCALIZATION NOTE: The command line provides completion for JavaScript
 # commands, however there are times when the scope of what we're completing
 # against can't be used. This error message is displayed when this happens.
 jstypeParseScope=Scope lost
 
 # LOCALIZATION NOTE (jstypeParseMissing, jstypeBeginSyntax,
 # jstypeBeginUnterm): These error messages are displayed when the command line
 # is doing JavaScript completion and encounters errors.
-jstypeParseMissing=Can't find property '%S'
+jstypeParseMissing=Can’t find property ‘%S’
 jstypeBeginSyntax=Syntax error
 jstypeBeginUnterm=Unterminated string literal
 
 # LOCALIZATION NOTE: This message is displayed if the system for providing
 # JavaScript completions encounters and error it displays this.
 jstypeParseError=Error
 
 # LOCALIZATION NOTE (typesNumberNan, typesNumberNotInt2, typesDateNan): These
 # error messages are displayed when the command line is passed a variable
 # which has the wrong format and can't be converted. Parameters: %S is the
 # passed variable.
-typesNumberNan=Can't convert "%S" to a number.
-typesNumberNotInt2=Can't convert "%S" to an integer.
-typesDateNan=Can't convert "%S" to a date.
+typesNumberNan=Can’t convert “%S” to a number.
+typesNumberNotInt2=Can’t convert “%S” to an integer.
+typesDateNan=Can’t convert “%S” to a date.
 
 # LOCALIZATION NOTE (typesNumberMax, typesNumberMin, typesDateMax,
 # typesDateMin): These error messages are displayed when the command line is
 # passed a variable which has a value out of range (number or date).
 # Parameters: %1$S is the passed variable, %2$S is the limit value.
 typesNumberMax=%1$S is greater than maximum allowed: %2$S.
 typesNumberMin=%1$S is smaller than minimum allowed: %2$S.
 typesDateMax=%1$S is later than maximum allowed: %2$S.
 typesDateMin=%1$S is earlier than minimum allowed: %2$S.
 
 # LOCALIZATION NOTE: This error message is displayed when the command line is
 # passed an option with a limited number of correct values, but the passed
 # value is not one of them.
-typesSelectionNomatch=Can't use '%S'.
+typesSelectionNomatch=Can’t use ‘%S’.
 
 # LOCALIZATION NOTE: This error message is displayed when the command line is
 # expecting a CSS query string, however the passed string is not valid.
 nodeParseSyntax=Syntax error in CSS query
 
 # LOCALIZATION NOTE (nodeParseMultiple, nodeParseNone): These error messages
 # are displayed when the command line is expecting a CSS string that matches a
 # single node, but more nodes (or none) match.
@@ -152,18 +152,18 @@ helpManNone=None
 # LOCALIZATION NOTE: This message is displayed in response to the 'help'
 # command when used without a filter, just above the list of known commands.
 helpListAll=Available Commands:
 
 # LOCALIZATION NOTE (helpListPrefix, helpListNone): These messages are
 # displayed in response to the 'help <search>' command (i.e. with a search
 # string), just above the list of matching commands. Parameters: %S is the
 # search string.
-helpListPrefix=Commands starting with '%S':
-helpListNone=No commands starting with '%S'
+helpListPrefix=Commands starting with ‘%S’:
+helpListNone=No commands starting with ‘%S’
 
 # LOCALIZATION NOTE (helpManRequired, helpManOptional, helpManDefault): When
 # the 'help x' command wants to show the manual for the 'x' command, it needs
 # to be able to describe the parameters as either required or optional, or if
 # they have a default value.
 helpManRequired=required
 helpManOptional=optional
 helpManDefault=optional, default=%S
@@ -181,22 +181,22 @@ subCommands=Sub-Commands
 # cannot find a match for the parse types.
 commandParseError=Command line parsing error
 
 # LOCALIZATION NOTE (contextDesc, contextManual, contextPrefixDesc): These
 # strings are used to describe the 'context' command and its 'prefix'
 # parameter. See localization comment for 'connect' for an explanation about
 # 'prefix'.
 contextDesc=Concentrate on a group of commands
-contextManual=Setup a default prefix to future commands. For example 'context git' would allow you to type 'commit' rather than 'git commit'.
+contextManual=Setup a default prefix to future commands. For example ‘context git’ would allow you to type ‘commit’ rather than ‘git commit’.
 contextPrefixDesc=The command prefix
 
 # LOCALIZATION NOTE: This message message displayed during the processing of
 # the 'context' command, when the found command is not a parent command.
-contextNotParentError=Can't use '%S' as a prefix because it is not a parent command.
+contextNotParentError=Can’t use ‘%S’ as a prefix because it is not a parent command.
 
 # LOCALIZATION NOTE (contextReply, contextEmptyReply): These messages are
 # displayed during the processing of the 'context' command, to indicate
 # success or that there is no command prefix.
 contextReply=Using %S as a command prefix
 contextEmptyReply=Command prefix is unset
 
 # LOCALIZATION NOTE (connectDesc, connectManual, connectPrefixDesc,
@@ -246,17 +246,17 @@ langOutput=You are now using %S
 
 # LOCALIZATION NOTE (prefDesc, prefManual, prefListDesc, prefListManual,
 # prefListSearchDesc, prefListSearchManual, prefShowDesc, prefShowManual,
 # prefShowSettingDesc, prefShowSettingManual): These strings describe the
 # 'pref' command and all its available sub-commands and parameters.
 prefDesc=Commands to control settings
 prefManual=Commands to display and alter preferences both for GCLI and the surrounding environment
 prefListDesc=Display available settings
-prefListManual=Display a list of preferences, optionally filtered when using the 'search' parameter
+prefListManual=Display a list of preferences, optionally filtered when using the ‘search’ parameter
 prefListSearchDesc=Filter the list of settings displayed
 prefListSearchManual=Search for the given string in the list of available preferences
 prefShowDesc=Display setting value
 prefShowManual=Display the value of a given preference
 prefShowSettingDesc=Setting to display
 prefShowSettingManual=The name of the setting to display
 
 # LOCALIZATION NOTE: This message is used to show the preference name and the
@@ -291,17 +291,17 @@ prefOutputFilter=Filter
 # displayed in the output from the 'pref list' command as table headings.
 prefOutputName=Name
 prefOutputValue=Value
 
 # LOCALIZATION NOTE (introDesc, introManual): These strings describe the
 # 'intro' command. The localization of 'Got it!' should be the same used in
 # introTextGo.
 introDesc=Show the opening message
-introManual=Redisplay the message that is shown to new users until they click the 'Got it!' button
+introManual=Redisplay the message that is shown to new users until they click the ‘Got it!’ button
 
 # LOCALIZATION NOTE (introTextOpening3, introTextCommands, introTextKeys2,
 # introTextF1Escape, introTextGo): These strings are displayed when the user
 # first opens the developer toolbar to explain the command line, and is shown
 # each time it is opened until the user clicks the 'Got it!' button.
 introTextOpening3=GCLI is an experiment to create a highly usable command line for web developers.
 introTextCommands=For a list of commands type
 introTextKeys2=, or to show/hide command hints press
--- a/devtools/shared/locales/en-US/gclicommands.properties
+++ b/devtools/shared/locales/en-US/gclicommands.properties
@@ -17,17 +17,17 @@
 helpDesc=Get help on the available commands
 
 # LOCALIZATION NOTE (helpAvailable) Used in the output of the help command to
 # explain the contents of the command help table.
 helpAvailable=Available Commands
 
 # LOCALIZATION NOTE (notAvailableInE10S) Used in the output of any command that
 # is not compatible with multiprocess mode (E10S).
-notAvailableInE10S=The command '%1$S' is not available in multiprocess mode (E10S)
+notAvailableInE10S=The command ‘%1$S’ is not available in multiprocess mode (E10S)
 
 # LOCALIZATION NOTE (consoleDesc) A very short string used to describe the
 # function of the console command.
 consoleDesc=Commands to control the console
 
 # LOCALIZATION NOTE (consoleManual) A longer description describing the
 # set of commands that control the console.
 consoleManual=Filter, clear and close the web console
@@ -49,17 +49,17 @@ screenshotManual=Save a PNG image of the
 # LOCALIZATION NOTE (screenshotFilenameDesc) A very short string to describe
 # the 'filename' parameter to the 'screenshot' command, which is displayed in
 # a dialog when the user is using this command.
 screenshotFilenameDesc=Destination filename
 
 # LOCALIZATION NOTE (screenshotFilenameManual) A fuller description of the
 # 'filename' parameter to the 'screenshot' command, displayed when the user
 # asks for help on what it does.
-screenshotFilenameManual=The name of the file (should have a '.png' extension) to which we write the screenshot.
+screenshotFilenameManual=The name of the file (should have a ‘.png’ extension) to which we write the screenshot.
 
 # LOCALIZATION NOTE (screenshotClipboardDesc) A very short string to describe
 # the 'clipboard' parameter to the 'screenshot' command, which is displayed in
 # a dialog when the user is using this command.
 screenshotClipboardDesc=Copy screenshot to clipboard? (true/false)
 
 # LOCALIZATION NOTE (screenshotClipboardManual) A fuller description of the
 # 'clipboard' parameter to the 'screenshot' command, displayed when the user
@@ -71,17 +71,17 @@ screenshotClipboardManual=True if you wa
 # a dialog when the user is using this command.
 # The argument (%1$S) is the browser name.
 screenshotChromeDesc2=Capture %1$S chrome window? (true/false)
 
 # LOCALIZATION NOTE (screenshotChromeManual) A fuller description of the
 # 'chrome' parameter to the 'screenshot' command, displayed when the user
 # asks for help on what it does.
 # The argument (%1$S) is the browser name.
-screenshotChromeManual2=True if you want to take the screenshot of the %1$S window rather than the web page's content window.
+screenshotChromeManual2=True if you want to take the screenshot of the %1$S window rather than the web page’s content window.
 
 # LOCALIZATION NOTE (screenshotGroupOptions) A label for the optional options of
 # the screenshot command.
 screenshotGroupOptions=Options
 
 # LOCALIZATION NOTE (screenshotGroupOptions) A label for the advanced options of
 # the screenshot command.
 screenshotAdvancedOptions=Advanced Options
@@ -222,17 +222,17 @@ highlightShowAllManual=If too many nodes
 # LOCALIZATION NOTE (highlightRegionDesc) A very short string to describe the
 # 'region' option parameter to the 'highlight' command, which is displayed in a
 # dialog when the user is using this command.
 highlightRegionDesc=Box model region
 
 # LOCALIZATION NOTE (highlightRegionManual) A fuller description of the 'region'
 # option parameter to the 'highlight' command, displayed when the user asks for
 # help on what it does.
-highlightRegionManual=Which box model region should be highlighted: 'content', 'padding', 'border' or 'margin'
+highlightRegionManual=Which box model region should be highlighted: ‘content’, ‘padding’, ‘border’ or ‘margin’
 
 # LOCALIZATION NOTE (highlightFillDesc) A very short string to describe the
 # 'fill' option parameter to the 'highlight' command, which is displayed in a
 # dialog when the user is using this command.
 highlightFillDesc=Fill style
 
 # LOCALIZATION NOTE (highlightFillManual) A fuller description of the 'fill'
 # option parameter to the 'highlight' command, displayed when the user asks for
@@ -255,27 +255,27 @@ highlightKeepManual=By default, existing
 # to turn highlighting off
 highlightOutputConfirm2=%1$S node highlighted;%1$S nodes highlighted
 
 # LOCALIZATION NOTE (highlightOutputMaxReached) A confirmation message for the
 # 'highlight' command, displayed to the user once the command has been entered,
 # informing the user how many nodes have been highlighted successfully and that
 # some nodes could not be highlighted due to the maximum number of nodes being
 # reached, and how to turn highlighting off
-highlightOutputMaxReached=%1$S nodes matched, but only %2$S nodes highlighted. Use '--showall' to show all
+highlightOutputMaxReached=%1$S nodes matched, but only %2$S nodes highlighted. Use ‘--showall’ to show all
 
 # LOCALIZATION NOTE (unhighlightDesc) A very short description of the
 # 'unhighlight' command. See unhighlightManual for a fuller description of what
 # it does. This string is designed to be shown in a menu alongside the
 # command name, which is why it should be as short as possible.
 unhighlightDesc=Unhighlight all nodes
 
 # LOCALIZATION NOTE (unhighlightManual) A fuller description of the 'unhighlight'
 # command, displayed when the user asks for help on what it does.
-unhighlightManual=Unhighlight all nodes previously highlighted with the 'highlight' command
+unhighlightManual=Unhighlight all nodes previously highlighted with the ‘highlight’ command
 
 # LOCALIZATION NOTE (restartBrowserDesc) A very short description of the
 # 'restart' command. This string is designed to be shown in a menu alongside the
 # command name, which is why it should be as short as possible.
 # The argument (%1$S) is the browser name.
 restartBrowserDesc=Restart %1$S
 
 # LOCALIZATION NOTE (restartBrowserNocacheDesc) A very short string to
@@ -456,17 +456,17 @@ dbgListSourcesDesc=List the source URLs 
 dbgBlackBoxDesc=Black box sources in the debugger
 
 # LOCALIZATION NOTE (dbgBlackBoxSourceDesc) A very short string used to describe the
 # 'source' parameter to the 'dbg blackbox' command.
 dbgBlackBoxSourceDesc=A specific source to black box
 
 # LOCALIZATION NOTE (dbgBlackBoxGlobDesc) A very short string used to describe the
 # 'glob' parameter to the 'dbg blackbox' command.
-dbgBlackBoxGlobDesc=Black box all sources that match this glob (for example: "*.min.js")
+dbgBlackBoxGlobDesc=Black box all sources that match this glob (for example: “*.min.js”)
 
 # LOCALIZATION NOTE (dbgBlackBoxInvertDesc) A very short string used to describe the
 # 'invert' parameter to the 'dbg blackbox' command.
 dbgBlackBoxInvertDesc=Invert matching, so that we black box every source that is not the source provided or does not match the provided glob pattern.
 
 # LOCALIZATION NOTE (dbgBlackBoxEmptyDesc) A very short string used to let the
 # user know that no sources were black boxed.
 dbgBlackBoxEmptyDesc=(No sources black boxed)
@@ -485,17 +485,17 @@ dbgBlackBoxErrorDesc=Error black boxing:
 dbgUnBlackBoxDesc=Stop black boxing sources in the debugger
 
 # LOCALIZATION NOTE (dbgUnBlackBoxSourceDesc) A very short string used to describe the
 # 'source' parameter to the 'dbg unblackbox' command.
 dbgUnBlackBoxSourceDesc=A specific source to stop black boxing
 
 # LOCALIZATION NOTE (dbgUnBlackBoxGlobDesc) A very short string used to describe the
 # 'glob' parameter to the 'dbg blackbox' command.
-dbgUnBlackBoxGlobDesc=Stop black boxing all sources that match this glob (for example: "*.min.js")
+dbgUnBlackBoxGlobDesc=Stop black boxing all sources that match this glob (for example: “*.min.js”)
 
 # LOCALIZATION NOTE (dbgUnBlackBoxEmptyDesc) A very short string used to let the
 # user know that we did not stop black boxing any sources.
 dbgUnBlackBoxEmptyDesc=(Did not stop black boxing any sources)
 
 # LOCALIZATION NOTE (dbgUnBlackBoxNonEmptyDesc) A very short string used to let the
 # user know which sources we stopped black boxing.
 dbgUnBlackBoxNonEmptyDesc=Stopped black boxing the following sources:
@@ -595,26 +595,26 @@ cmdDesc=Manipulate the commands
 
 # LOCALIZATION NOTE (cmdRefreshDesc) A very short description of the 'cmd refresh'
 # command. This string is designed to be shown in a menu alongside the command
 # name, which is why it should be as short as possible.
 cmdRefreshDesc=Re-read mozcmd directory
 
 # LOCALIZATION NOTE (cmdStatus3) When the we load new commands from mozcmd
 # directory, we report where we loaded from using %1$S.
-cmdStatus3=Loaded commands from '%1$S'
+cmdStatus3=Loaded commands from ‘%1$S’
 
 # LOCALIZATION NOTE (cmdSetdirDesc)  A very short description of the 'cmd setdir'
 # command. This string is designed to be shown in a menu alongside the command
 # name, which is why it should be as short as possible.
 cmdSetdirDesc=Setup a mozcmd directory
 
-# LOCALIZATION NOTE (cmdSetdirManual2) A fuller description of the 'cmd setdir'
+# LOCALIZATION NOTE (cmdSetdirManual3) A fuller description of the 'cmd setdir'
 # command, displayed when the user asks for help on what it does.
-cmdSetdirManual2=A 'mozcmd' directory is an easy way to create new custom commands. For more information see the <a href="https://developer.mozilla.org/docs/Tools/GCLI/Customization">MDN documentation</a>.
+cmdSetdirManual3=A ‘mozcmd’ directory is an easy way to create new custom commands. For more information see https://developer.mozilla.org/docs/Tools/GCLI/Customization
 
 # LOCALIZATION NOTE (cmdSetdirDirectoryDesc) The description of the directory
 # parameter to the 'cmd setdir' command.
 cmdSetdirDirectoryDesc=Directory containing .mozcmd files
 
 # LOCALIZATION NOTE (addonDesc) A very short description of the 'addon'
 # command. This string is designed to be shown in a menu alongside the command
 # name, which is why it should be as short as possible.
@@ -1214,17 +1214,17 @@ callLogChromeVarNotFoundChrome=Variable 
 # LOCALIZATION NOTE (callLogChromeEvalException) A string displayed as the
 # result of the 'calllog chromestart' command with a source type of JavaScript
 # and invalid JavaScript code.
 callLogChromeEvalException=Evaluated JavaScript threw the following exception
 
 # LOCALIZATION NOTE (callLogChromeEvalNeedsObject) A string displayed as the
 # result of passing a non-JavaScript object creating source via the
 # 'calllog chromestart javascript' command.
-callLogChromeEvalNeedsObject=The JavaScript source must evaluate to an object whose method calls are to be logged e.g. "({a1: function() {this.a2()},a2: function() {}});"
+callLogChromeEvalNeedsObject=The JavaScript source must evaluate to an object whose method calls are to be logged e.g. “({a1: function() {this.a2()},a2: function() {}});”
 
 # LOCALIZATION NOTE (scratchpadOpenTooltip) A string displayed as the
 # tooltip of button in devtools toolbox which opens Scratchpad.
 scratchpadOpenTooltip=Scratchpad
 
 # LOCALIZATION NOTE (paintflashingDesc) A very short string used to describe the
 # function of the "paintflashing" command
 paintflashingDesc=Highlight painted area
@@ -1383,17 +1383,17 @@ profilerAlreadyStarted2=Profile has alre
 # an operation cannot be completed because the profile in question could not be
 # found.
 profilerNotFound=Profile not found
 
 # LOCALIZATION NOTE (profilerNotStarted) A message that is displayed whenever
 # an operation cannot be completed because the profile in question has not been
 # started yet. It also contains a hint to use the 'profile start' command to
 # start the profiler.
-profilerNotStarted3=Profiler has not been started yet. Use 'profile start' to start profiling
+profilerNotStarted3=Profiler has not been started yet. Use ‘profile start’ to start profiling
 
 # LOCALIZATION NOTE (profilerStarted2) A very short string that indicates that
 # we have started recording.
 profilerStarted2=Recording…
 
 # LOCALIZATION NOTE (profilerStopped) A very short string that indicates that
 # we have stopped recording.
 profilerStopped=Stopped…
@@ -1486,17 +1486,17 @@ mdnDesc=Retrieve documentation from MDN
 mdnCssDesc=Retrieve documentation about a given CSS property name from MDN
 # LOCALIZATION NOTE (mdnCssProp) String used to describe the 'property name'
 # parameter used in the 'mdn css' command.
 mdnCssProp=Property name
 # LOCALIZATION NOTE (mdnCssPropertyNotFound) String used to display an error in
 # the result of the 'mdn css' command. Errors occur when a given CSS property
 # wasn't found on MDN. The %1$S parameter will be replaced with the name of the
 # CSS property.
-mdnCssPropertyNotFound=MDN documentation for the CSS property '%1$S' was not found.
+mdnCssPropertyNotFound=MDN documentation for the CSS property ‘%1$S’ was not found.
 # LOCALIZATION NOTE (mdnCssVisitPage) String used as the label of a link to the
 # MDN page for a given CSS property.
 mdnCssVisitPage=Visit MDN page
 
 # LOCALIZATION NOTE (security)
 securityPrivacyDesc=Display supported security and privacy features
 securityManual=Commands to list and get suggestions about security features for the current domain.
 securityListDesc=Display security features
--- a/testing/mochitest/tests/SimpleTest/WindowSnapshot.js
+++ b/testing/mochitest/tests/SimpleTest/WindowSnapshot.js
@@ -7,16 +7,20 @@ try {
 } catch (e) {
   gWindowUtils = null;
 }
 
 function snapshotWindow(win, withCaret) {
   return SpecialPowers.snapshotWindow(win, withCaret);
 }
 
+function snapshotRect(win, rect) {
+  return SpecialPowers.snapshotRect(win, rect);
+}
+
 // If the two snapshots don't compare as expected (true for equal, false for
 // unequal), returns their serializations as data URIs.  In all cases, returns
 // whether the comparison was as expected.
 function compareSnapshots(s1, s2, expectEqual, fuzz) {
   if (s1.width != s2.width || s1.height != s2.height) {
     ok(false, "Snapshot canvases are not the same size - comparing them makes no sense");
     return [false];
   }
new file mode 100644
--- /dev/null
+++ b/toolkit/components/prompts/test/chromeScript.js
@@ -0,0 +1,44 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/Timer.jsm");
+
+// Define these to make EventUtils happy.
+let window = this;
+let parent = {};
+
+let EventUtils = {};
+Services.scriptloader.loadSubScript(
+  "chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
+  EventUtils
+);
+
+addMessageListener("cancelPrompt", message => {
+  cancelPromptWhenItAppears();
+});
+
+function cancelPromptWhenItAppears() {
+  let interval = setInterval(() => {
+    if (cancelPrompt()) {
+      clearInterval(interval);
+    }
+  }, 100);
+}
+
+function cancelPrompt() {
+  let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
+  let gBrowser = browserWin.gBrowser;
+  let promptManager = gBrowser.getTabModalPromptBox(gBrowser.selectedBrowser);
+  let prompts = promptManager.listPrompts();
+  if (!prompts.length) {
+    return false;
+  }
+  sendAsyncMessage("promptCanceled", {
+    ui: {
+      infoTitle: {
+        hidden: prompts[0].ui.infoTitle.getAttribute("hidden") == "true",
+      },
+    },
+  });
+  EventUtils.synthesizeKey("KEY_Escape", { code: "Escape" }, browserWin);
+  return true;
+}
+
--- a/toolkit/components/prompts/test/mochitest.ini
+++ b/toolkit/components/prompts/test/mochitest.ini
@@ -1,16 +1,17 @@
 [DEFAULT]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || e10s
+skip-if = buildapp == 'mulet' || buildapp == 'b2g'
 support-files =
   bug619644_inner.html
   bug625187_iframe.html
   prompt_common.js
+  chromeScript.js
 
 [test_bug619644.html]
 [test_bug620145.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug625187.html]
 [test_bug861605.html]
 [test_modal_prompts.html]
-skip-if = toolkit == 'android' || (os == 'linux' && (debug || asan)) #TIMED_OUT (For Linux : 950636)
+skip-if = toolkit == 'android' || (os == 'linux' && (debug || asan)) || e10s #android: TIMED_OUT (For Linux : 950636)
 [test_modal_select.html]
-skip-if = toolkit == 'android' #TIMED_OUT
+skip-if = toolkit == 'android' || e10s #android: TIMED_OUT
--- a/toolkit/components/prompts/test/test_bug619644.html
+++ b/toolkit/components/prompts/test/test_bug619644.html
@@ -26,26 +26,32 @@ if (!hasTabModalPrompts()) {
 } else {
 SimpleTest.waitForExplicitFinish();
 
 // This is a little yucky, but it works
 // The contents of bug619644_inner.html
 const expectedFinalDoc =
 "<head><\/head><body><p>Original content<\/p>\n<script>\n    window.opener.postMessage(\"\", \"*\");\n    confirm (\"Message\");\n    document.write (\"Extra content\");\n    window.opener.postMessage(document.documentElement.innerHTML, \"*\");\n<\/script>Extra content<\/body>";
 
+let gChromeMessageManager;
+
 function runtest(e)
 {
   window.removeEventListener("message", runtest, false);
   window.addEventListener("message", checktest, false);
-  synthesizeKey("VK_ESCAPE", {}, e.source);
+
+  let url = SimpleTest.getTestFileURL("chromeScript.js");
+  gChromeMessageManager = SpecialPowers.loadChromeScript(url);
+  gChromeMessageManager.sendAsyncMessage("cancelPrompt");
 }
 
 function checktest(e) {
   is(e.data, expectedFinalDoc, "ESC press should not abort document load");
   e.source.close();
+  gChromeMessageManager.destroy();
   SimpleTest.finish();
 }
 
 window.addEventListener("message", runtest, false);
 window.open("bug619644_inner.html", "619644");
 }
 </script>
 </pre>
--- a/toolkit/components/prompts/test/test_bug620145.html
+++ b/toolkit/components/prompts/test/test_bug620145.html
@@ -1,15 +1,14 @@
 <html>
 <head>
   <title>Test for Bug 620145</title>
   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <script type="text/javascript" src="prompt_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body onload="runtest()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=620145">Mozilla Bug 620145</a>
 <pre id="test">
 </pre>
 
 <div id="text" style="max-width: 100px" onmouseup="openAlert()">
@@ -48,52 +47,61 @@ function runtest()
   // The <button> in this test's HTML opens a prompt when clicked.
   // Here we send the events to simulate clicking it.
   ok(true, "starting test");
   isTabModal = hasTabModalPrompts();
   if (!isTabModal)
     todo(false, "Test is run with tab modal prompts disabled.");
   else
     ok(true, "Test is run with tab modal prompts enabled.");
-  startCallbackTimer();
+
+  selectionTest = isTabModal;
+
+  let url = SimpleTest.getTestFileURL("chromeScript.js");
+  let chrome = SpecialPowers.loadChromeScript(url);
+  chrome.sendAsyncMessage("cancelPrompt");
 
   var button = $("button");
   dispatchMouseEvent(button, "mousedown");
   dispatchMouseEvent(button, "mouseup");
 
-  selectionTest = isTabModal;
+  // alert appears at this point, to be closed by the chrome script.
 
-  startCallbackTimer();
+  checkSelection();
+
+  chrome.sendAsyncMessage("cancelPrompt");
 
   var text = $("text");
   dispatchMouseEvent(text, "mousedown");
   dispatchMouseEvent(text, "mouseup");
 
+  // alert appears at this point, to be closed by the chrome script.
+
+  checkSelection();
+
+  chrome.destroy();
   SimpleTest.finish();
 }
 
 function dispatchMouseEvent(target, type)
 {
   var win = target.ownerDocument.defaultView;
   e = document.createEvent("MouseEvent");
   e.initEvent(type, false, false, win, 0, 1, 1, 1, 1,
               false, false, false, false, 0, null);
   var utils = SpecialPowers.getDOMWindowUtils(win);
   utils.dispatchDOMEventViaPresShell(target, e, true);
   ok(true, type + " sent to " + target.id);
 }
 
-function handleDialog(ui, testNum)
+function checkSelection()
 {
   if (!selectionTest) {
     todo(false, "Selection test is disabled when tab modal prompts are not enabled.");
   } else {
     synthesizeMouse($("text"), 25, 55, { type: "mousemove" });
     is(window.getSelection().toString(), "", "selection not made");
   }
-
-  ok(true, "handleDialog sending mouseclick to dialog...");
-  synthesizeMouse(ui.button0, 5, 5, { }, SpecialPowers.unwrap(ui.button0.ownerDocument.defaultView));
 }
 </script>
 
 </body>
 </html>
--- a/toolkit/components/prompts/test/test_bug625187.html
+++ b/toolkit/components/prompts/test/test_bug625187.html
@@ -1,15 +1,14 @@
 <html>
 <head>
   <title>Test for Bug 625187</title>
   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <script type="text/javascript" src="prompt_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
 <!--
    - Any copyright is dedicated to the Public Domain.
    - http://creativecommons.org/publicdomain/zero/1.0/
    -
    - Contributor(s):
    -   Mihai Sucan <mihai.sucan@gmail.com>
    -->
@@ -44,52 +43,58 @@ function runtest()
 
   // This test depends on tab modal prompts being enabled.
   if (!isTabModal) {
     todo(false, "Test disabled when tab modal prompts are not enabled.");
     SimpleTest.finish();
     return;
   }
 
-  startCallbackTimer();
+  let url = SimpleTest.getTestFileURL("chromeScript.js");
+  let chrome = SpecialPowers.loadChromeScript(url);
+
+  chrome.addMessageListener("promptCanceled", msg => {
+    checkSelection(msg.ui);
+  });
+
+  chrome.sendAsyncMessage("cancelPrompt");
 
   var button = document.querySelector("button");
   dispatchMouseEvent(button, "click");
 
-  startCallbackTimer();
+  chrome.sendAsyncMessage("cancelPrompt");
 
   var iframe = document.getElementById("iframe_diff_origin");
   button = SpecialPowers.wrap(iframe.contentWindow).document.getElementById("btn1");
   dispatchMouseEvent(button, "click");
 
-  startCallbackTimer();
+  chrome.sendAsyncMessage("cancelPrompt");
 
   iframe = document.getElementById("iframe_same_origin");
   button = iframe.contentWindow.document.getElementById("btn1");
   dispatchMouseEvent(button, "click");
 
-  startCallbackTimer();
+  chrome.sendAsyncMessage("cancelPrompt");
 
   button = iframe.contentWindow.document.getElementById("btn2");
   dispatchMouseEvent(button, "click");
 
+  chrome.destroy();
   SimpleTest.finish();
 }
 
-function handleDialog(ui)
+function checkSelection(ui)
 {
   dialogNum++;
   if (dialogNum == 1 || dialogNum == 3 || dialogNum == 4)
-    is(ui.infoTitle.getAttribute("hidden"), "true",
+    ok(ui.infoTitle.hidden,
        "dialog #" + dialogNum + ": the tabprompt infoTitle element is hidden");
   else if (dialogNum == 2)
-    ok(!ui.infoTitle.hasAttribute("hidden"),
+    ok(!ui.infoTitle.hidden,
        "dialog #" + dialogNum + ": the tabprompt infoTitle element is not hidden");
-
-  synthesizeMouse(ui.button0, 2, 2, {}, SpecialPowers.unwrap(ui.button0.ownerDocument.defaultView));
 }
 
 function dispatchMouseEvent(target, type)
 {
   var win = SpecialPowers.unwrap(target.ownerDocument.defaultView);
   var e = document.createEvent("MouseEvent");
   e.initEvent(type, false, false, win, 0, 1, 1, 1, 1,
               false, false, false, false, 0, null);
--- a/toolkit/components/prompts/test/test_bug861605.html
+++ b/toolkit/components/prompts/test/test_bug861605.html
@@ -1,15 +1,14 @@
 <html>
 <head>
   <title>Test for Bug 861605</title>
   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <script type="text/javascript" src="prompt_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body onload="runtest()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=861605">Mozilla Bug 861605</a>
 <pre id="test">
 </pre>
 
 <script class="testbody" type="text/javascript">
@@ -33,34 +32,37 @@ function runtest()
   if (!isTabModal) {
     todo(false, "Test is run with tab modal prompts disabled.");
     SimpleTest.finish();
     return;
   }
 
   ok(true, "Test is run with tab modal prompts enabled.");
 
-  startCallbackTimer();
+  let url = SimpleTest.getTestFileURL("chromeScript.js");
+  let chrome = SpecialPowers.loadChromeScript(url);
+  chrome.sendAsyncMessage("cancelPrompt");
 
   try {
     alert();
     ok(true, "alert() without arguments should not throw!");
   } catch(e) {
     ok(false, "alert() without arguments should not throw!");
   }
 
-  startCallbackTimer();
+  chrome.sendAsyncMessage("cancelPrompt");
 
   try {
     confirm();
     ok(true, "confirm() without arguments should not throw!");
   } catch(e) {
     ok(false, "confirm() without arguments should not throw!");
   }
 
+  chrome.destroy();
   SimpleTest.finish();
 }
 
 function handleDialog(ui, testNum)
 {
   synthesizeMouse(ui.button0, 5, 5, { }, SpecialPowers.unwrap(ui.button0.ownerDocument.defaultView));
 }
 </script>
--- a/toolkit/content/tests/browser/browser.ini
+++ b/toolkit/content/tests/browser/browser.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 support-files =
   head.js
   file_contentTitle.html
   audio.ogg
 [browser_autoscroll_disabled.js]
 [browser_bug295977_autoscroll_overflow.js]
+[browser_bug451286.js]
 [browser_bug594509.js]
 [browser_bug982298.js]
 [browser_bug1198465.js]
 [browser_contentTitle.js]
 [browser_default_image_filename.js]
 [browser_f7_caret_browsing.js]
 skip-if = e10s
 [browser_findbar.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/browser/browser_bug451286.js
@@ -0,0 +1,136 @@
+Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js", this);
+
+add_task(function*() {
+  const SEARCH_TEXT = "text";
+  const DATAURI = "data:text/html," + SEARCH_TEXT;
+
+  // Bug 451286. An iframe that should be highlighted
+  let visible = "<iframe id='visible' src='" + DATAURI + "'></iframe>";
+
+  // Bug 493658. An invisible iframe that shouldn't interfere with
+  // highlighting matches lying after it in the document
+  let invisible = "<iframe id='invisible' style='display: none;' " +
+                  "src='" + DATAURI + "'></iframe>";
+
+  let uri = DATAURI + invisible + SEARCH_TEXT + visible + SEARCH_TEXT;
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, uri);
+  let contentRect = tab.linkedBrowser.getBoundingClientRect();
+  let noHighlightSnapshot = snapshotRect(window, contentRect);
+  ok(noHighlightSnapshot, "Got noHighlightSnapshot");
+
+  yield openFindBarAndWait();
+  gFindBar._findField.value = SEARCH_TEXT;
+  var matchCase = gFindBar.getElement("find-case-sensitive");
+  if (matchCase.checked)
+    matchCase.doCommand();
+
+  // Turn on highlighting
+  yield toggleHighlightAndWait(true);
+  yield closeFindBarAndWait();
+
+  // Take snapshot of highlighting
+  let findSnapshot = snapshotRect(window, contentRect);
+  ok(findSnapshot, "Got findSnapshot");
+
+  // Now, remove the highlighting, and take a snapshot to compare
+  // to our original state
+  yield openFindBarAndWait();
+  yield toggleHighlightAndWait(false);
+  yield closeFindBarAndWait();
+
+  let unhighlightSnapshot = snapshotRect(window, contentRect);
+  ok(unhighlightSnapshot, "Got unhighlightSnapshot");
+
+  // Select the matches that should have been highlighted manually
+  yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
+    let doc = content.document;
+    let win = doc.defaultView;
+
+    // Create a manual highlight in the visible iframe to test bug 451286
+    let iframe = doc.getElementById("visible");
+    let ifBody = iframe.contentDocument.body;
+    let range = iframe.contentDocument.createRange();
+    range.selectNodeContents(ifBody.childNodes[0]);
+    let ifWindow = iframe.contentWindow;
+    let ifDocShell = ifWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIWebNavigation)
+                             .QueryInterface(Ci.nsIDocShell);
+
+    let ifController = ifDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                                 .getInterface(Ci.nsISelectionDisplay)
+                                 .QueryInterface(Ci.nsISelectionController);
+
+    let frameFindSelection =
+      ifController.getSelection(ifController.SELECTION_FIND);
+    frameFindSelection.addRange(range);
+
+    // Create manual highlights in the main document (the matches that lie
+    // before/after the iframes
+    let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIWebNavigation)
+                      .QueryInterface(Ci.nsIDocShell);
+
+    let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsISelectionDisplay)
+                             .QueryInterface(Ci.nsISelectionController);
+
+    let docFindSelection =
+      controller.getSelection(ifController.SELECTION_FIND);
+
+    range = doc.createRange();
+    range.selectNodeContents(doc.body.childNodes[0]);
+    docFindSelection.addRange(range);
+    range = doc.createRange();
+    range.selectNodeContents(doc.body.childNodes[2]);
+    docFindSelection.addRange(range);
+    range = doc.createRange();
+    range.selectNodeContents(doc.body.childNodes[4]);
+    docFindSelection.addRange(range);
+  });
+
+  // Take snapshot of manual highlighting
+  let manualSnapshot = snapshotRect(window, contentRect);
+  ok(manualSnapshot, "Got manualSnapshot");
+
+  // Test 1: Were the matches in iframe correctly highlighted?
+  let res = compareSnapshots(findSnapshot, manualSnapshot, true);
+  ok(res[0], "Matches found in iframe correctly highlighted");
+
+  // Test 2: Were the matches in iframe correctly unhighlighted?
+  res = compareSnapshots(noHighlightSnapshot, unhighlightSnapshot, true);
+  ok(res[0], "Highlighting in iframe correctly removed");
+
+  yield BrowserTestUtils.removeTab(tab);
+});
+
+function toggleHighlightAndWait(shouldHighlight) {
+  return new Promise((resolve) => {
+    let listener = {
+      onFindResult() {},
+      onHighlightFinished() {
+        gFindBar.browser.finder.removeResultListener(listener);
+        resolve();
+      },
+      onMatchesCountResult() {}
+    };
+    gFindBar.browser.finder.addResultListener(listener);
+    gFindBar.toggleHighlight(shouldHighlight);
+  });
+}
+
+function* openFindBarAndWait() {
+  let awaitTransitionEnd = BrowserTestUtils.waitForEvent(gFindBar, "transitionend");
+  gFindBar.open();
+  yield awaitTransitionEnd;
+}
+
+// This test is comparing snapshots. It is necessary to wait for the gFindBar
+// to close before taking the snapshot so the gFindBar does not take up space
+// on the new snapshot.
+function* closeFindBarAndWait() {
+  let awaitTransitionEnd = BrowserTestUtils.waitForEvent(gFindBar, "transitionend", false, event => {
+    return event.propertyName == "visibility";
+  });
+  gFindBar.close();
+  yield awaitTransitionEnd;
+}
deleted file mode 100644
--- a/toolkit/content/tests/chrome/bug451286_window.xul
+++ /dev/null
@@ -1,193 +0,0 @@
-<?xml version="1.0"?>
-
-<!-- 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/. -->
-
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-
-<window id="451286test"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        width="600"
-        height="600"
-        onload="SimpleTest.executeSoon(startTest);"
-        title="451286 test (also tests bug 493658)">
-
-  <script type="application/javascript"><![CDATA[
-    const Ci = Components.interfaces;
-    const Cc = Components.classes;
-    const Cr = Components.results;
-    const SEARCH_TEXT = "text";
-    const DATAURI = "data:text/html," + SEARCH_TEXT;
-
-    let {Task} = Components.utils.import("resource://gre/modules/Task.jsm", {});
-
-    var gFindBar = null;
-    var gBrowser;
-    var gWin;
-
-    var noHighlightSnapshot;
-    var findSnapshot;
-
-    var imports = ["SimpleTest", "ok", "snapshotWindow", "compareSnapshots",
-                   "parentFinish"];
-    for (var name of imports) {
-      window[name] = window.opener.wrappedJSObject[name];
-    }
-
-    function finish() {
-      window.close();
-      parentFinish();
-    }
-
-    function startTest() {
-      gFindBar = document.getElementById("FindToolbar");
-      gBrowser = document.getElementById("content");
-      gBrowser.addEventListener("pageshow", onPageShow, false);
-
-      // Bug 451286. An iframe that should be highlighted
-      var visible = "<iframe id='visible' src='" + DATAURI + "'></iframe>";
-
-      // Bug 493658. An invisible iframe that shouldn't interfere with
-      // highlighting matches lying after it in the document
-      var invisible = "<iframe id='invisible' style='display: none;' " +
-                      "src='" + DATAURI + "'></iframe>";
-
-      var uri = DATAURI + invisible + SEARCH_TEXT + visible + SEARCH_TEXT;
-      gBrowser.loadURI(uri);
-    }
-
-    // This test is comparing snapshots. It is necessary to wait for the findbar
-    // to close before taking the snapshot so the findbar does not take up space
-    // on the new snapshot.
-    function closeFindbarAndWait() {
-      return new Promise((resolve) => {
-        gFindBar.addEventListener("transitionend", function cont(aEvent) {
-          if (aEvent.propertyName != "visibility") {
-            return;
-          }
-          gFindBar.removeEventListener("transitionend", cont);
-          resolve();
-        });
-        gFindBar.close();
-      });
-    }
-
-    function toggleHighlightAndWait(aHighlight) {
-      return new Promise((resolve) => {
-        let listener = {
-          onHighlightFinished: function() {
-            gFindBar.browser.finder.removeResultListener(listener);
-            resolve();
-          }
-        };
-        gFindBar.browser.finder.addResultListener(listener);
-        gFindBar.toggleHighlight(aHighlight);
-      });
-    }
-
-    let onPageShow = Task.async(function* (aEvent) {
-      // Don't respond to pageshow events coming from the <iframes>
-      if (aEvent.target != gBrowser.contentDocument)
-        return;
-
-      gBrowser.removeEventListener("pageshow", onPageShow, false);
-
-      // First, take a snapshot of the window without highlighting
-      // to be used later to test the unhighlighting case
-      gWin = gBrowser.contentWindow;
-      noHighlightSnapshot = snapshotWindow(gWin);
-
-      yield part1();
-      yield part2();
-      yield part3();
-
-      finish();
-    });
-
-    let part1 = Task.async(function* () {
-      gFindBar.open();
-      gFindBar._findField.value = SEARCH_TEXT;
-      var matchCase = gFindBar.getElement("find-case-sensitive");
-      if (matchCase.checked)
-        matchCase.doCommand();
-
-      // Turn on highlighting
-      yield toggleHighlightAndWait(true);
-      yield closeFindbarAndWait();
-    });
-
-    let part2 = Task.async(function* () {
-      // Take snapshot of highlighting
-      findSnapshot = snapshotWindow(gWin);
-
-      // Now, remove the highlighting, and take a snapshot to compare
-      // to our original state
-      gFindBar.open();
-      yield toggleHighlightAndWait(false);
-      yield closeFindbarAndWait();
-    });
-
-    let part3 = Task.async(function* () {
-      var unhighlightSnapshot = snapshotWindow(gWin);
-
-      // Select the matches that should have been highlighted manually
-      var doc = gBrowser.contentDocument;
-
-      // Create a manual highlight in the visible iframe to test bug 451286
-      var iframe = doc.getElementById("visible");
-      var ifBody = iframe.contentDocument.body;
-      var range = iframe.contentDocument.createRange();
-      range.selectNodeContents(ifBody.childNodes[0]);
-      var ifWindow = iframe.contentWindow;
-      var ifDocShell = ifWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIWebNavigation)
-                               .QueryInterface(Ci.nsIDocShell);
-
-      var ifController = ifDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                                   .getInterface(Ci.nsISelectionDisplay)
-                                   .QueryInterface(Ci.nsISelectionController);
-
-      var frameFindSelection =
-        ifController.getSelection(ifController.SELECTION_FIND);
-      frameFindSelection.addRange(range);
-
-      // Create manual highlights in the main document (the matches that lie
-      // before/after the iframes
-      var docShell = gWin.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIDocShell);
-
-      var controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsISelectionDisplay)
-                               .QueryInterface(Ci.nsISelectionController);
-
-      var docFindSelection =
-        controller.getSelection(ifController.SELECTION_FIND);
-
-      range = doc.createRange();
-      range.selectNodeContents(doc.body.childNodes[0]);
-      docFindSelection.addRange(range);
-      range = doc.createRange();
-      range.selectNodeContents(doc.body.childNodes[2]);
-      docFindSelection.addRange(range);
-      range = doc.createRange();
-      range.selectNodeContents(doc.body.childNodes[4]);
-      docFindSelection.addRange(range);
-
-      // Take snapshot of manual highlighting
-      var manualSnapshot = snapshotWindow(gBrowser.contentWindow);
-
-      // Test 1: Were the matches in iframe correctly highlighted?
-      var res = compareSnapshots(findSnapshot, manualSnapshot, true);
-      ok(res[0], "Matches found in iframe correctly highlighted");
-
-      // Test 2: Were the matches in iframe correctly unhighlighted?
-      res = compareSnapshots(noHighlightSnapshot, unhighlightSnapshot, true);
-      ok(res[0], "Highlighting in iframe correctly removed");
-    });
-  ]]></script>
-
-  <browser type="content-primary" flex="1" id="content" src="about:blank"/>
-  <findbar id="FindToolbar" browserid="content"/>
-</window>
--- a/toolkit/content/tests/chrome/chrome.ini
+++ b/toolkit/content/tests/chrome/chrome.ini
@@ -6,17 +6,16 @@ support-files =
   RegisterUnregisterChrome.js
   bug263683_window.xul
   bug304188_window.xul
   bug331215_window.xul
   bug360437_window.xul
   bug366992_window.xul
   bug409624_window.xul
   bug429723_window.xul
-  bug451286_window.xul
   bug624329_window.xul
   dialog_dialogfocus.xul
   file_about_networking_wsh.py
   file_autocomplete_with_composition.js
   findbar_events_window.xul
   findbar_window.xul
   frame_popup_anchor.xul
   frame_popupremoving_frame.xul
@@ -71,17 +70,16 @@ skip-if = buildapp == 'mulet'
 [test_bug360437.xul]
 [test_bug365773.xul]
 [test_bug366992.xul]
 [test_bug382990.xul]
 [test_bug409624.xul]
 [test_bug418874.xul]
 [test_bug429723.xul]
 [test_bug437844.xul]
-[test_bug451286.xul]
 [test_bug457632.xul]
 [test_bug460942.xul]
 [test_bug471776.xul]
 [test_bug509732.xul]
 [test_bug554279.xul]
 [test_bug557987.xul]
 [test_bug562554.xul]
 [test_bug570192.xul]
deleted file mode 100644
--- a/toolkit/content/tests/chrome/test_bug451286.xul
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet 
-  href="chrome://mochikit/content/tests/SimpleTest/test.css"
-  type="text/css"?>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=451286
--->
-<window title="Mozilla Bug 451286"
-  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
-  <script type="application/javascript" 
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" 
-          src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
-
-  <body  xmlns="http://www.w3.org/1999/xhtml">
-    <a target="_blank" 
-       href="https://bugzilla.mozilla.org/show_bug.cgi?id=451286">
-      Mozilla Bug 451286
-    </a>
-
-    <p id="display"></p>
-    <div id="content" style="display: none">
-    </div>
-    <pre id="test">
-    </pre>
-  </body>
-
-  <script class="testbody" type="application/javascript">
-    <![CDATA[
-
-      // To be called by the window we open.
-      function parentFinish() {
-        // This is called with JS code from the window we open on the
-        // stack.  We need to GC everything in that window, so do that
-        // from a timeout and then call SimpleTest.finish().
-        setTimeout(doFinish, 0);
-      }
-
-      function doFinish() {
-        // Garbage collect objects created in this test can cause
-        // assertions, so GC now to blame those assertions to this test.
-        SpecialPowers.gc();
-        SimpleTest.finish();
-      }
-
-      /** Test for Bug 451286 **/
-      SimpleTest.waitForExplicitFinish();
-      window.open("bug451286_window.xul", "451286test", 
-                  "chrome,width=600,height=600");
-
-    ]]>
-  </script>
-
-</window>
--- a/toolkit/modules/RemoteFinder.jsm
+++ b/toolkit/modules/RemoteFinder.jsm
@@ -84,18 +84,20 @@ RemoteFinder.prototype = {
         params = [ aMessage.data ];
         break;
     }
 
     for (let l of this._listeners) {
       // Don't let one callback throwing stop us calling the rest
       try {
         l[callback].apply(l, params);
-      }
-      catch (e) {
+      } catch (e) {
+        if (!l[callback]) {
+          Cu.reportError(`Missing ${callback} callback on RemoteFinderListener`);
+        }
         Cu.reportError(e);
       }
     }
   },
 
   get searchString() {
     return this._searchString;
   },