Merge autoland to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 19 Sep 2016 15:34:31 -0700
changeset 357234 5ef7111ff596959cc54653f2eec975b249ab7775
parent 357222 fd0564234eca242b7fb753a110312679020f8059 (current diff)
parent 357233 d1df1692dbb4b1f635ef52696233373309ba0b8c (diff)
child 357250 80a9c7007243ac4e931a8c4352723cbf840aff03
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to central, a=merge
--- a/browser/base/content/browser-media.js
+++ b/browser/base/content/browser-media.js
@@ -291,17 +291,17 @@ let gDecoderDoctorHandler = {
       if (!formatsInPref) {
         Services.prefs.setCharPref(formatsPref, formats);
         histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SHOWN_FIRST);
       } else {
         // Split existing formats into an array of strings.
         let existing = formatsInPref.split(",").map(String.trim);
         // Keep given formats that were not already recorded.
         let newbies = formats.split(",").map(String.trim)
-                      .filter(x => existing.includes(x));
+                      .filter(x => !existing.includes(x));
         // And rewrite pref with the added new formats (if any).
         if (newbies.length) {
           Services.prefs.setCharPref(formatsPref,
                                      existing.concat(newbies).join(", "));
         }
       }
       histogram.add(decoderDoctorReportId, TELEMETRY_DDSTAT_SHOWN);
 
--- a/browser/components/preferences/connection.xul
+++ b/browser/components/preferences/connection.xul
@@ -9,17 +9,22 @@
 <?xml-stylesheet href="chrome://global/skin/"?>
 
 <prefwindow id="ConnectionsDialog" type="child"
             xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
             title="&connectionsDialog.title;"
             dlgbuttons="accept,cancel,help"
             onbeforeaccept="return gConnectionsDialog.beforeAccept();"
             onload="gConnectionsDialog.checkForSystemProxy();"
-            ondialoghelp="openPrefsHelp()">
+            ondialoghelp="openPrefsHelp()"
+#ifdef XP_MACOSX
+            style="width: &window.macWidth2; !important;">
+#else
+            style="width: &window.width2; !important;">
+#endif
 
   <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
 
   <prefpane id="ConnectionsDialogPane"
             class="largeDialogContainer"
             helpTopic="prefs-connection-settings">
 
     <preferences>
--- a/browser/components/preferences/jar.mn
+++ b/browser/components/preferences/jar.mn
@@ -5,17 +5,17 @@
 browser.jar:
     content/browser/preferences/applicationManager.xul
     content/browser/preferences/applicationManager.js
     content/browser/preferences/blocklists.xul
     content/browser/preferences/blocklists.js
 *   content/browser/preferences/colors.xul
 *   content/browser/preferences/cookies.xul
     content/browser/preferences/cookies.js
-    content/browser/preferences/connection.xul
+*   content/browser/preferences/connection.xul
     content/browser/preferences/connection.js
     content/browser/preferences/donottrack.xul
 *   content/browser/preferences/fonts.xul
     content/browser/preferences/fonts.js
     content/browser/preferences/handlers.xml
     content/browser/preferences/handlers.css
 *   content/browser/preferences/languages.xul
     content/browser/preferences/languages.js
--- a/browser/locales/en-US/chrome/browser/preferences/connection.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/connection.dtd
@@ -1,14 +1,16 @@
 <!-- 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/. -->
 
 
 <!ENTITY  connectionsDialog.title       "Connection Settings">
+<!ENTITY  window.width2                 "49em">
+<!ENTITY  window.macWidth2              "44em">
 
 <!ENTITY  proxyTitle.label              "Configure Proxies to Access the Internet">
 <!ENTITY  noProxyTypeRadio.label        "No proxy">
 <!ENTITY  noProxyTypeRadio.accesskey    "y">
 <!ENTITY  systemTypeRadio.label         "Use system proxy settings">
 <!ENTITY  systemTypeRadio.accesskey     "u">
 <!ENTITY  WPADTypeRadio.label           "Auto-detect proxy settings for this network">
 <!ENTITY  WPADTypeRadio.accesskey       "w">
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -19,29 +19,35 @@ this.SitePermissions = {
   /* Returns all custom permissions for a given URI, the return
    * type is a list of objects with the keys:
    * - id: the permissionId of the permission
    * - state: a constant representing the current permission state
    *   (e.g. SitePermissions.ALLOW)
    *
    * To receive a more detailed, albeit less performant listing see
    * SitePermissions.getPermissionDetailsByURI().
+   *
+   * install addon permission is excluded, check bug 1303108
    */
   getAllByURI: function (aURI) {
     let result = [];
     if (!this.isSupportedURI(aURI)) {
       return result;
     }
 
     let permissions = Services.perms.getAllForURI(aURI);
     while (permissions.hasMoreElements()) {
       let permission = permissions.getNext();
 
       // filter out unknown permissions
       if (gPermissionObject[permission.type]) {
+        // XXX Bug 1303108 - Control Center should only show non-default permissions
+        if (permission.type == "install") {
+          continue;
+        }
         result.push({
           id: permission.type,
           state: permission.capability,
         });
       }
     }
 
     return result;
--- a/browser/modules/test/xpcshell/test_SitePermissions.js
+++ b/browser/modules/test/xpcshell/test_SitePermissions.js
@@ -40,16 +40,21 @@ add_task(function* testGetAllByURI() {
   Assert.deepEqual(SitePermissions.getAllByURI(uri), [
       { id: "camera", state: SitePermissions.ALLOW },
       { id: "desktop-notification", state: SitePermissions.BLOCK }
   ]);
 
   SitePermissions.remove(uri, "camera");
   SitePermissions.remove(uri, "desktop-notification");
   Assert.deepEqual(SitePermissions.getAllByURI(uri), []);
+
+  // XXX Bug 1303108 - Control Center should only show non-default permissions
+  SitePermissions.set(uri, "addon", SitePermissions.BLOCK);
+  Assert.deepEqual(SitePermissions.getAllByURI(uri), []);
+  SitePermissions.remove(uri, "addon");
 });
 
 add_task(function* testGetPermissionDetailsByURI() {
   // check that it returns an empty array on an invalid URI
   // like a file URI, which doesn't support site permissions
   let wrongURI = Services.io.newURI("file:///example.js", null, null)
   Assert.deepEqual(SitePermissions.getPermissionDetailsByURI(wrongURI), []);
 
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Preferences.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Preferences.jsm
@@ -23,19 +23,21 @@ this.Preferences = {
       ["paneApplications", null],
       ["panePrivacy", null],
       ["panePrivacy", null, DNTDialog],
       ["paneSecurity", null],
       ["paneSync", null],
       ["paneAdvanced", "generalTab"],
       ["paneAdvanced", "dataChoicesTab"],
       ["paneAdvanced", "networkTab"],
+      ["paneAdvanced", "networkTab", connectionDialog],
       ["paneAdvanced", "updateTab"],
       ["paneAdvanced", "encryptionTab"],
       ["paneAdvanced", "encryptionTab", certManager],
+      ["paneAdvanced", "encryptionTab", deviceManager],
     ];
     for (let [primary, advanced, customFn] of panes) {
       let configName = primary.replace(/^pane/, "prefs") + (advanced ? "-" + advanced : "");
       if (customFn) {
         configName += "-" + customFn.name;
       }
       this.configurations[configName] = {};
       this.configurations[configName].applyConfig = prefHelper.bind(null, primary, advanced, customFn);
@@ -94,13 +96,25 @@ function paintPromise(browserWindow) {
 }
 
 function* DNTDialog(aBrowser) {
   yield ContentTask.spawn(aBrowser, null, function* () {
     content.document.getElementById("doNotTrackSettings").click();
   });
 }
 
+function* connectionDialog(aBrowser) {
+  yield ContentTask.spawn(aBrowser, null, function* () {
+    content.document.getElementById("connectionSettings").click();
+  });
+}
+
 function* certManager(aBrowser) {
   yield ContentTask.spawn(aBrowser, null, function* () {
     content.document.getElementById("viewCertificatesButton").click();
   });
 }
+
+function* deviceManager(aBrowser) {
+  yield ContentTask.spawn(aBrowser, null, function* () {
+    content.document.getElementById("viewSecurityDevicesButton").click();
+  });
+}
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -216,18 +216,18 @@ if test -n "$MOZ_INSTALL_TRACKING"; then
     AC_SUBST(MOZ_INSTALL_TRACKING)
     MOZ_ANDROID_AAR(play-services-ads, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
     MOZ_ANDROID_AAR(play-services-basement, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
 fi
 
 ])
 
 dnl Configure an Android SDK.
-dnl Arg 1: target SDK version, like 22.
-dnl Arg 2: build tools version, like 22.0.1.
+dnl Arg 1: target SDK version, like 23.
+dnl Arg 2: list of build-tools versions, like "23.0.3 23.0.1".
 AC_DEFUN([MOZ_ANDROID_SDK],
 [
 
 MOZ_ARG_WITH_STRING(android-sdk,
 [  --with-android-sdk=DIR
                           location where the Android SDK can be found (like ~/.mozbuild/android-sdk-linux)],
     android_sdk_root=$withval)
 
@@ -249,22 +249,30 @@ case "$target" in
     android_target_sdk=$1
     AC_MSG_CHECKING([for Android SDK platform version $android_target_sdk])
     android_sdk=$android_sdk_root/platforms/android-$android_target_sdk
     if ! test -e "$android_sdk/source.properties" ; then
         AC_MSG_ERROR([You must download Android SDK platform version $android_target_sdk.  Try |mach bootstrap|.  (Looked for $android_sdk)])
     fi
     AC_MSG_RESULT([$android_sdk])
 
-    android_build_tools="$android_sdk_root"/build-tools/$2
-    AC_MSG_CHECKING([for Android build-tools version $2])
-    if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then
-        AC_MSG_RESULT([$android_build_tools])
-    else
-        AC_MSG_ERROR([You must install the Android build-tools version $2.  Try |mach bootstrap|.  (Looked for $android_build_tools)])
+    AC_MSG_CHECKING([for Android build-tools])
+    android_build_tools_base="$android_sdk_root"/build-tools
+    android_build_tools_version=""
+    for version in $2; do
+        android_build_tools="$android_build_tools_base"/$version
+        if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then
+            android_build_tools_version=$version
+            AC_MSG_RESULT([$android_build_tools])
+            break
+        fi
+    done
+    if test "$android_build_tools_version" == ""; then
+        version=$(echo $2 | cut -d" " -f1)
+        AC_MSG_ERROR([You must install the Android build-tools version $version.  Try |mach bootstrap|.  (Looked for "$android_build_tools_base"/$version)])
     fi
 
     MOZ_PATH_PROG(ZIPALIGN, zipalign, :, [$android_build_tools])
     MOZ_PATH_PROG(DX, dx, :, [$android_build_tools])
     MOZ_PATH_PROG(AAPT, aapt, :, [$android_build_tools])
     MOZ_PATH_PROG(AIDL, aidl, :, [$android_build_tools])
     if test -z "$ZIPALIGN" -o "$ZIPALIGN" = ":"; then
       AC_MSG_ERROR([The program zipalign was not found.  Try |mach bootstrap|.])
@@ -304,17 +312,17 @@ case "$target" in
     if test -z "$EMULATOR" -o "$EMULATOR" = ":"; then
       AC_MSG_ERROR([The program emulator was not found.  Try |mach bootstrap|.])
     fi
 
     ANDROID_TARGET_SDK="${android_target_sdk}"
     ANDROID_SDK="${android_sdk}"
     ANDROID_SDK_ROOT="${android_sdk_root}"
     ANDROID_TOOLS="${android_tools}"
-    ANDROID_BUILD_TOOLS_VERSION="$2"
+    ANDROID_BUILD_TOOLS_VERSION="$android_build_tools_version"
     AC_DEFINE_UNQUOTED(ANDROID_TARGET_SDK,$ANDROID_TARGET_SDK)
     AC_SUBST(ANDROID_TARGET_SDK)
     AC_SUBST(ANDROID_SDK_ROOT)
     AC_SUBST(ANDROID_SDK)
     AC_SUBST(ANDROID_TOOLS)
     AC_SUBST(ANDROID_BUILD_TOOLS_VERSION)
 
     MOZ_ANDROID_AAR(customtabs, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -300,18 +300,19 @@ ResponsiveUI.prototype = {
    * to ensure all in-page state is preserved, just like when you move a tab to
    * a new window.
    *
    * For more details, see /devtools/docs/responsive-design-mode.md.
    */
   init: Task.async(function* () {
     let ui = this;
 
-    // Watch for tab close so we can clean up RDM synchronously
+    // Watch for tab close and window close so we can clean up RDM synchronously
     this.tab.addEventListener("TabClose", this);
+    this.browserWindow.addEventListener("unload", this);
 
     // Swap page content from the current tab into a viewport within RDM
     this.swap = swapToInnerBrowser({
       tab: this.tab,
       containerURL: TOOL_URL,
       getInnerBrowser: Task.async(function* (containerBrowser) {
         let toolWindow = ui.toolWindow = containerBrowser.contentWindow;
         toolWindow.addEventListener("message", ui);
@@ -343,24 +344,26 @@ ResponsiveUI.prototype = {
     if (this.destroying) {
       return false;
     }
     this.destroying = true;
 
     // If our tab is about to be closed, there's not enough time to exit
     // gracefully, but that shouldn't be a problem since the tab will go away.
     // So, skip any yielding when we're about to close the tab.
-    let isTabClosing = options && options.reason == "TabClose";
+    let isWindowClosing = options && options.reason === "unload";
+    let isTabClosing = (options && options.reason === "TabClose") || isWindowClosing;
 
     // Ensure init has finished before starting destroy
     if (!isTabClosing) {
       yield this.inited;
     }
 
     this.tab.removeEventListener("TabClose", this);
+    this.browserWindow.removeEventListener("unload", this);
     this.toolWindow.removeEventListener("message", this);
 
     if (!isTabClosing) {
       // Stop the touch event simulator if it was running
       yield this.emulationFront.clearTouchEventsOverride();
 
       // Notify the inner browser to stop the frame script
       yield message.request(this.toolWindow, "stop-frame-script");
@@ -376,18 +379,20 @@ ResponsiveUI.prototype = {
 
     // Close the debugger client used to speak with emulation actor
     let clientClosed = this.client.close();
     if (!isTabClosing) {
       yield clientClosed;
     }
     this.client = this.emulationFront = null;
 
-    // Undo the swap and return the content back to a normal tab
-    swap.stop();
+    if (!isWindowClosing) {
+      // Undo the swap and return the content back to a normal tab
+      swap.stop();
+    }
 
     this.destroyed = true;
 
     return true;
   }),
 
   connectToServer: Task.async(function* () {
     if (!DebuggerServer.initialized) {
@@ -402,16 +407,17 @@ ResponsiveUI.prototype = {
 
   handleEvent(event) {
     let { browserWindow, tab } = this;
 
     switch (event.type) {
       case "message":
         this.handleMessage(event);
         break;
+      case "unload":
       case "TabClose":
         ResponsiveUIManager.closeIfNeeded(browserWindow, tab, {
           reason: event.type,
         });
         break;
     }
   },
 
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -3,16 +3,17 @@ tags = devtools
 subsuite = devtools
 # !e10s: RDM only works for remote tabs
 skip-if = !e10s
 support-files =
   devices.json
   doc_page_state.html
   geolocation.html
   head.js
+  touch.html
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/framework/test/shared-redux-head.js
   !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_device_change.js]
@@ -31,8 +32,9 @@ support-files =
 [browser_resize_cmd.js]
 [browser_screenshot_button.js]
 [browser_shutdown_close_sync.js]
 [browser_toolbox_computed_view.js]
 [browser_toolbox_rule_view.js]
 [browser_toolbox_swap_browsers.js]
 [browser_touch_simulation.js]
 [browser_viewport_basics.js]
+[browser_window_close.js]
--- a/devtools/client/responsive.html/test/browser/browser_touch_simulation.js
+++ b/devtools/client/responsive.html/test/browser/browser_touch_simulation.js
@@ -1,25 +1,240 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test global touch simulation button
 
-const TEST_URL = "data:text/html;charset=utf-8,";
+const TEST_URL = `${URL_ROOT}touch.html`;
+const PREF_DOM_META_VIEWPORT_ENABLED = "dom.meta-viewport.enabled";
 
 addRDMTask(TEST_URL, function* ({ ui }) {
-  let { store, document } = ui.toolWindow;
-  let touchButton = document.querySelector("#global-touch-simulation-button");
+  yield waitBootstrap(ui);
+  yield testWithNoTouch(ui);
+  yield enableTouchSimulation(ui);
+  yield testWithTouch(ui);
+  yield testWithMetaViewportEnabled(ui);
+  yield testWithMetaViewportDisabled(ui);
+  testTouchButton(ui);
+});
+
+function* testWithNoTouch(ui) {
+  yield injectEventUtils(ui);
+  yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
+    let { EventUtils } = content;
+
+    let div = content.document.querySelector("div");
+    let x = 0, y = 0;
+
+    info("testWithNoTouch: Initial test parameter and mouse mouse outside div");
+    x = -1; y = -1;
+    yield EventUtils.synthesizeMouse(div, x, y,
+          { type: "mousemove", isSynthesized: false }, content);
+    div.style.transform = "none";
+    div.style.backgroundColor = "";
+
+    info("testWithNoTouch: Move mouse into the div element");
+    yield EventUtils.synthesizeMouseAtCenter(div,
+          { type: "mousemove", isSynthesized: false }, content);
+    is(div.style.backgroundColor, "red", "mouseenter or mouseover should work");
+
+    info("testWithNoTouch: Drag the div element");
+    yield EventUtils.synthesizeMouseAtCenter(div,
+          { type: "mousedown", isSynthesized: false }, content);
+    x = 100; y = 100;
+    yield EventUtils.synthesizeMouse(div, x, y,
+          { type: "mousemove", isSynthesized: false }, content);
+    is(div.style.transform, "none", "touchmove shouldn't work");
+    yield EventUtils.synthesizeMouse(div, x, y,
+          { type: "mouseup", isSynthesized: false }, content);
+
+    info("testWithNoTouch: Move mouse out of the div element");
+    x = -1; y = -1;
+    yield EventUtils.synthesizeMouse(div, x, y,
+          { type: "mousemove", isSynthesized: false }, content);
+    is(div.style.backgroundColor, "blue", "mouseout or mouseleave should work");
+
+    info("testWithNoTouch: Click the div element");
+    yield EventUtils.synthesizeClick(div);
+    is(div.dataset.isDelay, "false",
+      "300ms delay between touch events and mouse events should not work");
+  });
+}
+
+function* testWithTouch(ui) {
+  yield injectEventUtils(ui);
+
+  yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
+    let { EventUtils } = content;
+
+    let div = content.document.querySelector("div");
+    let x = 0, y = 0;
+
+    info("testWithTouch: Initial test parameter and mouse mouse outside div");
+    x = -1; y = -1;
+    yield EventUtils.synthesizeMouse(div, x, y,
+          { type: "mousemove", isSynthesized: false }, content);
+    div.style.transform = "none";
+    div.style.backgroundColor = "";
+
+    info("testWithTouch: Move mouse into the div element");
+    yield EventUtils.synthesizeMouseAtCenter(div,
+          { type: "mousemove", isSynthesized: false }, content);
+    isnot(div.style.backgroundColor, "red",
+      "mouseenter or mouseover should not work");
 
-  // Wait until the viewport has been added
-  yield waitUntilState(store, state => state.viewports.length == 1);
-  yield waitForFrameLoad(ui, TEST_URL);
+    info("testWithTouch: Drag the div element");
+    yield EventUtils.synthesizeMouseAtCenter(div,
+          { type: "mousedown", isSynthesized: false }, content);
+    x = 100; y = 100;
+    yield EventUtils.synthesizeMouse(div, x, y,
+          { type: "mousemove", isSynthesized: false }, content);
+    isnot(div.style.transform, "none", "touchmove should work");
+    yield EventUtils.synthesizeMouse(div, x, y,
+          { type: "mouseup", isSynthesized: false }, content);
+
+    info("testWithTouch: Move mouse out of the div element");
+    x = -1; y = -1;
+    yield EventUtils.synthesizeMouse(div, x, y,
+          { type: "mousemove", isSynthesized: false }, content);
+    isnot(div.style.backgroundColor, "blue",
+      "mouseout or mouseleave should not work");
+  });
+}
+
+function* testWithMetaViewportEnabled(ui) {
+  yield SpecialPowers.pushPrefEnv({set: [[PREF_DOM_META_VIEWPORT_ENABLED, true]]});
+
+  yield injectEventUtils(ui);
+
+  yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
+    let { synthesizeClick } = content.EventUtils;
+
+    let meta = content.document.querySelector("meta[name=viewport]");
+    let div = content.document.querySelector("div");
+    div.dataset.isDelay = "false";
+
+    info("testWithMetaViewportEnabled: " +
+         "click the div element with <meta name='viewport'>");
+    meta.content = "";
+    yield synthesizeClick(div);
+    is(div.dataset.isDelay, "true",
+      "300ms delay between touch events and mouse events should work");
+
+    info("testWithMetaViewportEnabled: " +
+         "click the div element with " +
+         "<meta name='viewport' content='user-scalable=no'>");
+    meta.content = "user-scalable=no";
+    yield synthesizeClick(div);
+    is(div.dataset.isDelay, "false",
+      "300ms delay between touch events and mouse events should not work");
+
+    info("testWithMetaViewportEnabled: " +
+         "click the div element with " +
+         "<meta name='viewport' content='minimum-scale=maximum-scale'>");
+    meta.content = "minimum-scale=maximum-scale";
+    yield synthesizeClick(div);
+    is(div.dataset.isDelay, "false",
+      "300ms delay between touch events and mouse events should not work");
+
+    info("testWithMetaViewportEnabled: " +
+         "click the div element with " +
+         "<meta name='viewport' content='width=device-width'>");
+    meta.content = "width=device-width";
+    yield synthesizeClick(div);
+    is(div.dataset.isDelay, "false",
+      "300ms delay between touch events and mouse events should not work");
+  });
+}
+
+function* testWithMetaViewportDisabled(ui) {
+  yield SpecialPowers.pushPrefEnv({set: [[PREF_DOM_META_VIEWPORT_ENABLED, false]]});
+
+  yield injectEventUtils(ui);
+
+  yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
+    let { synthesizeClick } = content.EventUtils;
+
+    let meta = content.document.querySelector("meta[name=viewport]");
+    let div = content.document.querySelector("div");
+    div.dataset.isDelay = "false";
+
+    info("testWithMetaViewportDisabled: click the div with <meta name='viewport'>");
+    meta.content = "";
+    yield synthesizeClick(div);
+    is(div.dataset.isDelay, "true",
+      "300ms delay between touch events and mouse events should work");
+  });
+}
+
+function testTouchButton(ui) {
+  let { document } = ui.toolWindow;
+  let touchButton = document.querySelector("#global-touch-simulation-button");
 
   ok(!touchButton.classList.contains("active"),
     "Touch simulation is not active by default.");
 
   touchButton.click();
 
   ok(touchButton.classList.contains("active"),
     "Touch simulation is started on click.");
-});
+
+  touchButton.click();
+
+  ok(!touchButton.classList.contains("active"),
+    "Touch simulation is stopped on click.");
+}
+
+function* waitBootstrap(ui) {
+  let { store } = ui.toolWindow;
+
+  yield waitUntilState(store, state => state.viewports.length == 1);
+  yield waitForFrameLoad(ui, TEST_URL);
+}
+
+function* injectEventUtils(ui) {
+  yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
+    if ("EventUtils" in content) {
+      return;
+    }
+
+    let EventUtils = content.EventUtils = {};
+
+    EventUtils.window = {};
+    EventUtils.parent = EventUtils.window;
+    /* eslint-disable camelcase */
+    EventUtils._EU_Ci = Components.interfaces;
+    EventUtils._EU_Cc = Components.classes;
+    /* eslint-enable camelcase */
+    // EventUtils' `sendChar` function relies on the navigator to synthetize events.
+    EventUtils.navigator = content.navigator;
+    EventUtils.KeyboardEvent = content.KeyboardEvent;
+
+    EventUtils.synthesizeClick = element => new Promise(resolve => {
+      element.addEventListener("click", function onClick() {
+        element.removeEventListener("click", onClick);
+        resolve();
+      });
+
+      EventUtils.synthesizeMouseAtCenter(element,
+        { type: "mousedown", isSynthesized: false }, content);
+      EventUtils.synthesizeMouseAtCenter(element,
+        { type: "mouseup", isSynthesized: false }, content);
+    });
+
+    Services.scriptloader.loadSubScript(
+      "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
+  });
+}
+
+const enableTouchSimulation = ui => new Promise(
+  Task.async(function* (resolve) {
+    let browser = ui.getViewportBrowser();
+
+    browser.addEventListener("mozbrowserloadend", function onLoad() {
+      browser.removeEventListener("mozbrowserloadend", onLoad);
+      resolve();
+    });
+
+    yield ui.updateTouchSimulation(true);
+  }));
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_window_close.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(function* () {
+  let newWindowPromise = BrowserTestUtils.waitForNewWindow();
+  window.open("data:text/html;charset=utf-8,", "_blank");
+  let newWindow = yield newWindowPromise;
+
+  newWindow.focus();
+  yield once(newWindow.gBrowser, "load", true);
+
+  let tab = newWindow.gBrowser.selectedTab;
+  yield openRDM(tab);
+
+  // Close the window on a tab with an active responsive design UI and
+  // wait for the UI to gracefully shutdown.  This has leaked the window
+  // in the past.
+  ok(ResponsiveUIManager.isActiveForTab(tab),
+     "ResponsiveUI should be active for tab when the window is closed");
+  let offPromise = once(ResponsiveUIManager, "off");
+  yield BrowserTestUtils.closeWindow(newWindow);
+  yield offPromise;
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/touch.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+
+<meta charset="utf-8" />
+<meta name="viewport" />
+<title>test</title>
+
+
+<style>
+  div {
+    border :1px solid red;
+    width: 100px; height: 100px;
+  }
+</style>
+
+<div data-is-delay="false"></div>
+
+<script type="text/javascript;version=1.8">
+  "use strict";
+  let div = document.querySelector("div");
+  let initX, initY;
+  let previousEvent = "", touchendTime = 0;
+  let updatePreviousEvent = function (e) {
+    previousEvent = e.type;
+  };
+
+  div.style.transform = "none";
+  div.style.backgroundColor = "";
+
+  div.addEventListener("touchstart", function (evt) {
+    let touch = evt.changedTouches[0];
+    initX = touch.pageX;
+    initY = touch.pageY;
+    updatePreviousEvent(evt);
+  }, true);
+
+  div.addEventListener("touchmove", function (evt) {
+    let touch = evt.changedTouches[0];
+    let deltaX = touch.pageX - initX;
+    let deltaY = touch.pageY - initY;
+    div.style.transform = "translate(" + deltaX + "px, " + deltaY + "px)";
+    updatePreviousEvent(evt);
+  }, true);
+
+  div.addEventListener("touchend", function (evt) {
+    if (!evt.touches.length) {
+      div.style.transform = "none";
+    }
+    touchendTime = performance.now();
+    updatePreviousEvent(evt);
+  }, true);
+
+  div.addEventListener("mouseenter", function (evt) {
+    div.style.backgroundColor = "red";
+    updatePreviousEvent(evt);
+  }, true);
+  div.addEventListener("mouseover", function(evt) {
+    div.style.backgroundColor = "red";
+    updatePreviousEvent(evt);
+  }, true);
+
+  div.addEventListener("mouseout", function (evt) {
+    div.style.backgroundColor = "blue";
+    updatePreviousEvent(evt);
+  }, true);
+
+  div.addEventListener("mouseleave", function (evt) {
+    div.style.backgroundColor = "blue";
+    updatePreviousEvent(evt);
+  }, true);
+
+  div.addEventListener("mousedown", function (evt) {
+    if (previousEvent === "touchend" && touchendTime !== 0) {
+      let now = performance.now();
+      div.dataset.isDelay = ((now - touchendTime) >= 300);
+    } else {
+      div.dataset.isDelay = false;
+    }
+    updatePreviousEvent(evt);
+  }, true);
+
+  div.addEventListener("mousemove", updatePreviousEvent, true);
+
+  div.addEventListener("mouseup", updatePreviousEvent, true);
+
+  div.addEventListener("click", updatePreviousEvent, true);
+</script>
--- a/gfx/skia/skia/include/core/SkTypes.h
+++ b/gfx/skia/skia/include/core/SkTypes.h
@@ -4,16 +4,29 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
 #ifndef SkTypes_DEFINED
 #define SkTypes_DEFINED
 
 // IWYU pragma: begin_exports
+
+// In at least two known scenarios when using GCC with libc++:
+//  * GCC 4.8 targeting ARMv7 with NEON
+//  * GCC 4.9 targeting ARMv8 64 bit
+// we need to typedef float float32_t (or include <arm_neon.h> which does that)
+// before #including <memory>. This makes no sense.  I'm not very interested in
+// understanding why... these are old, bizarre platform configuration that we
+// should just let die.
+#if defined(MOZ_B2G) && defined(__GNUC__) && __GNUC__ == 4
+    typedef float float32_t;
+    #include <memory>
+#endif
+
 #include "SkPreConfig.h"
 #include "SkUserConfig.h"
 #include "SkPostConfig.h"
 #include <stddef.h>
 #include <stdint.h>
 
 #if defined(SK_ARM_HAS_NEON)
     #include <arm_neon.h>
--- a/old-configure.in
+++ b/old-configure.in
@@ -2487,17 +2487,17 @@ AC_SUBST(MOZ_B2G_VERSION)
 dnl ========================================================
 dnl Ensure Android SDK and build-tools versions depending on
 dnl mobile target.
 dnl ========================================================
 
 if test -z "$gonkdir" ; then
     case "$MOZ_BUILD_APP" in
     mobile/android)
-        MOZ_ANDROID_SDK(23, 23.0.3)
+        MOZ_ANDROID_SDK(23, 23.0.3 23.0.1)
         ;;
     esac
 fi
 
 dnl ========================================================
 dnl =
 dnl = Toolkit Options
 dnl =
--- a/security/manager/locales/en-US/chrome/pippki/deviceManager.dtd
+++ b/security/manager/locales/en-US/chrome/pippki/deviceManager.dtd
@@ -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/. -->
 
 <!ENTITY devmgr.title                           "Device Manager">
-<!-- LOCALIZATION NOTE (devmgr.style): This is CSS style for Device Manager
+<!-- LOCALIZATION NOTE (devmgr.style2): This is CSS style for Device Manager
      window size. Don't translate "width" nor "height". Adjust the numbers
      to make window contents fit. -->
-<!ENTITY devmgr.style                           "width: 52em; height: 32em;">
+<!ENTITY devmgr.style2                          "width: 67em; height: 32em;">
 
 <!ENTITY devmgr.devlist.label                   "Security Modules and Devices">
 <!ENTITY devmgr.details.title                   "Details">
 <!ENTITY devmgr.details.title2                  "Value">
 
 <!ENTITY devmgr.button.login.label              "Log In">
 <!ENTITY devmgr.button.login.accesskey          "n">
 <!ENTITY devmgr.button.logout.label             "Log Out">
--- a/security/manager/pki/resources/content/device_manager.xul
+++ b/security/manager/pki/resources/content/device_manager.xul
@@ -9,81 +9,81 @@
 <!ENTITY % deviceManangerDTD SYSTEM "chrome://pippki/locale/deviceManager.dtd">
 %deviceManangerDTD;
 <!ENTITY % pippkiDTD SYSTEM "chrome://pippki/locale/pippki.dtd" >
 %pippkiDTD;
 ]>
 
 <dialog id="devicemanager"
 	windowtype="mozilla:devicemanager"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         title="&devmgr.title;"
-        style="&devmgr.style;"
+        style="&devmgr.style2;"
         persist="screenX screenY width height"
         onload="LoadModules();"
         onunload="DeregisterSmartCardObservers();"
         buttons="accept">
 
 <stringbundleset id="stringbundleset">
   <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
   <stringbundle id="pipnss_bundle" src="chrome://pipnss/locale/pipnss.properties"/>
 </stringbundleset>
 
 <script type="application/javascript" src="chrome://pippki/content/device_manager.js"/>
 
 <grid flex="1" style="margin:5px">
   <columns>
-    <column flex="1"/> 
-    <column flex="3"/> 
+    <column flex="1"/>
+    <column flex="3"/>
     <column/>
   </columns>
   <rows>
     <row flex="1">
       <vbox> <!-- List of devices -->
         <tree id="device_tree" seltype="single"
               onselect="enableButtons();" hidecolumnpicker="true"
               flex="1" style="min-width: 15em">
-          <treecols> 
+          <treecols>
             <treecol id="deviceCol" flex="1" primary="true" label="&devmgr.devlist.label;"/>
           </treecols>
           <treechildren id="device_list"/>
         </tree>
       </vbox> <!-- / List of devices -->
       <vbox> <!-- Device status -->
         <tree id="info_tree" seltype="single" hidecolumnpicker="true"
               flex="1" style="min-width: 10em">
-          <treecols> 
-            <treecol id="title1Col" flex="5" primary="true" label="&devmgr.details.title;"/> 
-            <treecol id="title2Col" flex="7" label="&devmgr.details.title2;"/> 
+          <treecols>
+            <treecol id="title1Col" flex="5" primary="true" label="&devmgr.details.title;"/>
+            <treecol id="title2Col" flex="7" label="&devmgr.details.title2;"/>
           </treecols>
           <treechildren id="info_list"/>
         </tree>
       </vbox> <!-- / Device status -->
       <vbox> <!-- Buttons for manipulating devices -->
-        <button id="login_button" 
+        <button id="login_button"
                 label="&devmgr.button.login.label;"
                 accesskey="&devmgr.button.login.accesskey;"
-                oncommand="doLogin();" disabled="true"/> 
-        <button id="logout_button" 
+                oncommand="doLogin();" disabled="true"/>
+        <button id="logout_button"
                 label="&devmgr.button.logout.label;"
                 accesskey="&devmgr.button.logout.accesskey;"
-                oncommand="doLogout();" disabled="true"/> 
-        <button id="change_pw_button" 
+                oncommand="doLogout();" disabled="true"/>
+        <button id="change_pw_button"
                 label="&devmgr.button.changepw.label;"
                 accesskey="&devmgr.button.changepw.accesskey;"
-                oncommand="changePassword();" disabled="true"/> 
-        <button id="load_button" 
+                oncommand="changePassword();" disabled="true"/>
+        <button id="load_button"
                 label="&devmgr.button.load.label;"
                 accesskey="&devmgr.button.load.accesskey;"
-                oncommand="doLoad();"/> 
-        <button id="unload_button" 
+                oncommand="doLoad();"/>
+        <button id="unload_button"
                 label="&devmgr.button.unload.label;"
                 accesskey="&devmgr.button.unload.accesskey;"
-                oncommand="doUnload();" disabled="true"/> 
-        <button id="fipsbutton" 
+                oncommand="doUnload();" disabled="true"/>
+        <button id="fipsbutton"
                 label=""
                 accesskey="&devmgr.button.fips.accesskey;"
                 oncommand="toggleFIPS();"/>
       </vbox> <!-- / Buttons for manipulating devices -->
     </row>
   </rows>
 </grid>
 
--- a/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl
+++ b/toolkit/components/passwordmgr/nsILoginManagerPrompter.idl
@@ -10,21 +10,25 @@ interface nsIDOMElement;
 interface nsIDOMWindow;
 
 [scriptable, uuid(425f73b9-b2db-4e8a-88c5-9ac2512934ce)]
 interface nsILoginManagerPrompter : nsISupports {
   /**
    * Initialize the prompter. Must be called before using other interfaces.
    *
    * @param aWindow
-   *        The in which the user is doing some login-related action that's
+   *        The window in which the user is doing some login-related action that's
    *        resulting in a need to prompt them for something. The prompt
    *        will be associated with this window (or, if a notification bar
    *        is being used, topmost opener in some cases).
    *
+   *        aWindow can be null if there is no associated window, e.g. in a JSM
+   *        or Sandbox. In this case there will be no checkbox to save the login
+   *        since the window is needed to know if this is a private context.
+   *
    *        If this window is a content window, the corresponding window and browser
    *        elements will be calculated. If this window is a chrome window, the
    *        corresponding browser element needs to be set using setBrowser.
    */
   void init(in nsIDOMWindow aWindow);
 
   /**
    * The browser this prompter is being created for.
--- a/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
+++ b/toolkit/components/passwordmgr/nsLoginManagerPrompter.js
@@ -264,16 +264,17 @@ LoginManagerPrompter.prototype = {
     if (this._chromeWindow) {
       return PrivateBrowsingUtils.isWindowPrivate(this._chromeWindow);
     }
     // If we don't that we're in private browsing mode if the caller did
     // not provide a window.  The callers which really care about this
     // will indeed pass down a window to us, and for those who don't,
     // we can just assume that we don't want to save the entered login
     // information.
+    this.log("We have no chromeWindow so assume we're in a private context");
     return true;
   },
 
 
 
 
   /* ---------- nsIAuthPrompt prompts ---------- */
 
@@ -687,18 +688,22 @@ LoginManagerPrompter.prototype = {
   },
 
 
 
 
   /* ---------- nsILoginManagerPrompter prompts ---------- */
 
 
-  init : function (aWindow, aFactory) {
-    if (aWindow instanceof Ci.nsIDOMChromeWindow) {
+  init : function (aWindow = null, aFactory = null) {
+    if (!aWindow) {
+      // There may be no applicable window e.g. in a Sandbox or JSM.
+      this._chromeWindow = null;
+      this._browser = null;
+    } else if (aWindow instanceof Ci.nsIDOMChromeWindow) {
       this._chromeWindow = aWindow;
       // needs to be set explicitly using setBrowser
       this._browser = null;
     } else {
       let {win, browser} = this._getChromeWindow(aWindow);
       this._chromeWindow = win;
       this._browser = browser;
     }
--- a/toolkit/components/passwordmgr/test/mochitest/mochitest.ini
+++ b/toolkit/components/passwordmgr/test/mochitest/mochitest.ini
@@ -44,14 +44,16 @@ skip-if = toolkit == 'android' # autocom
 [test_input_events.html]
 [test_input_events_for_identical_values.html]
 [test_maxlength.html]
 [test_passwords_in_type_password.html]
 [test_prompt.html]
 skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts
 [test_prompt_http.html]
 skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts
+[test_prompt_noWindow.html]
+skip-if = e10s || toolkit == 'android' # Tests desktop prompts. e10s: bug 1217876
 [test_prompt_promptAuth.html]
 skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts
 [test_prompt_promptAuth_proxy.html]
 skip-if = e10s || os == "linux" || toolkit == 'android' # Tests desktop prompts
 [test_recipe_login_fields.html]
 [test_xhr_2.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/test/mochitest/test_prompt_noWindow.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test HTTP auth prompts by loading authenticate.sjs with no window</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="pwmgr_common.js"></script>
+  <script type="text/javascript" src="prompt_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+
+<div id="content" style="display: none">
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// Force parent to not look for tab-modal prompts, as they're not used for auth prompts.
+isTabModal = false;
+
+let chromeScript = runInParent(SimpleTest.getTestFileURL("pwmgr_common.js"));
+
+runInParent(() => {
+  const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+  Cu.import("resource://gre/modules/Services.jsm");
+
+  let login = Cc["@mozilla.org/login-manager/loginInfo;1"].
+              createInstance(Ci.nsILoginInfo);
+  login.init("http://mochi.test:8888", null, "mochitest",
+             "mochiuser1", "mochipass1", "", "");
+  Services.logins.addLogin(login);
+});
+
+add_task(function* test_sandbox_xhr() {
+  let state = {
+    msg         : "http://mochi.test:8888 is requesting your username and password. The site says: “mochitest”",
+    title       : "Authentication Required",
+    textValue   : "mochiuser1",
+    passValue   : "mochipass1",
+    iconClass   : "authentication-icon question-icon",
+    titleHidden : true,
+    textHidden  : false,
+    passHidden  : false,
+    checkHidden : true,
+    checkMsg    : "",
+    checked     : false,
+    focused     : "textField",
+    defButton   : "button0",
+  };
+  let action = {
+    buttonClick : "ok",
+  };
+  let promptDone = handlePrompt(state, action);
+
+  let url = new URL("authenticate.sjs?user=mochiuser1&pass=mochipass1", window.location.href);
+  let sandboxConstructor = SpecialPowers.Cu.Sandbox;
+  let sandbox = new sandboxConstructor(this, {wantXrays: true});
+  function sandboxedRequest(url) {
+    let req = SpecialPowers.Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+                           .createInstance(SpecialPowers.Ci.nsIXMLHttpRequest);
+    req.open("GET", url, true);
+    req.send(null);
+  }
+
+  let loginModifiedPromise = promiseStorageChanged(["modifyLogin"]);
+  sandbox.sandboxedRequest = sandboxedRequest(url);
+  info("send the XHR request in the sandbox");
+  SpecialPowers.Cu.evalInSandbox("sandboxedRequest;", sandbox);
+
+  yield promptDone;
+  info("prompt shown, waiting for metadata updates");
+  // Ensure the timeLastUsed and timesUsed metadata are updated.
+  yield loginModifiedPromise;
+});
+</script>
+</pre>
+</body>
+</html>
--- a/toolkit/content/tests/chrome/findbar_window.xul
+++ b/toolkit/content/tests/chrome/findbar_window.xul
@@ -147,16 +147,17 @@
       yield openFindbar();
       yield testFailedStringReset();
       gFindBar.close();
       yield testQuickFindClose();
       // TODO: This doesn't seem to work when the findbar is connected to a
       //       remote browser element.
       if (!gBrowser.hasAttribute("remote"))
         yield testFindAgainNotFound();
+      yield testToggleEntireWord();
     }
 
     function* testFindbarSelection() {
       function checkFindbarState(aTestName, aExpSelection) {
         ok(!gFindBar.hidden, "testFindbarSelection: failed to open findbar: " + aTestName);
         ok(document.commandDispatcher.focusedElement == gFindBar._findField.inputField,
            "testFindbarSelection: find field is not focused: " + aTestName);
         if (!gHasFindClipboard) {
@@ -230,19 +231,19 @@
         };
         setTimeout(_delayedCheckStatusText, 100);
       });
     }
 
     function promiseFindResult() {
       return new Promise(resolve => {
         let listener = {
-          onFindResult: function() {
+          onFindResult: function(result) {
             gFindBar.browser.finder.removeResultListener(listener);
-            resolve();
+            resolve(result);
           }
         };
         gFindBar.browser.finder.addResultListener(listener);
       });
     }
 
     function promiseMatchesCountResult() {
       return new Promise(resolve => {
@@ -645,16 +646,34 @@
       yield enterStringIntoFindField(SEARCH_TEXT);
       gFindBar.close();
       ok(gFindBar.hidden, "The findbar is closed.");
       promise = promiseFindResult();
       gFindBar.onFindAgainCommand();
       yield promise;
       ok(gFindBar.hidden, "Successful Find Again leaves the find bar closed.");
     }
+
+    function* testToggleEntireWord() {
+      yield openFindbar();
+      let promise = promiseFindResult();
+      yield enterStringIntoFindField("Tex", false);
+      let result = yield promise;
+      is(result.result, Ci.nsITypeAheadFind.FIND_FOUND, "Text should be found");
+
+      yield new Promise(resolve => setTimeout(resolve, ITERATOR_TIMEOUT));
+      promise = promiseFindResult();
+      let check = gFindBar.getElement("find-entire-word");
+      check.click();
+      result = yield promise;
+      is(result.result, Ci.nsITypeAheadFind.FIND_NOTFOUND, "Text should NOT be found");
+
+      check.click();
+      gFindBar.close(true);
+    }
   ]]></script>
 
   <commandset>
     <command id="cmd_find" oncommand="document.getElementById('FindToolbar').onFindCommand();"/>
   </commandset>
   <browser type="content-primary" flex="1" id="content" src="about:blank"/>
   <browser type="content-primary" flex="1" id="content-remote" remote="true" src="about:blank"/>
   <findbar id="FindToolbar" browserid="content"/>
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -194,17 +194,17 @@
                          oncommand="_setCaseSensitivity(this.checked ? 1 : 0);"
                          type="checkbox"
                          xbl:inherits="accesskey=matchcaseaccesskey"/>
       <xul:toolbarbutton anonid="find-entire-word"
                          class="findbar-entire-word findbar-button tabbable"
                          label="&entireWord.label;"
                          accesskey="&entireWord.accesskey;"
                          tooltiptext="&entireWord.tooltiptext;"
-                         oncommand="_setEntireWord(this.checked);"
+                         oncommand="toggleEntireWord(this.checked);"
                          type="checkbox"
                          xbl:inherits="accesskey=entirewordaccesskey"/>
       <xul:label anonid="match-case-status" class="findbar-find-fast"/>
       <xul:label anonid="entire-word-status" class="findbar-find-fast"/>
       <xul:label anonid="found-matches" class="findbar-find-fast found-matches" hidden="true"/>
       <xul:image anonid="find-status-icon" class="findbar-find-fast find-status-icon"/>
       <xul:description anonid="find-status"
                        control="findbar-textbox"
@@ -338,19 +338,17 @@
             case "accessibility.typeaheadfind.linksonly":
               this._self._typeAheadLinksOnly = prefsvc.getBoolPref(aPrefName);
               break;
             case "accessibility.typeaheadfind.casesensitive":
               this._self._setCaseSensitivity(prefsvc.getIntPref(aPrefName));
               break;
             case "findbar.entireword":
               this._self._entireWord = prefsvc.getBoolPref(aPrefName);
-              this._self._updateEntireWord();
-              // Update the matches count.
-              this._updateMatchesCount(this.nsITypeAheadFind.FIND_FOUND);
+              this._self.toggleEntireWord(this._self._entireWord, true);
               break;
             case "findbar.highlightAll":
               this._self.toggleHighlight(prefsvc.getBoolPref(aPrefName), true);
               break;
             case "findbar.modalHighlight":
               this._self._useModalHighlight = prefsvc.getBoolPref(aPrefName);
               if (this._self.browser.finder)
                 this._self.browser.finder.onModalHighlightChange(this._self._useModalHighlight);
@@ -633,17 +631,17 @@
 
           this._dispatchFindEvent("casesensitivitychange");
         ]]></body>
       </method>
 
       <!--
         - Updates the entire-word mode of the findbar and its UI.
         -->
-      <method name="_updateEntireWord">
+      <method name="_setEntireWord">
         <body><![CDATA[
           let entireWord = this._entireWord;
           let checkbox = this.getElement("find-entire-word");
           let statusLabel = this.getElement("entire-word-status");
           checkbox.checked = entireWord;
 
           statusLabel.value = entireWord ? this._entireWordStr : "";
 
@@ -657,27 +655,28 @@
         ]]></body>
       </method>
 
       <!--
         - Sets the findbar entire-word mode
         - @param aEntireWord (boolean)
         - Whether or not entire-word mode should be turned on.
         -->
-      <method name="_setEntireWord">
+      <method name="toggleEntireWord">
         <parameter name="aEntireWord"/>
+        <parameter name="aFromPrefObserver"/>
         <body><![CDATA[
-          let prefsvc =
-            Components.classes["@mozilla.org/preferences-service;1"]
-                      .getService(Components.interfaces.nsIPrefBranch);
+          if (!aFromPrefObserver) {
+            // Just set the pref; our observer will change the find bar behavior.
+            this._prefsvc.setBoolPref("findbar.entireword", aEntireWord);
+            return;
+          }
 
-          // Just set the pref; our observer will change the find bar behavior.
-          prefsvc.setBoolPref("findbar.entireword", aEntireWord);
-
-          this._maybeHighlightAll();
+          this._findFailedString = null;
+          this._find();
         ]]></body>
       </method>
 
       <field name="_strBundle">null</field>
       <property name="strBundle">
         <getter><![CDATA[
           if (!this._strBundle) {
             this._strBundle =
@@ -966,17 +965,17 @@
             if (node == wrapper || node == foundMatches)
                continue;
             node.hidden = showMinimalUI;
           }
           this.getElement("find-next").hidden =
             this.getElement("find-previous").hidden = showMinimalUI;
           foundMatches.hidden = showMinimalUI || !foundMatches.value;
           this._updateCaseSensitivity();
-          this._updateEntireWord();
+          this._setEntireWord();
           this._setHighlightAll();
 
           if (showMinimalUI)
             this._findField.classList.add("minimal");
           else
             this._findField.classList.remove("minimal");
 
           if (this._findMode == this.FIND_TYPEAHEAD)
@@ -1014,17 +1013,17 @@
             // initial prefilling is ignored if it hasn't happened yet.
             if (this._startFindDeferred) {
               this._startFindDeferred.resolve();
               this._startFindDeferred = null;
             }
 
             this._enableFindButtons(val);
             this._updateCaseSensitivity(val);
-            this._updateEntireWord();
+            this._setEntireWord();
 
             this.browser.finder.fastFind(val, this._findMode == this.FIND_LINKS,
                                          this._findMode != this.FIND_NORMAL);
           }
 
           if (this._findMode != this.FIND_NORMAL)
             this._setFindCloseTimeout();
 
--- a/toolkit/modules/FinderHighlighter.jsm
+++ b/toolkit/modules/FinderHighlighter.jsm
@@ -401,18 +401,17 @@ FinderHighlighter.prototype = {
         if (this.iterator._areParamsEqual(params, dict.lastIteratorParams))
           return;
         if (params)
           this.highlight(true, params.word, params.linksOnly);
       }
       return;
     }
 
-    // Place the match placeholder on top of the current found range.
-    if (data.result == Ci.nsITypeAheadFind.FIND_NOTFOUND || !foundRange) {
+    if (data.result == Ci.nsITypeAheadFind.FIND_NOTFOUND || !data.searchString || !foundRange) {
       this.hide();
       return;
     }
 
     if (foundRange !== dict.currentFoundRange || data.findAgain) {
       dict.currentFoundRange = foundRange;
 
       let textContent = this._getRangeContentArray(foundRange);
@@ -441,17 +440,17 @@ FinderHighlighter.prototype = {
     if (outlineNode) {
       if (dict.animation)
         dict.animation.finish();
       dict.animation = outlineNode.setAnimationForElement(kModalOutlineId,
         Cu.cloneInto(kModalOutlineAnim.keyframes, window), kModalOutlineAnim.duration);
       dict.animation.onfinish = () => dict.animation = null;
     }
 
-    if (this._highlightAll && data.searchString)
+    if (this._highlightAll)
       this.highlight(true, data.searchString, data.linksOnly);
   },
 
   /**
    * Invalidates the list by clearing the map of highlighted ranges that we
    * keep to build the mask for.
    */
   clear(window = null) {
--- a/toolkit/modules/tests/browser/browser_FinderHighlighter.js
+++ b/toolkit/modules/tests/browser/browser_FinderHighlighter.js
@@ -385,8 +385,37 @@ add_task(function* testHideOnLocationCha
     insertCalls: [0, 0],
     removeCalls: [1, 2]
   });
   yield BrowserTestUtils.loadURI(browser, url);
   yield promise;
 
   yield BrowserTestUtils.removeTab(tab);
 });
+
+add_task(function* testHideOnClear() {
+  let url = kFixtureBaseURL + "file_FinderSample.html";
+  yield BrowserTestUtils.withNewTab(url, function* (browser) {
+    let findbar = gBrowser.getFindBar();
+    yield promiseOpenFindbar(findbar);
+
+    let word = "Roland";
+    let expectedResult = {
+      rectCount: 1,
+      insertCalls: [2, 4],
+      removeCalls: [1, 2]
+    };
+    let promise = promiseTestHighlighterOutput(browser, word, expectedResult);
+    yield promiseEnterStringIntoFindField(findbar, word);
+    yield promise;
+
+    yield new Promise(resolve => setTimeout(resolve, kIteratorTimeout));
+    promise = promiseTestHighlighterOutput(browser, "", {
+      rectCount: 0,
+      insertCalls: [0, 0],
+      removeCalls: [1, 2]
+    });
+    findbar.clear();
+    yield promise;
+
+    findbar.close(true);
+  });
+});