Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 28 Jul 2015 16:30:58 +0200
changeset 286612 27732f823726d44659740f89ac3275bf441f8a55
parent 286611 e3c15b9f89b3c331805b4f1a83f62805f32bbb9a (current diff)
parent 286575 bc589dd18ad57ab24bd70070855c4c9568796cc5 (diff)
child 286613 2a2f1d5a647be9639eb0dc6a35eb37c9f60e3565
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone42.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 mozilla-central to mozilla-inbound
browser/themes/windows/tabbrowser/connecting@2x.png
browser/themes/windows/tabbrowser/loading@2x.png
browser/themes/windows/tabbrowser/tab-background-end-preWin10.png
browser/themes/windows/tabbrowser/tab-background-end-preWin10@2x.png
browser/themes/windows/tabbrowser/tab-background-middle-preWin10.png
browser/themes/windows/tabbrowser/tab-background-middle-preWin10@2x.png
browser/themes/windows/tabbrowser/tab-background-start-preWin10.png
browser/themes/windows/tabbrowser/tab-background-start-preWin10@2x.png
modules/libpref/init/all.js
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "14e32276025b0310d3e89027320cf4b2a24cedfb", 
+        "git_revision": "a8319543833b08e986fa3ed590faeb2624bf1683", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "8b7476489ad4a91eb41856b43934364fe8ef68be", 
+    "revision": "4fc562ca01a3e8e17dd042af9076f1fe7bc8f91d", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="14e32276025b0310d3e89027320cf4b2a24cedfb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a8319543833b08e986fa3ed590faeb2624bf1683"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -349,16 +349,20 @@ pref("browser.urlbar.suggest.history",  
 pref("browser.urlbar.suggest.bookmark",             true);
 pref("browser.urlbar.suggest.openpage",             true);
 #ifdef NIGHTLY_BUILD
 pref("browser.urlbar.suggest.searches",             true);
 #else
 pref("browser.urlbar.suggest.searches",             false);
 #endif
 
+// Limit the number of characters sent to the current search engine to fetch
+// suggestions.
+pref("browser.urlbar.maxCharsForSearchSuggestions", 20);
+
 // Restrictions to current suggestions can also be applied (intersection).
 // Typed suggestion works only if history is set to true.
 pref("browser.urlbar.suggest.history.onlyTyped",    false);
 
 pref("browser.urlbar.formatting.enabled", true);
 pref("browser.urlbar.trimURLs", true);
 
 pref("browser.altClickSave", false);
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -129,16 +129,17 @@ skip-if = e10s # Bug 1093153 - no about:
 [browser_action_searchengine.js]
 [browser_action_searchengine_alias.js]
 [browser_addKeywordSearch.js]
 [browser_search_favicon.js]
 [browser_alltabslistener.js]
 [browser_autocomplete_a11y_label.js]
 skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir (works on its own)
 [browser_autocomplete_cursor.js]
+[browser_autocomplete_edit_completed.js]
 [browser_autocomplete_enter_race.js]
 [browser_autocomplete_no_title.js]
 [browser_autocomplete_autoselect.js]
 [browser_autocomplete_oldschool_wrap.js]
 [browser_autocomplete_tag_star_visibility.js]
 [browser_backButtonFitts.js]
 skip-if = os == "mac" # The Fitt's Law back button is not supported on OS X
 [browser_beforeunload_duplicate_dialogs.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_autocomplete_edit_completed.js
@@ -0,0 +1,53 @@
+add_task(function*() {
+  yield PlacesTestUtils.clearHistory();
+
+  yield PlacesTestUtils.addVisits([
+    { uri: makeURI("http://example.com/foo") },
+    { uri: makeURI("http://example.com/foo/bar") },
+  ]);
+
+  Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
+  yield* do_test();
+  Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", false);
+  yield* do_test();
+});
+
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("browser.urlbar.unifiedcomplete");
+  yield PlacesTestUtils.clearHistory();
+});
+
+function* do_test() {
+  gBrowser.selectedTab = gBrowser.addTab("about:blank");
+  gURLBar.focus();
+
+  yield promiseAutocompleteResultPopup("http://example.com");
+
+  let popup = gURLBar.popup;
+  let list = popup.richlistbox;
+  let initialIndex = list.selectedIndex;
+
+  info("Key Down to select the next item.");
+  EventUtils.synthesizeKey("VK_DOWN", {});
+
+  let nextIndex = initialIndex + 1;
+  let nextValue = gURLBar.controller.getFinalCompleteValueAt(nextIndex);
+  is(list.selectedIndex, nextIndex, "The next item is selected.");
+  is(gURLBar.value, nextValue, "The selected URL is completed.");
+
+  info("Press backspace");
+  EventUtils.synthesizeKey("VK_BACK_SPACE", {});
+  yield promiseSearchComplete();
+
+  let editedValue = gURLBar.value;
+  is(list.selectedIndex, initialIndex, "The initial index is selected again.");
+  isnot(editedValue, nextValue, "The URL has changed.");
+
+  info("Press return to load edited URL.");
+  EventUtils.synthesizeKey("VK_RETURN", {});
+  yield Promise.all([
+    promisePopupHidden(gURLBar.popup),
+    waitForDocLoadAndStopIt("http://" + editedValue)]);
+
+  gBrowser.removeTab(gBrowser.selectedTab);
+}
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -1,35 +1,40 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
  * vim: sw=2 ts=2 sts=2 et */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-const Cr = Components.results;
+const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 
 const FILE_INPUT_STREAM_CID = "@mozilla.org/network/file-input-stream;1";
 
 const S100NS_FROM1601TO1970 = 0x19DB1DED53E8000;
 const S100NS_PER_MS = 10;
 
+const AUTH_TYPE = {
+  SCHEME_HTML: 0,
+  SCHEME_BASIC: 1,
+  SCHEME_DIGEST: 2
+};
+
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
+                                  "resource://gre/modules/OSCrypto.jsm");
 
 /**
  * Convert Chrome time format to Date object
  *
  * @param   aTime
  *          Chrome time 
  * @return  converted Date object
  * @note    Google Chrome uses FILETIME / 10 as time.
@@ -88,17 +93,21 @@ ChromeProfileMigrator.prototype = Object
 ChromeProfileMigrator.prototype.getResources =
   function Chrome_getResources(aProfile) {
     if (this._chromeUserDataFolder) {
       let profileFolder = this._chromeUserDataFolder.clone();
       profileFolder.append(aProfile.id);
       if (profileFolder.exists()) {
         let possibleResources = [GetBookmarksResource(profileFolder),
                                  GetHistoryResource(profileFolder),
-                                 GetCookiesResource(profileFolder)];
+                                 GetCookiesResource(profileFolder),
+#ifdef XP_WIN
+                                 GetWindowsPasswordsResource(profileFolder)
+#endif
+                                 ];
         return [r for each (r in possibleResources) if (r != null)];
       }
     }
     return [];
   };
 
 Object.defineProperty(ChromeProfileMigrator.prototype, "sourceProfiles", {
   get: function Chrome_sourceProfiles() {
@@ -348,13 +357,108 @@ function GetCookiesResource(aProfileFold
           aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
         },
       });
       stmt.finalize();
     }
   }
 }
 
+function GetWindowsPasswordsResource(aProfileFolder) {
+  let loginFile = aProfileFolder.clone();
+  loginFile.append("Login Data");
+  if (!loginFile.exists())
+    return null;
+
+  return {
+    type: MigrationUtils.resourceTypes.PASSWORDS,
+
+    migrate(aCallback) {
+      let dbConn = Services.storage.openUnsharedDatabase(loginFile);
+      let stmt = dbConn.createAsyncStatement(`
+        SELECT origin_url, action_url, username_element, username_value,
+        password_element, password_value, signon_realm, scheme, date_created,
+        times_used FROM logins WHERE blacklisted_by_user = 0`);
+      let crypto = new OSCrypto();
+
+      stmt.executeAsync({
+        _rowToLoginInfo(row) {
+          let loginInfo = {
+            username: row.getResultByName("username_value"),
+            password: crypto.decryptData(row.getResultByName("password_value")),
+            hostName: NetUtil.newURI(row.getResultByName("origin_url")).prePath,
+            submitURL: null,
+            httpRealm: null,
+            usernameElement: row.getResultByName("username_element"),
+            passwordElement: row.getResultByName("password_element"),
+            timeCreated: chromeTimeToDate(row.getResultByName("date_created") + 0).getTime(),
+            timesUsed: row.getResultByName("times_used") + 0,
+          };
+
+          switch (row.getResultByName("scheme")) {
+            case AUTH_TYPE.SCHEME_HTML:
+              loginInfo.submitURL = NetUtil.newURI(row.getResultByName("action_url")).prePath;
+              break;
+            case AUTH_TYPE.SCHEME_BASIC:
+            case AUTH_TYPE.SCHEME_DIGEST:
+              // signon_realm format is URIrealm, so we need remove URI
+              loginInfo.httpRealm = row.getResultByName("signon_realm")
+                                    .substring(loginInfo.hostName.length + 1);
+              break;
+            default:
+              throw new Error("Login data scheme type not supported: " +
+                              row.getResultByName("scheme"));
+          }
+
+          return loginInfo;
+        },
+
+        handleResult(aResults) {
+          for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) {
+            try {
+              let loginInfo = this._rowToLoginInfo(row);
+              let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
+
+              login.init(loginInfo.hostName, loginInfo.submitURL, loginInfo.httpRealm,
+                         loginInfo.username, loginInfo.password, loginInfo.usernameElement,
+                         loginInfo.passwordElement);
+              login.QueryInterface(Ci.nsILoginMetaInfo);
+              login.timeCreated = loginInfo.timeCreated;
+              login.timeLastUsed = loginInfo.timeCreated;
+              login.timePasswordChanged = loginInfo.timeCreated;
+              login.timesUsed = loginInfo.timesUsed;
+
+              // Add the login only if there's not an existing entry
+              let logins = Services.logins.findLogins({}, login.hostname,
+                                                      login.formSubmitURL,
+                                                      login.httpRealm);
+
+              // Bug 1187190: Password changes should be propagated depending on timestamps.
+              if (!logins.some(l => login.matches(l, true))) {
+                Services.logins.addLogin(login);
+              }
+            } catch (e) {
+              Cu.reportError(e);
+            }
+          }
+        },
+
+        handleError(aError) {
+          Cu.reportError("Async statement execution returned with '" +
+                         aError.result + "', '" + aError.message + "'");
+        },
+
+        handleCompletion(aReason) {
+          dbConn.asyncClose();
+          aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED);
+          crypto.finalize();
+        },
+      });
+      stmt.finalize();
+    }
+  };
+}
+
 ChromeProfileMigrator.prototype.classDescription = "Chrome Profile Migrator";
 ChromeProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=chrome";
 ChromeProfileMigrator.prototype.classID = Components.ID("{4cec1de4-1671-4fc3-a53e-6c539dc77a26}");
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ChromeProfileMigrator]);
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..914149c710a8c748b97543c541aaedfee74aa1f6
GIT binary patch
literal 22528
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
V5Zr`7u?69O0s;a80)oF6{ts6yA$0%%
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/test_Chrome_passwords.js
@@ -0,0 +1,213 @@
+Cu.import("resource://gre/modules/OSCrypto.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const PROFILE = {
+  id: "Default",
+  name: "Person 1",
+};
+
+const TEST_LOGINS = [
+  {
+    id: 1, // id of the row in the chrome login db
+    username: "username",
+    password: "password",
+    hostname: "https://c9.io",
+    formSubmitURL: "https://c9.io",
+    httpRealm: null,
+    usernameField: "inputEmail",
+    passwordField: "inputPassword",
+    timeCreated: 1437418416037,
+    timePasswordChanged: 1437418416037,
+    timesUsed: 1,
+  },
+  {
+    id: 2,
+    username: "username@gmail.com",
+    password: "password2",
+    hostname: "https://accounts.google.com",
+    formSubmitURL: "https://accounts.google.com",
+    httpRealm: null,
+    usernameField: "Email",
+    passwordField: "Passwd",
+    timeCreated: 1437418446598,
+    timePasswordChanged: 1437418446598,
+    timesUsed: 6,
+  },
+  {
+    id: 3,
+    username: "username",
+    password: "password3",
+    hostname: "https://www.facebook.com",
+    formSubmitURL: "https://www.facebook.com",
+    httpRealm: null,
+    usernameField: "email",
+    passwordField: "pass",
+    timeCreated: 1437418478851,
+    timePasswordChanged: 1437418478851,
+    timesUsed: 1,
+  },
+  {
+    id: 4,
+    username: "user",
+    password: "password",
+    hostname: "http://httpbin.org",
+    formSubmitURL: null,
+    httpRealm: "me@kennethreitz.com", // Digest auth.
+    usernameField: "",
+    passwordField: "",
+    timeCreated: 1437787462368,
+    timePasswordChanged: 1437787462368,
+    timesUsed: 1,
+  },
+  {
+    id: 5,
+    username: "buser",
+    password: "bpassword",
+    hostname: "http://httpbin.org",
+    formSubmitURL: null,
+    httpRealm: "Fake Realm", // Basic auth.
+    usernameField: "",
+    passwordField: "",
+    timeCreated: 1437787539233,
+    timePasswordChanged: 1437787539233,
+    timesUsed: 1,
+  },
+];
+
+let crypto = new OSCrypto();
+let dbConn;
+
+function promiseSetPassword(login) {
+  return new Promise((resolve, reject) => {
+    let stmt = dbConn.createAsyncStatement(`
+      UPDATE logins
+      SET password_value = :password_value
+      WHERE rowid = :rowid
+    `);
+    let passwordValue = crypto.encryptData(login.password);
+    stmt.bindBlobByName("password_value", passwordValue, passwordValue.length);
+    stmt.params.rowid = login.id;
+
+    stmt.executeAsync({
+      handleError(aError) {
+        reject("Error with the query: " + aError.message);
+      },
+
+      handleCompletion(aReason) {
+        if (aReason === Ci.mozIStorageStatementCallback.REASON_FINISHED){
+          resolve();
+        } else {
+          reject("Query has failed: " + aReason);
+        }
+      },
+    });
+    stmt.finalize();
+  });
+}
+
+function checkLoginsAreEqual(passwordManagerLogin, chromeLogin, id) {
+  passwordManagerLogin.QueryInterface(Ci.nsILoginMetaInfo);
+
+  Assert.equal(passwordManagerLogin.username, chromeLogin.username,
+               "The two logins ID " + id + " have the same username");
+  Assert.equal(passwordManagerLogin.password, chromeLogin.password,
+               "The two logins ID " + id + " have the same password");
+  Assert.equal(passwordManagerLogin.hostname, chromeLogin.hostname,
+               "The two logins ID " + id + " have the same hostname");
+  Assert.equal(passwordManagerLogin.formSubmitURL, chromeLogin.formSubmitURL,
+               "The two logins ID " + id + " have the same formSubmitURL");
+  Assert.equal(passwordManagerLogin.httpRealm, chromeLogin.httpRealm,
+               "The two logins ID " + id + " have the same httpRealm");
+  Assert.equal(passwordManagerLogin.usernameField, chromeLogin.usernameField,
+               "The two logins ID " + id + " have the same usernameElement");
+  Assert.equal(passwordManagerLogin.passwordField, chromeLogin.passwordField,
+               "The two logins ID " + id + " have the same passwordElement");
+  Assert.equal(passwordManagerLogin.timeCreated, chromeLogin.timeCreated,
+               "The two logins ID " + id + " have the same timeCreated");
+  Assert.equal(passwordManagerLogin.timePasswordChanged, chromeLogin.timePasswordChanged,
+               "The two logins ID " + id + " have the same timePasswordChanged");
+  Assert.equal(passwordManagerLogin.timesUsed, chromeLogin.timesUsed,
+               "The two logins ID " + id + " have the same timesUsed");
+}
+
+function generateDifferentLogin(login) {
+  let newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
+
+  newLogin.init(login.hostname, login.formSubmitURL, null,
+                login.username, login.password + 1, login.usernameField + 1,
+                login.passwordField + 1);
+  newLogin.QueryInterface(Ci.nsILoginMetaInfo);
+  newLogin.timeCreated = login.timeCreated + 1;
+  newLogin.timePasswordChanged = login.timePasswordChanged + 1;
+  newLogin.timesUsed = login.timesUsed + 1;
+  return newLogin;
+}
+
+add_task(function* setup() {
+  let loginDataFile = do_get_file("AppData/Local/Google/Chrome/User Data/Default/Login Data");
+  dbConn = Services.storage.openUnsharedDatabase(loginDataFile);
+  registerFakePath("LocalAppData", do_get_file("AppData/Local/"));
+
+  do_register_cleanup(() => {
+    Services.logins.removeAllLogins();
+    dbConn.asyncClose();
+    crypto.finalize();
+  });
+});
+
+add_task(function* test_importIntoEmptyDB() {
+  for (let login of TEST_LOGINS) {
+    yield promiseSetPassword(login);
+  }
+
+  let migrator = MigrationUtils.getMigrator("chrome");
+  Assert.ok(migrator.sourceExists, "Sanity check the source exists");
+
+  let logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, 0, "There are no logins initially");
+
+  // Migrate the logins.
+  yield promiseMigration(migrator, MigrationUtils.resourceTypes.PASSWORDS, PROFILE);
+
+  logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, TEST_LOGINS.length, "Check login count after importing the data");
+
+  for (let i = 0; i < TEST_LOGINS.length; i++) {
+    checkLoginsAreEqual(logins[i], TEST_LOGINS[i], i + 1);
+  }
+});
+
+// Test that existing logins for the same primary key don't get overwritten
+add_task(function* test_importExistingLogins() {
+  let migrator = MigrationUtils.getMigrator("chrome");
+  Assert.ok(migrator.sourceExists, "Sanity check the source exists");
+
+  Services.logins.removeAllLogins();
+  let logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, 0, "There are no logins after removing all of them");
+
+  let newLogins = [];
+
+  // Create 3 new logins that are different but where the key properties are still the same.
+  for (let i = 0; i < 3; i++) {
+    newLogins.push(generateDifferentLogin(TEST_LOGINS[i]));
+    Services.logins.addLogin(newLogins[i]);
+  }
+
+  logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, newLogins.length, "Check login count after the insertion");
+
+  for (let i = 0; i < newLogins.length; i++) {
+    checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
+  }
+  // Migrate the logins.
+  yield promiseMigration(migrator, MigrationUtils.resourceTypes.PASSWORDS, PROFILE);
+
+  logins = Services.logins.getAllLogins({});
+  Assert.equal(logins.length, TEST_LOGINS.length,
+               "Check there are still the same number of logins after re-importing the data");
+
+  for (let i = 0; i < newLogins.length; i++) {
+    checkLoginsAreEqual(logins[i], newLogins[i], i + 1);
+  }
+});
--- a/browser/components/migration/tests/unit/xpcshell.ini
+++ b/browser/components/migration/tests/unit/xpcshell.ini
@@ -1,17 +1,20 @@
 [DEFAULT]
 head = head_migration.js
 tail =
 firefox-appdir = browser
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 support-files =
   Library/**
+  AppData/**
 
 [test_Chrome_cookies.js]
 skip-if = os != "mac" # Relies on ULibDir
+[test_Chrome_passwords.js]
+skip-if = os != "win"
 [test_fx_fhr.js]
 [test_IE_bookmarks.js]
 skip-if = os != "win"
 [test_IE_cookies.js]
 skip-if = os != "win"
 [test_Safari_bookmarks.js]
 skip-if = os != "mac"
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -2850,16 +2850,23 @@ TextPropertyEditor.prototype = {
    * Boolean indicating if the name or value is being currently edited.
    */
   get editing() {
     return !!(this.nameSpan.inplaceEditor || this.valueSpan.inplaceEditor ||
       this.ruleView.tooltips.isEditing) || this.popup.isOpen;
   },
 
   /**
+   * Get the rule to the current text property
+   */
+  get rule() {
+    return this.prop.rule;
+  },
+
+  /**
    * Create the property editor's DOM.
    */
   _create: function() {
     this.element = this.doc.createElementNS(HTML_NS, "li");
     this.element.classList.add("ruleview-property");
     this.element._textPropertyEditor = this;
 
     this.container = createChild(this.element, "div", {
@@ -2999,17 +3006,17 @@ TextPropertyEditor.prototype = {
   },
 
   /**
    * Get the path from which to resolve requests for this
    * rule's stylesheet.
    * @return {string} the stylesheet's href.
    */
   get sheetHref() {
-    let domRule = this.prop.rule.domRule;
+    let domRule = this.rule.domRule;
     if (domRule) {
       return domRule.href || domRule.nodeHref;
     }
   },
 
   /**
    * Get the URI from which to resolve relative requests for
    * this rule's stylesheet.
@@ -3063,24 +3070,24 @@ TextPropertyEditor.prototype = {
       this.element.classList.remove("ruleview-overridden");
     }
 
     let name = this.prop.name;
     this.nameSpan.textContent = name;
 
     // Combine the property's value and priority into one string for
     // the value.
-    let store = this.prop.rule.elementStyle.store;
-    let val = store.userProperties.getProperty(this.prop.rule.style, name,
+    let store = this.rule.elementStyle.store;
+    let val = store.userProperties.getProperty(this.rule.style, name,
                                                this.prop.value);
     if (this.prop.priority) {
       val += " !" + this.prop.priority;
     }
 
-    let propDirty = store.userProperties.contains(this.prop.rule.style, name);
+    let propDirty = store.userProperties.contains(this.rule.style, name);
 
     if (propDirty) {
       this.element.setAttribute("dirty", "");
     } else {
       this.element.removeAttribute("dirty");
     }
 
     const sharedSwatchClass = "ruleview-swatch ";
@@ -3151,17 +3158,16 @@ TextPropertyEditor.prototype = {
     // Populate the computed styles.
     this._updateComputed();
 
     // Update the rule property highlight.
     this.ruleView._updatePropertyHighlight(this);
   },
 
   _onStartEditing: function() {
-    this.element.classList.remove("ruleview-overridden");
     this._previewValue(this.prop.value);
   },
 
   /**
    * Populate the list of computed styles.
    */
   _updateComputed: function() {
     // Clear out existing viewers.
@@ -3289,33 +3295,48 @@ TextPropertyEditor.prototype = {
    * commits it.
    *
    * @param {string} aValue
    *        The value contained in the editor.
    * @param {boolean} aCommit
    *        True if the change should be applied.
    */
   _onNameDone: function(aValue, aCommit) {
-    if (aCommit && !this.ruleEditor.isEditing) {
-      // Unlike the value editor, if a name is empty the entire property
-      // should always be removed.
-      if (aValue.trim() === "") {
-        this.remove();
-      } else {
-        // Adding multiple rules inside of name field overwrites the current
-        // property with the first, then adds any more onto the property list.
-        let properties = parseDeclarations(aValue);
-
-        if (properties.length) {
-          this.prop.setName(properties[0].name);
-          if (properties.length > 1) {
-            this.prop.setValue(properties[0].value, properties[0].priority);
-            this.ruleEditor.addProperties(properties.slice(1), this.prop);
-          }
-        }
+    if ((!aCommit && this.ruleEditor.isEditing) ||
+        this.committed.name == aValue) {
+      // Disable the property if the property was originally disabled.
+      if (!this.prop.enabled) {
+        this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
+      }
+
+      return;
+    }
+
+    // Unlike the value editor, if a name is empty the entire property
+    // should always be removed.
+    if (aValue.trim() === "") {
+      this.remove();
+      return;
+    }
+
+    // Adding multiple rules inside of name field overwrites the current
+    // property with the first, then adds any more onto the property list.
+    let properties = parseDeclarations(aValue);
+
+    if (properties.length) {
+      this.prop.setName(properties[0].name);
+      this.committed.name = this.prop.name;
+
+      if (!this.prop.enabled) {
+        this.prop.setEnabled(true);
+      }
+
+      if (properties.length > 1) {
+        this.prop.setValue(properties[0].value, properties[0].priority);
+        this.ruleEditor.addProperties(properties.slice(1), this.prop);
       }
     }
   },
 
   /**
    * Remove property from style and the editors from DOM.
    * Begin editing next available property.
    */
@@ -3337,45 +3358,48 @@ TextPropertyEditor.prototype = {
    * Called when a value editor closes.  If the user pressed escape,
    * revert to the value this property had before editing.
    *
    * @param {string} aValue
    *        The value contained in the editor.
    * @param {bool} aCommit
    *        True if the change should be applied.
    */
-  _onValueDone: function(aValue, aCommit) {
-    if (!aCommit && !this.ruleEditor.isEditing) {
+  _onValueDone: function(aValue="", aCommit) {
+    let parsedProperties = this._getValueAndExtraProperties(aValue);
+    let val = parseSingleValue(parsedProperties.firstValue);
+    let isValueUnchanged =
+      !parsedProperties.propertiesToAdd.length &&
+      this.committed.value == val.value &&
+      this.committed.priority == val.priority;
+
+    if ((!aCommit && !this.ruleEditor.isEditing) || isValueUnchanged) {
       // A new property should be removed when escape is pressed.
       if (this.removeOnRevert) {
         this.remove();
       } else {
-        // update the editor back to committed value
-        this.update();
-
-        // undo the preview in content style
-        this.ruleEditor.rule.previewPropertyValue(this.prop,
-          this.prop.value, this.prop.priority);
+        // Disable the property if the property was originally disabled.
+        this.rule.setPropertyEnabled(this.prop, this.prop.enabled);
       }
       return;
     }
 
-    let {propertiesToAdd, firstValue} =
-        this._getValueAndExtraProperties(aValue);
-
     // First, set this property value (common case, only modified a property)
-    let val = parseSingleValue(firstValue);
-
     this.prop.setValue(val.value, val.priority);
+
+    if (!this.prop.enabled) {
+      this.prop.setEnabled(true);
+    }
+
     this.removeOnRevert = false;
     this.committed.value = this.prop.value;
     this.committed.priority = this.prop.priority;
 
     // If needed, add any new properties after this.prop.
-    this.ruleEditor.addProperties(propertiesToAdd, this.prop);
+    this.ruleEditor.addProperties(parsedProperties.propertiesToAdd, this.prop);
 
     // If the name or value is not actively being edited, and the value is
     // empty, then remove the whole property.
     // A timeout is used here to accurately check the state, since the inplace
     // editor `done` and `destroy` events fire before the next editor
     // is focused.
     if (val.value.trim() === "") {
       setTimeout(() => {
@@ -3439,16 +3463,19 @@ TextPropertyEditor.prototype = {
    */
   _previewValue: function(aValue) {
     // Since function call is throttled, we need to make sure we are still
     // editing, and any selector modifications have been completed
     if (!this.editing || this.ruleEditor.isEditing) {
       return;
     }
 
+    this.element.classList.remove("ruleview-overridden");
+    this.enable.style.visibility = "hidden";
+
     let val = parseSingleValue(aValue);
     this.ruleEditor.rule.previewPropertyValue(this.prop, val.value,
                                               val.priority);
   },
 
   /**
    * Validate this property. Does it make sense for this value to be assigned
    * to this property name? This does not apply the property value
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -87,16 +87,20 @@ skip-if = e10s # Bug 1039528: "inspect e
 [browser_ruleview_custom.js]
 [browser_ruleview_edit-property-commit.js]
 [browser_ruleview_edit-property-computed.js]
 [browser_ruleview_edit-property-increments.js]
 [browser_ruleview_edit-property-order.js]
 [browser_ruleview_edit-property_01.js]
 [browser_ruleview_edit-property_02.js]
 [browser_ruleview_edit-property_03.js]
+[browser_ruleview_edit-property_04.js]
+[browser_ruleview_edit-property_05.js]
+[browser_ruleview_edit-property_06.js]
+[browser_ruleview_edit-property_07.js]
 [browser_ruleview_edit-selector-commit.js]
 [browser_ruleview_edit-selector_01.js]
 [browser_ruleview_edit-selector_02.js]
 [browser_ruleview_edit-selector_03.js]
 [browser_ruleview_edit-selector_04.js]
 [browser_ruleview_edit-selector_05.js]
 [browser_ruleview_eyedropper.js]
 [browser_ruleview_filtereditor-appears-on-swatch-click.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_04.js
@@ -0,0 +1,88 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that a disabled property is previewed when the property name or value
+// editor is focused and the property remains disabled when the escaping out of
+// the property editor.
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  "</style>",
+  "<div id='testid'>Styled Node</div>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testDisableProperty(inspector, view);
+});
+
+function* testDisableProperty(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  info("Disabling a property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  let newValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: "background-color"
+  });
+  is(newValue, "", "background-color should have been unset.");
+
+  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+    propEditor.nameSpan, "VK_ESCAPE");
+  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+    propEditor.valueSpan, "VK_ESCAPE");
+  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+    propEditor.valueSpan, "VK_TAB");
+  yield testPreviewDisableProperty(view, ruleEditor, propEditor,
+    propEditor.valueSpan, "VK_RETURN");
+}
+
+function* testPreviewDisableProperty(view, ruleEditor, propEditor,
+    editableField, commitKey) {
+  let editor = yield focusEditableField(view, editableField);
+  yield ruleEditor.rule._applyingModifications;
+
+  ok(!propEditor.element.classList.contains("ruleview-overridden"),
+    "property is not overridden.");
+  is(propEditor.enable.style.visibility, "hidden",
+    "property enable checkbox is hidden.");
+
+  let newValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: "background-color"
+  });
+  is(newValue, "blue", "background-color should have been previewed.");
+
+  let onBlur = once(editor.input, "blur");
+  EventUtils.synthesizeKey(commitKey, {}, view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  ok(!propEditor.prop.enabled, "property is disabled.");
+  ok(propEditor.element.classList.contains("ruleview-overridden"),
+    "property is overridden.");
+  is(propEditor.enable.style.visibility, "visible",
+    "property enable checkbox is visible.");
+  ok(!propEditor.enable.getAttribute("checked"),
+    "property enable checkbox is not checked.");
+
+  newValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: "background-color"
+  });
+  is(newValue, "", "background-color should have been unset.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_05.js
@@ -0,0 +1,86 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that a disabled property is re-enabled if the property name or value is
+// modified
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: blue;",
+  "}",
+  "</style>",
+  "<div id='testid'>Styled Node</div>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testEditingDisableProperty(inspector, view);
+});
+
+function* testEditingDisableProperty(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  info("Disabling background-color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  let newValue = yield getRulePropertyValue("background-color");
+  is(newValue, "", "background-color should have been unset.");
+
+  yield focusEditableField(view, propEditor.nameSpan);
+
+  info("Entering a new property name, including : to commit and " +
+       "focus the value");
+  let onValueFocus = once(ruleEditor.element, "focus", true);
+  EventUtils.sendString("border-color:", view.styleWindow);
+  yield onValueFocus;
+  yield ruleEditor.rule._applyingModifications;
+
+  info("Escape editing the property value");
+  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
+  yield ruleEditor.rule._applyingModifications;
+
+  newValue = yield getRulePropertyValue("border-color");
+  is(newValue, "blue", "border-color should have been set.");
+
+  ok(propEditor.prop.enabled, "border-color property is enabled.");
+  ok(!propEditor.element.classList.contains("ruleview-overridden"),
+    "border-color is not overridden");
+
+  info("Disabling border-color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  newValue = yield getRulePropertyValue("border-color");
+  is(newValue, "", "border-color should have been unset.");
+
+  info("Enter a new property value for the border-color property");
+  let editor = yield focusEditableField(view, propEditor.valueSpan);
+  let onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  newValue = yield getRulePropertyValue("border-color");
+  is(newValue, "red", "new border-color should have been set.");
+
+  ok(propEditor.prop.enabled, "border-color property is enabled.");
+  ok(!propEditor.element.classList.contains("ruleview-overridden"),
+    "border-color is not overridden");
+}
+
+function* getRulePropertyValue(name) {
+  let propValue = yield executeInContent("Test:GetRulePropertyValue", {
+    styleSheetIndex: 0,
+    ruleIndex: 0,
+    name: name
+  });
+  return propValue;
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_06.js
@@ -0,0 +1,64 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that editing a property's priority is behaving correctly, and disabling
+// and editing the property will re-enable the property.
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "body {",
+  "  background-color: green !important;",
+  "}",
+  "body {",
+  "  background-color: red;",
+  "}",
+  "</style>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("body", inspector);
+  yield testEditPropertyPriorityAndDisable(inspector, view);
+});
+
+function* testEditPropertyPriorityAndDisable(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(0, 128, 0)", "green background color is set.");
+
+  let editor = yield focusEditableField(view, propEditor.valueSpan);
+  let onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red !important;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  is(propEditor.valueSpan.textContent, "red !important",
+    "'red !important' property value is correctly set.");
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(255, 0, 0)", "red background color is set.");
+
+  info("Disabling red background color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(0, 128, 0)", "green background color is set.");
+
+  editor = yield focusEditableField(view, propEditor.valueSpan);
+  onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  is(propEditor.valueSpan.textContent, "red",
+    "'red' property value is correctly set.");
+  ok(propEditor.prop.enabled, "red background-color property is enabled.");
+  is((yield getComputedStyleProperty("body", null, "background-color")),
+    "rgb(0, 128, 0)", "green background color is set.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_07.js
@@ -0,0 +1,55 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that adding multiple values will enable the property even if the
+// property does not change, and that the extra values are added correctly.
+
+let TEST_URI = [
+  "<style type='text/css'>",
+  "#testid {",
+  "  background-color: red;",
+  "}",
+  "</style>",
+  "<div id='testid'>Styled Node</div>",
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#testid", inspector);
+  yield testEditDisableProperty(inspector, view);
+});
+
+function* testEditDisableProperty(inspector, view) {
+  let ruleEditor = getRuleViewRuleEditor(view, 1);
+  let propEditor = ruleEditor.rule.textProps[0].editor;
+
+  info("Disabling red background color property");
+  propEditor.enable.click();
+  yield ruleEditor.rule._applyingModifications;
+
+  ok(!propEditor.prop.enabled, "red background-color property is disabled.");
+
+  let editor = yield focusEditableField(view, propEditor.valueSpan);
+  let onBlur = once(editor.input, "blur");
+  EventUtils.sendString("red; color: red;", view.styleWindow);
+  yield onBlur;
+  yield ruleEditor.rule._applyingModifications;
+
+  is(propEditor.valueSpan.textContent, "red",
+    "'red' property value is correctly set.");
+  ok(propEditor.prop.enabled, "red background-color property is enabled.");
+  is((yield getComputedStyleProperty("#testid", null, "background-color")),
+    "rgb(255, 0, 0)", "red background color is set.");
+
+  propEditor = ruleEditor.rule.textProps[1].editor;
+  is(propEditor.nameSpan.textContent, "color",
+    "new 'color' property name is correctly set.");
+  is(propEditor.valueSpan.textContent, "red",
+    "new 'red' property value is correctly set.");
+  is((yield getComputedStyleProperty("#testid", null, "color")),
+    "rgb(255, 0, 0)", "red color is set.");
+}
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2023,16 +2023,24 @@ richlistitem[type~="action"][actiontype=
   .tab-background-end[visuallyselected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-start@2x.png);
   }
 
   .tab-background-end[visuallyselected=true]:-moz-locale-dir(ltr)::after,
   .tab-background-start[visuallyselected=true]:-moz-locale-dir(rtl)::after {
     background-image: url(chrome://browser/skin/tabbrowser/tab-stroke-end@2x.png);
   }
+
+  .tab-throbber[busy] {
+    list-style-image: url("chrome://browser/skin/tabbrowser/connecting@2x.png");
+  }
+
+  .tab-throbber[progress] {
+    list-style-image: url("chrome://browser/skin/tabbrowser/loading@2x.png");
+  }
 }
 
 /* Remove border between tab strip and navigation toolbar on Windows 10+ */
 @media not all and (-moz-os-version: windows-xp) {
   @media not all and (-moz-os-version: windows-vista) {
     @media not all and (-moz-os-version: windows-win7) {
       @media not all and (-moz-os-version: windows-win8) {
         .tab-background-end[visuallyselected=true]::after,
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -325,32 +325,40 @@ browser.jar:
         skin/classic/browser/tabbrowser/newtab@2x.png                (tabbrowser/newtab@2x.png)
         skin/classic/browser/tabbrowser/newtab-XPVista7.png          (tabbrowser/newtab-XPVista7.png)
         skin/classic/browser/tabbrowser/newtab-XPVista7@2x.png       (tabbrowser/newtab-XPVista7@2x.png)
         skin/classic/browser/tabbrowser/newtab-inverted.png          (tabbrowser/newtab-inverted.png)
         skin/classic/browser/tabbrowser/newtab-inverted@2x.png       (tabbrowser/newtab-inverted@2x.png)
         skin/classic/browser/tabbrowser/newtab-inverted-XPVista7.png (tabbrowser/newtab-inverted-XPVista7.png)
         skin/classic/browser/tabbrowser/newtab-inverted-XPVista7@2x.png (tabbrowser/newtab-inverted-XPVista7@2x.png)
         skin/classic/browser/tabbrowser/connecting.png               (tabbrowser/connecting.png)
+        skin/classic/browser/tabbrowser/connecting@2x.png            (tabbrowser/connecting@2x.png)
         skin/classic/browser/tabbrowser/crashed.svg                  (../shared/tabbrowser/crashed.svg)
         skin/classic/browser/tabbrowser/loading.png                  (tabbrowser/loading.png)
+        skin/classic/browser/tabbrowser/loading@2x.png               (tabbrowser/loading@2x.png)
         skin/classic/browser/tabbrowser/tab-active-middle.png        (tabbrowser/tab-active-middle.png)
         skin/classic/browser/tabbrowser/tab-active-middle@2x.png     (tabbrowser/tab-active-middle@2x.png)
         skin/classic/browser/tabbrowser/tab-arrow-left.png           (tabbrowser/tab-arrow-left.png)
         skin/classic/browser/tabbrowser/tab-arrow-left@2x.png        (tabbrowser/tab-arrow-left@2x.png)
         skin/classic/browser/tabbrowser/tab-arrow-left-XPVista7.png  (tabbrowser/tab-arrow-left-XPVista7.png)
         skin/classic/browser/tabbrowser/tab-arrow-left-XPVista7@2x.png  (tabbrowser/tab-arrow-left-XPVista7@2x.png)
         skin/classic/browser/tabbrowser/tab-arrow-left-inverted.png  (tabbrowser/tab-arrow-left-inverted.png)
         skin/classic/browser/tabbrowser/tab-arrow-left-inverted@2x.png  (tabbrowser/tab-arrow-left-inverted@2x.png)
         skin/classic/browser/tabbrowser/tab-background-start.png     (tabbrowser/tab-background-start.png)
         skin/classic/browser/tabbrowser/tab-background-start@2x.png  (tabbrowser/tab-background-start@2x.png)
         skin/classic/browser/tabbrowser/tab-background-middle.png    (tabbrowser/tab-background-middle.png)
         skin/classic/browser/tabbrowser/tab-background-middle@2x.png (tabbrowser/tab-background-middle@2x.png)
         skin/classic/browser/tabbrowser/tab-background-end.png       (tabbrowser/tab-background-end.png)
         skin/classic/browser/tabbrowser/tab-background-end@2x.png    (tabbrowser/tab-background-end@2x.png)
+        skin/classic/browser/tabbrowser/tab-background-start-preWin10.png     (tabbrowser/tab-background-start-preWin10.png)
+        skin/classic/browser/tabbrowser/tab-background-start-preWin10@2x.png  (tabbrowser/tab-background-start-preWin10@2x.png)
+        skin/classic/browser/tabbrowser/tab-background-middle-preWin10.png    (tabbrowser/tab-background-middle-preWin10.png)
+        skin/classic/browser/tabbrowser/tab-background-middle-preWin10@2x.png (tabbrowser/tab-background-middle-preWin10@2x.png)
+        skin/classic/browser/tabbrowser/tab-background-end-preWin10.png       (tabbrowser/tab-background-end-preWin10.png)
+        skin/classic/browser/tabbrowser/tab-background-end-preWin10@2x.png    (tabbrowser/tab-background-end-preWin10@2x.png)
         skin/classic/browser/tabbrowser/tab-overflow-indicator.png   (../shared/tabbrowser/tab-overflow-indicator.png)
         skin/classic/browser/tabbrowser/pendingpaint.png             (../shared/tabbrowser/pendingpaint.png)
 
 # NOTE: The following two files (tab-selected-end.svg, tab-selected-start.svg) get pre-processed in
 #       Makefile.in with a non-default marker of "%" and the result of that gets packaged.
         skin/classic/browser/tabbrowser/tab-selected-end.svg         (tab-selected-end.svg)
         skin/classic/browser/tabbrowser/tab-selected-start.svg       (tab-selected-start.svg)
 
@@ -699,8 +707,14 @@ browser.jar:
 % override chrome://browser/skin/preferences/checkbox.png             chrome://browser/skin/preferences/checkbox-aero.png               os=WINNT osversion=6.1
 % override chrome://browser/skin/preferences/checkbox.png             chrome://browser/skin/preferences/checkbox-xp.png                 os=WINNT osversion<6
 
 % override chrome://browser/skin/reload-stop-go.png                   chrome://browser/skin/reload-stop-go-preWin10.png                 os=WINNT osversion<=6.3
 % override chrome://browser/skin/reload-stop-go@2x.png                chrome://browser/skin/reload-stop-go-preWin10@2x.png              os=WINNT osversion<=6.3
 % override chrome://browser/skin/urlbar-history-dropmarker.png        chrome://browser/skin/urlbar-history-dropmarker-preWin10.png      os=WINNT osversion<=6.3
 % override chrome://browser/skin/urlbar-history-dropmarker@2x.png     chrome://browser/skin/urlbar-history-dropmarker-preWin10@2x.png   os=WINNT osversion<=6.3
 
+% override chrome://browser/skin/tabbrowser/tab-background-start.png     chrome://browser/skin/tabbrowser/tab-background-start-preWin10.png     os=WINNT osversion<=6.3
+% override chrome://browser/skin/tabbrowser/tab-background-start@2x.png  chrome://browser/skin/tabbrowser/tab-background-start-preWin10@2x.png  os=WINNT osversion<=6.3
+% override chrome://browser/skin/tabbrowser/tab-background-middle.png    chrome://browser/skin/tabbrowser/tab-background-middle-preWin10.png    os=WINNT osversion<=6.3
+% override chrome://browser/skin/tabbrowser/tab-background-middle@2x.png chrome://browser/skin/tabbrowser/tab-background-middle-preWin10@2x.png os=WINNT osversion<=6.3
+% override chrome://browser/skin/tabbrowser/tab-background-end.png       chrome://browser/skin/tabbrowser/tab-background-end-preWin10.png       os=WINNT osversion<=6.3
+% override chrome://browser/skin/tabbrowser/tab-background-end@2x.png    chrome://browser/skin/tabbrowser/tab-background-end-preWin10@2x.png    os=WINNT osversion<=6.3
copy from browser/themes/osx/tabbrowser/connecting@2x.png
copy to browser/themes/windows/tabbrowser/connecting@2x.png
index ba54836e9834f2c495bcfb05738b56481b2dbed0..b886c73eff9239196fbe3db01116c220f726469b
GIT binary patch
literal 12585
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
copy from browser/themes/osx/tabbrowser/loading@2x.png
copy to browser/themes/windows/tabbrowser/loading@2x.png
copy from browser/themes/windows/tabbrowser/tab-background-end.png
copy to browser/themes/windows/tabbrowser/tab-background-end-preWin10.png
copy from browser/themes/windows/tabbrowser/tab-background-end@2x.png
copy to browser/themes/windows/tabbrowser/tab-background-end-preWin10@2x.png
index fb353b17e70657d85e501a30ca6e2e6249e83a76..d68ea6da6a9834dfed10a88e73afa10ba1933099
GIT binary patch
literal 256
zc$@(M0ssDqP)<h;3K|Lk000e1NJLTq0015U0018d1ONa4T4lA20002TNkl<Zcmc)B
zB|?M&5I|wv9TEwTxLtsTL&OqJBjWC^SKwZ_jqIO?*~RkCZ|~(VA-wp8k7<YIaTwcQ
zbK}Hx`#)au6qFlrbiSc6NjKu?yaI)0tb9$ftXRisJJzAm1;%wrvSR(S&IRTP7Z@)j
z)r|ETH@M3M#yJ<5_U%}QLKoO+bb(>e1?EK;STAvbBONX<u1lgBD{pmyY4;(^iglir
zBr{e%A?a4E<9eh{5{)?aCJ#6bRC1SW!LhmDX>m$F^1lGYxa1-eg!DoH0000<MNUMn
GLSTZ+0&AlH
index eefb6ac47cd820af31556f1d8c6631ad36396baa..8ed84ab37969668dcee44c1f5915cde2a48e59a0
GIT binary patch
literal 400
zc$@){0dM|^P)<h;3K|Lk000e1NJLTq002Ay002G+1ONa4-951K00043Nkl<ZcmeIv
zwR#&t6vc5dHDM+<hR)2?nVH${+?X-UEN1P~<Y~e<_pa1VuD&hP>x9n#0e%CvzkmNn
z2In|RL4HX4v_`{b4V@_w{A=o|S}<$0x8$%DCusHCbpULoHuIMtu+UkaeHQ@0)~^??
zAz+D9g;oeGbdI|butj%4V4<^A1%ah7ox#9{+SPUlEOZM9rjYhQU}+cxb5wf~u=KqP
z1oK@CEPcBUf@vHC^Aiv(^f?sVje^%fFa<%dbQT2vr~|?IQ4p*)K`;eTu<_#*2+r4`
zZaf8o=?Da?ly)Lu>3bOnHhV!Z?So*>Xg>m$R0|5;0>Pa5ei8yp!zehYT^QKlZV*gC
z?Zd!^IS+y<M->oQ3VuHcfu-Bp4}qnd?*bUu;76Y?Ltvqks_%^p02fnFRFifB;9}-0
up5Su;*ovcSlSZkP&T5Z#JapLp(ftHT{;3T*{uNOG0000<MNUMnLSTY7xvBL4
copy from browser/themes/windows/tabbrowser/tab-background-middle.png
copy to browser/themes/windows/tabbrowser/tab-background-middle-preWin10.png
copy from browser/themes/windows/tabbrowser/tab-background-middle@2x.png
copy to browser/themes/windows/tabbrowser/tab-background-middle-preWin10@2x.png
index 51e066c2e9c6d71f5ce4846b1b030d2f111127ef..faaf7e38e5d9726af460735604497ee0e8eed164
GIT binary patch
literal 75
zc%17D@N?(olHy`uVBq!ia0vp^Y(Ol}!2%@nWJ)FgDIrf6$B+uf<O95RXI&hA6}nk6
X+&1Fr%E^cVDP{0<^>bP0l+XkK3Q7>X
index b26cb95de6ab3231f676a4c939a2bbf210557771..c9d245f4f4459fe4e1265354b87f47b1e3a3ce32
GIT binary patch
literal 86
zc%17D@N?(olHy`uVBq!ia0vp^JV0#6!2%?=o-;ZHq+~r^978G?lNYf6FcG$1(tBhD
d46>YLV0fy)D;sK|d=sdU!PC{xWt~$(699p}7_$HX
copy from browser/themes/windows/tabbrowser/tab-background-start.png
copy to browser/themes/windows/tabbrowser/tab-background-start-preWin10.png
copy from browser/themes/windows/tabbrowser/tab-background-start@2x.png
copy to browser/themes/windows/tabbrowser/tab-background-start-preWin10@2x.png
index cf0dc852ac52f8b6a1d9fdd0b6211494263aa60b..d1f0b5561034d68884c0c6e48f0697da4da86474
GIT binary patch
literal 257
zc$@(N0sj7pP)<h;3K|Lk000e1NJLTq0015U0018d1ONa4T4lA20002UNkl<ZcmdVR
zr9uE<5Jh3!9TEw5_a`9X5V3?uD(>zwufU_B|9^+gL)^N1H*rq>QvORHg?`A-VH2l%
zqpvZbt9GmY1ri<+S~eE-HDoccE7jeQ#KM-R`w$YlRS1c-1wvv=)omL8c0g#X%QUu3
z|43srKzKZw0P98qj0+mOQV5NMo&?y<B*1=~#;AbM*bURz(r`j!KLjDM<>HLQmZobE
z3R|`=LkMiCKVAL+5(~ejqax_mX&gmDA^t87DuaHVg2ew1(G=t{@qmLN00000NkvXX
Hu0mjfJUM9X
index bbfc77dd189d6885201c4d9e923836485cfd8ad1..e860275a6435b42e181b6de2e60a0be01b031e55
GIT binary patch
literal 417
zc$@*D0bc%zP)<h;3K|Lk000e1NJLTq002Ay002G+1ONa4-951K0004KNkl<ZcmeI#
zv!dNW6vg3VHL=+QU2JtRuC3}e*S4)#`)SV8#GJJc%1L?9|7S(Nd4O;BOtpP~VgDn9
zZK;#Ej~4XlG}iQqzJ+)S+Gy&esWbDC;^QIw{UDn4c@A;{r}vnS9N`FlpU`Ua2I3gb
z9LW8sLl~FzCB!+bH}oyUIjq-ntV0;b^$o;1jBVI}IESs@^gsx(xd<V^HimTyj0X?`
zY@_;w0{^%Up};y$fxjPwP+*jjV4bDFx&y+2Z?NE25}aD0z}N<%!0A&g_z?-#b`p#=
z3jAX;gaW5eli;5fBp8Df*apo@3Y<L*p}?tIBp7`Z*v9oW1vVEU9JrGL|JbarDKH*D
z2yillRSK*(AOzUPuttGXwGaY~qj*bze{9AY#5ru^7=<{8-zP8#aSngkt1}SCa57|W
z>kEie*apn`-0JNJ)*V`w+Xg*^rvo-%BiN2hcw{>CiDzIA{^$Ay{|l(iuOFwK00000
LNkvXXu0mjfTWh>f
--- a/mobile/android/chrome/content/aboutLogins.js
+++ b/mobile/android/chrome/content/aboutLogins.js
@@ -2,16 +2,17 @@
 * 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/. */
 
 let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Messaging.jsm");
 Cu.import("resource://gre/modules/Services.jsm")
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
 
 XPCOMUtils.defineLazyGetter(window, "gChromeWin", function()
   window.QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIWebNavigation)
     .QueryInterface(Ci.nsIDocShellTreeItem)
     .rootTreeItem
     .QueryInterface(Ci.nsIInterfaceRequestor)
     .getInterface(Ci.nsIDOMWindow)
@@ -48,17 +49,19 @@ let Logins = {
     let contentBody = document.getElementById("content-body");
     let emptyBody = document.getElementById("empty-body");
     let filterIcon = document.getElementById("filter-button");
 
     this._toggleListBody(true);
     emptyBody.classList.add("hidden");
 
     try {
+      TelemetryStopwatch.start("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS");
       logins = Services.logins.getAllLogins();
+      TelemetryStopwatch.finish("PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS");
     } catch(e) {
       // Master password was not entered
       debug("Master password permissions error: " + e);
       logins = [];
     }
     this._toggleListBody(false);
 
     if (!logins.length) {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4732,21 +4732,21 @@ pref("dom.inter-app-communication-api.en
 // Disable mapped array buffer by default.
 pref("dom.mapped_arraybuffer.enabled", false);
 
 // The tables used for Safebrowsing phishing and malware checks.
 pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,test-malware-simple,test-unwanted-simple");
 pref("urlclassifier.phishTable", "goog-phish-shavar,test-phish-simple");
 pref("urlclassifier.downloadBlockTable", "");
 pref("urlclassifier.downloadAllowTable", "");
-pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simple,test-unwanted-simple,goog-downloadwhite-digest256,mozpub-track-digest256");
+pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple,goog-downloadwhite-digest256,mozpub-track-digest256");
 
 // The table and update/gethash URLs for Safebrowsing phishing and malware
 // checks.
-pref("urlclassifier.trackingTable", "mozpub-track-digest256");
+pref("urlclassifier.trackingTable", "test-track-simple,mozpub-track-digest256");
 pref("browser.trackingprotection.updateURL", "https://tracking.services.mozilla.com/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
 pref("browser.trackingprotection.gethashURL", "https://tracking.services.mozilla.com/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
 
 // Turn off Spatial navigation by default.
 pref("snav.enabled", false);
 
 // Original caret implementation on collapsed selection.
 pref("touchcaret.enabled", false);
--- a/testing/taskcluster/tasks/branches/base_jobs.yml
+++ b/testing/taskcluster/tasks/branches/base_jobs.yml
@@ -105,16 +105,18 @@ tests:
   gaia-build-unit:
     allowed_build_tasks:
       tasks/builds/b2g_desktop_opt.yml:
         task: tasks/tests/b2g_build_unit.yml
   gaia-js-integration:
     allowed_build_tasks:
       tasks/builds/b2g_desktop_opt.yml:
         task: tasks/tests/b2g_gaia_js_integration_tests.yml
+      tasks/builds/mulet_linux.yml:
+        task: tasks/tests/mulet_gaia_js_integration_tests.yml
   gaia-linter:
     allowed_build_tasks:
       tasks/builds/b2g_desktop_opt.yml:
         task: tasks/tests/b2g_linter.yml
   gaia-ui-test-accessibility:
     allowed_build_tasks:
       tasks/builds/b2g_desktop_opt.yml:
         task: tasks/tests/b2g_gaia_ui_test_accessibility.yml
--- a/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml
+++ b/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml
@@ -1,42 +1,44 @@
 ---
 $inherits:
   from: 'tasks/test.yml'
-reruns: 2
 task:
   metadata:
     name: '[TC] Mulet Gaia JS Integration Test'
     description: Mulet Gaia JS Integration Test run {{chunk}}
 
   payload:
     command:
       - entrypoint # entrypoint ensures we are running in xvfb
       - ./bin/pull_gaia.sh &&
       - >
         python ./mozharness/scripts/gaia_integration.py
         --application firefox
         --no-read-buildbot-config
         --config-file b2g/gaia_integration_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
         --config-file ./mozharness_configs/remove_executables.py
+        --no-pull
         --installer-url {{build_url}}
-        --no-pull
-        --test-url {{tests_url}}
+        --test-packages-url {{test_packages_url}}
         --download-symbols ondemand
         --total-chunk {{total_chunks}}
         --this-chunk {{chunk}}
         --gaia-repo {{gaia_head_repository}}
         --gaia-dir /home/worker
     artifacts:
       'public/build':
         type: directory
         path: '/home/worker/artifacts/'
         expires: '{{#from_now}}1 year{{/from_now}}'
 
   extra:
     chunks:
       total: 20
+    treeherderEnv:
+      - production
+      - staging
     treeherder:
       groupName: Gaia JS Integration Test
-      groupSymbol: tc-Gij
+      groupSymbol: Gij
       symbol: '{{chunk}}'
       productName: b2g
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -1339,17 +1339,19 @@ nsAutoCompleteController::EnterMatch(boo
         // If completeselectedindex is true, and EnterMatch was not invoked by
         // mouse-clicking a match (for example the user pressed Enter),
         // don't fill in the value as it will have already been filled in as
         // needed, unless the selected match has a final complete value that
         // differs from the user-facing value.
         GetResultValueAt(mCompletedSelectionIndex, true, finalValue);
         nsAutoString inputValue;
         input->GetTextValue(inputValue);
-        if (!finalValue.Equals(inputValue)) {
+        nsAutoString completedValue;
+        GetResultValueAt(mCompletedSelectionIndex, false, completedValue);
+        if (completedValue.Equals(inputValue) && !finalValue.Equals(inputValue)) {
           value = finalValue;
         }
         // Note that if the user opens the popup, mouses over entries without
         // ever selecting one with the keyboard, and then hits enter, none of
         // the above cases will be hitt, since mouseover doesn't activate
         // completeselectedindex and thus mCompletedSelectionIndex would be
         // -1.
       }
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/OSCrypto.jsm
@@ -0,0 +1,22 @@
+/* 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/. */
+
+/**
+ * Common front for various implementations of OSCrypto
+ */
+
+"use strict";
+
+Components.utils.import("resource://gre/modules/AppConstants.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+this.EXPORTED_SYMBOLS = ["OSCrypto"];
+
+let OSCrypto = {};
+
+if (AppConstants.platform == "win") {
+  Services.scriptloader.loadSubScript("resource://gre/modules/OSCrypto_win.js", this);
+} else {
+  throw new Error("OSCrypto.jsm isn't supported on this platform");
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/passwordmgr/OSCrypto_win.js
@@ -0,0 +1,143 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "ctypes", "resource://gre/modules/ctypes.jsm");
+
+function OSCrypto() {
+  this._structs = {};
+  this._functions = new Map();
+  this._libs = new Map();
+  this._structs.DATA_BLOB = new ctypes.StructType("DATA_BLOB",
+                                                  [
+                                                    {cbData: ctypes.uint32_t},
+                                                    {pbData: ctypes.uint8_t.ptr}
+                                                  ]);
+
+  try {
+
+    this._libs.set("crypt32", ctypes.open("Crypt32"));
+    this._libs.set("kernel32", ctypes.open("Kernel32"));
+
+    this._functions.set("CryptProtectData",
+                        this._libs.get("crypt32").declare("CryptProtectData",
+                                                          ctypes.winapi_abi,
+                                                          ctypes.uint32_t,
+                                                          this._structs.DATA_BLOB.ptr,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.uint32_t,
+                                                          this._structs.DATA_BLOB.ptr));
+
+    this._functions.set("CryptUnprotectData",
+                        this._libs.get("crypt32").declare("CryptUnprotectData",
+                                                          ctypes.winapi_abi,
+                                                          ctypes.uint32_t,
+                                                          this._structs.DATA_BLOB.ptr,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.voidptr_t,
+                                                          ctypes.uint32_t,
+                                                          this._structs.DATA_BLOB.ptr));
+    this._functions.set("LocalFree",
+                        this._libs.get("kernel32").declare("LocalFree",
+                                                           ctypes.winapi_abi,
+                                                           ctypes.uint32_t,
+                                                           ctypes.voidptr_t));
+  } catch (ex) {
+    Cu.reportError(ex);
+    this.finalize();
+    throw ex;
+  }
+}
+OSCrypto.prototype = {
+  /**
+   * Decrypt an array of numbers using the windows CryptUnprotectData API.
+   * @param {number[]} array - the encrypted array that needs to be decrypted.
+   * @returns {string} the decryption of the array.
+   */
+  decryptData(array) {
+    let decryptedData = "";
+    let encryptedData = ctypes.uint8_t.array(array.length)(array);
+    let inData = new this._structs.DATA_BLOB(encryptedData.length, encryptedData);
+    let outData = new this._structs.DATA_BLOB();
+    let status = this._functions.get("CryptUnprotectData")(inData.address(), null,
+                                                           null, null, null, 0,
+                                                           outData.address());
+    if (status === 0) {
+      throw new Error("decryptData failed: " + status);
+    }
+
+    // convert byte array to JS string.
+    let len = outData.cbData;
+    let decrypted = ctypes.cast(outData.pbData,
+                                ctypes.uint8_t.array(len).ptr).contents;
+    for (let i = 0; i < decrypted.length; i++) {
+      decryptedData += String.fromCharCode(decrypted[i]);
+    }
+
+    this._functions.get("LocalFree")(outData.pbData);
+    return decryptedData;
+  },
+
+  /**
+   * Encrypt a string using the windows CryptProtectData API.
+   * @param {string} string - the string that is going to be encrypted.
+   * @returns {number[]} the encrypted string encoded as an array of numbers.
+   */
+  encryptData(string) {
+    let encryptedData = [];
+    let decryptedData = ctypes.uint8_t.array(string.length)();
+
+    for (let i = 0; i < string.length; i++) {
+      decryptedData[i] = string.charCodeAt(i);
+    }
+
+    let inData = new this._structs.DATA_BLOB(string.length, decryptedData);
+    let outData = new this._structs.DATA_BLOB();
+    let status = this._functions.get("CryptProtectData")(inData.address(), null,
+                                                         null, null, null, 0,
+                                                         outData.address());
+    if (status === 0) {
+      throw new Error("encryptData failed: " + status);
+    }
+
+    // convert byte array to JS string.
+    let len = outData.cbData;
+    let encrypted = ctypes.cast(outData.pbData,
+                                ctypes.uint8_t.array(len).ptr).contents;
+
+    for (let i = 0; i < len; i++) {
+      encryptedData.push(encrypted[i]);
+    }
+
+    this._functions.get("LocalFree")(outData.pbData);
+    return encryptedData;
+  },
+
+  /**
+   * Must be invoked once after last use of any of the provided helpers.
+   */
+  finalize() {
+    this._structs = {};
+    this._functions.clear();
+    for (let lib of this._libs.values()) {
+      try {
+        lib.close();
+      } catch (ex) {
+        Cu.reportError(ex);
+      }
+    }
+    this._libs.clear();
+  },
+};
--- a/toolkit/components/passwordmgr/moz.build
+++ b/toolkit/components/passwordmgr/moz.build
@@ -36,31 +36,37 @@ EXTRA_PP_COMPONENTS += [
 ]
 
 EXTRA_PP_JS_MODULES += [
     'LoginManagerParent.jsm',
 ]
 
 EXTRA_JS_MODULES += [
     'InsecurePasswordUtils.jsm',
-    'LoginDoorhangers.jsm',
     'LoginHelper.jsm',
     'LoginManagerContent.jsm',
     'LoginRecipes.jsm',
+    'OSCrypto.jsm',
 ]
 
 if CONFIG['OS_TARGET'] == 'Android':
     EXTRA_COMPONENTS += [
         'storage-mozStorage.js',
     ]
 else:
     EXTRA_COMPONENTS += [
         'storage-json.js',
     ]
     EXTRA_JS_MODULES += [
+        'LoginDoorhangers.jsm',
         'LoginImport.jsm',
         'LoginStore.jsm',
     ]
 
+if CONFIG['OS_TARGET'] == 'WINNT':
+    EXTRA_JS_MODULES += [
+        'OSCrypto_win.js',
+    ]
+
 JAR_MANIFESTS += ['jar.mn']
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Password Manager')
--- a/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm
+++ b/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm
@@ -128,17 +128,19 @@ const SearchAutocompleteProviderInternal
 
 function SearchSuggestionControllerWrapper(engine, searchToken,
                                            inPrivateContext, maxResults) {
   this._controller = new SearchSuggestionController();
   this._controller.maxLocalResults = 0;
   this._controller.maxRemoteResults = maxResults;
   let promise = this._controller.fetch(searchToken, inPrivateContext, engine);
   this._suggestions = [];
+  this._success = false;
   this._promise = promise.then(results => {
+    this._success = true;
     this._suggestions = (results ? results.remote : null) || [];
   }).catch(err => {
     // fetch() rejects its promise if there's a pending request.
   });
 }
 
 SearchSuggestionControllerWrapper.prototype = {
 
@@ -160,16 +162,24 @@ SearchSuggestionControllerWrapper.protot
    */
   consume() {
     return !this._suggestions.length ? [null, null] :
            [SearchAutocompleteProviderInternal.defaultMatch,
             this._suggestions.shift()];
   },
 
   /**
+   * Returns the number of fetched suggestions, or -1 if the fetching was
+   * incomplete or failed.
+   */
+  get resultsCount() {
+    return this._success ? this._suggestions.length : -1;
+  },
+
+  /**
    * Stops the fetch.
    */
   stop() {
     this._controller.stop();
   },
 };
 
 let gInitializationPromise = null;
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -33,16 +33,18 @@ const PREF_MATCH_TITLE =            [ "m
 const PREF_MATCH_URL =              [ "match.url",              "@" ];
 
 const PREF_SUGGEST_HISTORY =        [ "suggest.history",        true ];
 const PREF_SUGGEST_BOOKMARK =       [ "suggest.bookmark",       true ];
 const PREF_SUGGEST_OPENPAGE =       [ "suggest.openpage",       true ];
 const PREF_SUGGEST_HISTORY_ONLYTYPED = [ "suggest.history.onlyTyped", false ];
 const PREF_SUGGEST_SEARCHES =       [ "suggest.searches",       true ];
 
+const PREF_MAX_CHARS_FOR_SUGGEST =  [ "maxCharsForSearchSuggestions", 20];
+
 // Match type constants.
 // These indicate what type of search function we should be using.
 const MATCH_ANYWHERE = Ci.mozIPlacesAutoComplete.MATCH_ANYWHERE;
 const MATCH_BOUNDARY_ANYWHERE = Ci.mozIPlacesAutoComplete.MATCH_BOUNDARY_ANYWHERE;
 const MATCH_BOUNDARY = Ci.mozIPlacesAutoComplete.MATCH_BOUNDARY;
 const MATCH_BEGINNING = Ci.mozIPlacesAutoComplete.MATCH_BEGINNING;
 const MATCH_BEGINNING_CASE_SENSITIVE = Ci.mozIPlacesAutoComplete.MATCH_BEGINNING_CASE_SENSITIVE;
 
@@ -411,16 +413,17 @@ XPCOMUtils.defineLazyGetter(this, "Prefs
     store.restrictSearchesToken = prefs.get(...PREF_RESTRICT_SEARCHES);
     store.matchTitleToken = prefs.get(...PREF_MATCH_TITLE);
     store.matchURLToken = prefs.get(...PREF_MATCH_URL);
     store.suggestHistory = prefs.get(...PREF_SUGGEST_HISTORY);
     store.suggestBookmark = prefs.get(...PREF_SUGGEST_BOOKMARK);
     store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE);
     store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED);
     store.suggestSearches = prefs.get(...PREF_SUGGEST_SEARCHES);
+    store.maxCharsForSearchSuggestions = prefs.get(...PREF_MAX_CHARS_FOR_SUGGEST);
 
     // If history is not set, onlyTyped value should be ignored.
     if (!store.suggestHistory) {
       store.suggestTyped = false;
     }
     store.defaultBehavior = types.concat("Typed").reduce((memo, type) => {
       let prefValue = store["suggest" + type];
       return memo | (prefValue &&
@@ -593,19 +596,21 @@ function makeActionURL(action, params) {
  *          possibly in permanent private-browsing mode.  The search
  *          should exclude privacy-sensitive results as appropriate.
  * @param autocompleteListener
  *        An nsIAutoCompleteObserver.
  * @param resultListener
  *        An nsIAutoCompleteSimpleResultListener.
  * @param autocompleteSearch
  *        An nsIAutoCompleteSearch.
+ * @param prohibitSearchSuggestions
+ *        Whether search suggestions are allowed for this search.
  */
 function Search(searchString, searchParam, autocompleteListener,
-                resultListener, autocompleteSearch) {
+                resultListener, autocompleteSearch, prohibitSearchSuggestions) {
   // We want to store the original string for case sensitive searches.
   this._originalSearchString = searchString;
   this._trimmedOriginalSearchString = searchString.trim();
   this._searchString = fixupSearchText(this._trimmedOriginalSearchString.toLowerCase());
 
   this._matchBehavior = Prefs.matchBehavior;
   // Set the default behavior for this search.
   this._behavior = this._searchString ? Prefs.defaultBehavior
@@ -627,16 +632,18 @@ function Search(searchString, searchPara
   // host, but the path must be matched in a case sensitive way.
   let pathIndex =
     this._trimmedOriginalSearchString.indexOf("/", this._strippedPrefix.length);
   this._autofillUrlSearchString = fixupSearchText(
     this._trimmedOriginalSearchString.slice(0, pathIndex).toLowerCase() +
     this._trimmedOriginalSearchString.slice(pathIndex)
   );
 
+  this._prohibitSearchSuggestions = prohibitSearchSuggestions;
+
   this._listener = autocompleteListener;
   this._autocompleteSearch = autocompleteSearch;
 
   // Create a new result to add eventual matches.  Note we need a result
   // regardless having matches.
   let result = Cc["@mozilla.org/autocomplete/simple-result;1"]
                  .createInstance(Ci.nsIAutoCompleteSimpleResult);
   result.setSearchString(searchString);
@@ -904,28 +911,39 @@ Search.prototype = {
       }
     }
 
     // Ensure to fill any remaining space.
     yield Promise.all(this._remoteMatchesPromises);
   }),
 
   *_matchSearchSuggestions() {
-    if (!this.hasBehavior("searches") || this._inPrivateWindow) {
+    // Limit the string sent for search suggestions to a maximum length.
+    let searchString = this._searchTokens.join(" ")
+                           .substr(0, Prefs.maxCharsForSearchSuggestions);
+    // Avoid fetching suggestions if they are not required, private browsing
+    // mode is enabled, or the search string may expose sensitive information.
+    if (!this.hasBehavior("searches") || this._inPrivateWindow ||
+        this._prohibitSearchSuggestionsFor(searchString)) {
       return;
     }
 
     this._searchSuggestionController =
       PlacesSearchAutocompleteProvider.getSuggestionController(
-        this._searchTokens.join(" "),
+        searchString,
         this._inPrivateWindow,
         Prefs.maxRichResults
       );
     let promise = this._searchSuggestionController.fetchCompletePromise
       .then(() => {
+        if (this._searchSuggestionController.resultsCount >= 0 &&
+            this._searchSuggestionController.resultsCount < 2) {
+          // The original string is used to properly compare with the next search.
+          this._lastLowResultsSearchSuggestion = this._originalSearchString;
+        }
         while (this.pending && this._remoteMatchesCount < Prefs.maxRichResults) {
           let [match, suggestion] = this._searchSuggestionController.consume();
           if (!suggestion)
             break;
           // Don't include the restrict token, if present.
           let searchString = this._searchTokens.join(" ");
           this._addSearchEngineMatch(match, searchString, suggestion);
         }
@@ -935,16 +953,38 @@ Search.prototype = {
       // We're done if we're restricting to search suggestions.
       yield promise;
       this.stop();
     } else {
       this._remoteMatchesPromises.push(promise);
     }
   },
 
+  _prohibitSearchSuggestionsFor(searchString) {
+    if (this._prohibitSearchSuggestions)
+      return true;
+
+    // Suggestions for a single letter are unlikely to be useful.
+    if (searchString.length < 2)
+      return true;
+
+    let tokens = searchString.split(/\s/);
+
+    // The first token may be a whitelisted host.
+    if (REGEXP_SINGLEWORD_HOST.test(tokens[0]) &&
+        Services.uriFixup.isDomainWhitelisted(tokens[0], -1))
+      return true;
+
+    // Disallow fetching search suggestions for strings looking like URLs, to
+    // avoid disclosing information about networks or passwords.
+    return tokens.some(token => {
+      return ["/", "@", ":", "."].some(c => token.includes(c));
+    });
+  },
+
   _matchKnownUrl: function* (conn) {
     // Hosts have no "/" in them.
     let lastSlashIndex = this._searchString.lastIndexOf("/");
     // Search only URLs if there's a slash in the search string...
     if (lastSlashIndex != -1) {
       // ...but not if it's exactly at the end of the search string.
       if (lastSlashIndex < this._searchString.length - 1) {
         // We don't want to execute this query right away because it needs to
@@ -1696,18 +1736,25 @@ UnifiedComplete.prototype = {
     // Stop the search in case the controller has not taken care of it.
     if (this._currentSearch) {
       this.stopSearch();
     }
 
     // Note: We don't use previousResult to make sure ordering of results are
     //       consistent.  See bug 412730 for more details.
 
+    // If the previous search didn't fetch enough search suggestions, it's
+    // unlikely a longer text would do.
+    let prohibitSearchSuggestions =
+      this._lastLowResultsSearchSuggestion &&
+      searchString.length > this._lastLowResultsSearchSuggestion.length &&
+      searchString.startsWith(this._lastLowResultsSearchSuggestion);
+
     this._currentSearch = new Search(searchString, searchParam, listener,
-                                     this, this);
+                                     this, this, prohibitSearchSuggestions);
 
     // If we are not enabled, we need to return now.  Notice we need an empty
     // result regardless, so we still create the Search object.
     if (!Prefs.enabled) {
       this.finishSearch(true);
       return;
     }
 
@@ -1740,16 +1787,17 @@ UnifiedComplete.prototype = {
    *        Indicates if we should notify the AutoComplete listener about our
    *        results or not.
    */
   finishSearch: function (notify=false) {
     TelemetryStopwatch.cancel(TELEMETRY_1ST_RESULT, this);
     TelemetryStopwatch.cancel(TELEMETRY_6_FIRST_RESULTS, this);
     // Clear state now to avoid race conditions, see below.
     let search = this._currentSearch;
+    this._lastLowResultsSearchSuggestion = search._lastLowResultsSearchSuggestion;
     delete this._currentSearch;
 
     if (!notify)
       return;
 
     // There is a possible race condition here.
     // When a search completes it calls finishSearch that notifies results
     // here.  When the controller gets the last result it fires
--- a/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js
@@ -409,8 +409,76 @@ add_task(function* mixup_frecency() {
         title: "low frecency 1" },
       { uri: NetUtil.newURI("http://example.com/lo0"),
         title: "low frecency 0" },
     ],
   });
 
   yield cleanUpSuggestions();
 });
+
+add_task(function* prohibit_suggestions() {
+  Services.prefs.setBoolPref(SUGGEST_PREF, true);
+
+  yield check_autocomplete({
+    search: "localhost",
+    matches: [
+      {
+        uri: makeActionURI(("searchengine"), {
+          engineName: ENGINE_NAME,
+          input: "localhost foo",
+          searchQuery: "localhost",
+          searchSuggestion: "localhost foo",
+        }),
+        title: ENGINE_NAME,
+        style: ["action", "searchengine"],
+        icon: "",
+      },
+      {
+        uri: makeActionURI(("searchengine"), {
+          engineName: ENGINE_NAME,
+          input: "localhost bar",
+          searchQuery: "localhost",
+          searchSuggestion: "localhost bar",
+        }),
+        title: ENGINE_NAME,
+        style: ["action", "searchengine"],
+        icon: "",
+      },
+    ],
+  });
+  Services.prefs.setBoolPref("browser.fixup.domainwhitelist.localhost", true);
+  do_register_cleanup(() => {
+    Services.prefs.clearUserPref("browser.fixup.domainwhitelist.localhost");
+  });
+  yield check_autocomplete({
+    search: "localhost",
+    matches: [],
+  });
+
+  yield check_autocomplete({
+    search: "1.2.3.4",
+    matches: [],
+  });
+  yield check_autocomplete({
+    search: "[2001::1]:30",
+    matches: [],
+  });
+  yield check_autocomplete({
+    search: "user:pass@test",
+    matches: [],
+  });
+  yield check_autocomplete({
+    search: "test/test",
+    matches: [],
+  });
+  yield check_autocomplete({
+    search: "data:text/plain,Content",
+    matches: [],
+  });
+
+  yield check_autocomplete({
+    search: "a",
+    matches: [],
+  });
+
+  yield cleanUpSuggestions();
+});
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8218,16 +8218,24 @@
     "alert_emails": ["firefox-dev@mozilla.org", "gavin@mozilla.com"],
     "expires_in_version": "50",
     "kind": "exponential",
     "high": "30000",
     "n_buckets": 20,
     "extended_statistics_ok": true,
     "description": "Sanitize: Time it takes to sanitize the open windows list (ms)"
   },
+  "PWMGR_ABOUT_LOGINS_GET_ALL_LOGINS_MS": {
+    "expires_in_version": "55",
+    "kind": "exponential",
+    "high": 60000,
+    "n_buckets": 30,
+    "extended_statistics_ok": true,
+    "description": "How long getAllLogins() on about:logins takes for mobile users"
+  },
   "PWMGR_BLOCKLIST_NUM_SITES": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 100,
     "n_buckets" : 10,
     "extended_statistics_ok": true,
     "description": "The number of sites for which the user has explicitly rejected saving logins"
   },
--- a/toolkit/components/url-classifier/SafeBrowsing.jsm
+++ b/toolkit/components/url-classifier/SafeBrowsing.jsm
@@ -209,41 +209,51 @@ this.SafeBrowsing = {
 
 
   addMozEntries: function() {
     // Add test entries to the DB.
     // XXX bug 779008 - this could be done by DB itself?
     const phishURL    = "itisatrap.org/firefox/its-a-trap.html";
     const malwareURL  = "itisatrap.org/firefox/its-an-attack.html";
     const unwantedURL = "itisatrap.org/firefox/unwanted.html";
+    const trackerURLs  = [
+      "trackertest.org/",
+      "itisatracker.org/",
+    ];
 
     let update = "n:1000\ni:test-malware-simple\nad:1\n" +
                  "a:1:32:" + malwareURL.length + "\n" +
                  malwareURL;
     update += "n:1000\ni:test-phish-simple\nad:1\n" +
               "a:1:32:" + phishURL.length + "\n" +
               phishURL;
     update += "n:1000\ni:test-unwanted-simple\nad:1\n" +
               "a:1:32:" + unwantedURL.length + "\n" +
               unwantedURL;
+    trackerURLs.forEach((trackerURL, i) => {
+      update += "n:1000\ni:test-track-simple\nad:1\n" +
+                "a:" + (i + 1) + ":32:" + trackerURL.length + "\n" +
+                trackerURL;
+    });
     log("addMozEntries:", update);
 
     let db = Cc["@mozilla.org/url-classifier/dbservice;1"].
              getService(Ci.nsIUrlClassifierDBService);
 
     // nsIUrlClassifierUpdateObserver
     let dummyListener = {
       updateUrlRequested: function() { },
       streamFinished:     function() { },
       updateError:        function() { },
       updateSuccess:      function() { }
     };
 
     try {
-      db.beginUpdate(dummyListener, "test-malware-simple,test-phish-simple,test-unwanted-simple", "");
+      let tables = "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple";
+      db.beginUpdate(dummyListener, tables, "");
       db.beginStream("", "");
       db.updateStream(update);
       db.finishStream();
       db.finishUpdate();
     } catch(ex) {
       // beginUpdate will throw harmlessly if there's an existing update in progress, ignore failures.
       log("addMozEntries failed!", ex);
     }
--- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
+++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
@@ -49,27 +49,30 @@ function delFile(name) {
 }
 
 function cleanUp() {
   delFile("urlclassifier3.sqlite");
   delFile("safebrowsing/classifier.hashkey");
   delFile("safebrowsing/test-phish-simple.sbstore");
   delFile("safebrowsing/test-malware-simple.sbstore");
   delFile("safebrowsing/test-unwanted-simple.sbstore");
+  delFile("safebrowsing/test-track-simple.sbstore");
   delFile("safebrowsing/test-phish-simple.cache");
   delFile("safebrowsing/test-malware-simple.cache");
   delFile("safebrowsing/test-unwanted-simple.cache");
+  delFile("safebrowsing/test-track-simple.cache");
   delFile("safebrowsing/test-phish-simple.pset");
   delFile("safebrowsing/test-malware-simple.pset");
   delFile("safebrowsing/test-unwanted-simple.pset");
+  delFile("safebrowsing/test-track-simple.pset");
   delFile("testLarge.pset");
   delFile("testNoDelta.pset");
 }
 
-var allTables = "test-phish-simple,test-malware-simple,test-unwanted-simple";
+var allTables = "test-phish-simple,test-malware-simple,test-unwanted-simple,test-track-simple";
 
 var dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIUrlClassifierDBService);
 var streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"]
                     .getService(Ci.nsIUrlClassifierStreamUpdater);
 
 
 /*
  * Builds an update from an object that looks like:
@@ -139,18 +142,17 @@ function doSimpleUpdate(updateText, succ
     },
 
     updateUrlRequested: function(url) { },
     streamFinished: function(status) { },
     updateError: function(errorCode) { failure(errorCode); },
     updateSuccess: function(requestedTimeout) { success(requestedTimeout); }
   };
 
-  dbservice.beginUpdate(listener,
-                        "test-phish-simple,test-malware-simple,test-unwanted-simple");
+  dbservice.beginUpdate(listener, allTables);
   dbservice.beginStream("", "");
   dbservice.updateStream(updateText);
   dbservice.finishStream();
   dbservice.finishUpdate();
 }
 
 /**
  * Simulates a failed database update.
@@ -182,17 +184,17 @@ function doErrorUpdate(tables, success, 
  */
 function doStreamUpdate(updateText, success, failure, downloadFailure) {
   var dataUpdate = "data:," + encodeURIComponent(updateText);
 
   if (!downloadFailure) {
     downloadFailure = failure;
   }
 
-  streamUpdater.downloadUpdates("test-phish-simple,test-malware-simple,test-unwanted-simple", "",
+  streamUpdater.downloadUpdates(allTables, "",
                                 dataUpdate, success, failure, downloadFailure);
 }
 
 var gAssertions = {
 
 tableData : function(expectedTables, cb)
 {
   dbservice.getTables(function(tables) {
--- a/tools/mercurial/hgsetup/wizard.py
+++ b/tools/mercurial/hgsetup/wizard.py
@@ -208,16 +208,30 @@ Would you like to activate bundleclone
 
 FILE_PERMISSIONS_WARNING = '''
 Your hgrc file is currently readable by others.
 
 Sensitive information such as your Bugzilla credentials could be
 stolen if others have access to this file/machine.
 '''.strip()
 
+MULTIPLE_VCT = '''
+*** WARNING ***
+
+Multiple version-control-tools repositories are referenced in your
+Mercurial config. Extensions and other code within the
+version-control-tools repository could run with inconsistent results.
+
+Please manually edit the following file to reference a single
+version-control-tools repository:
+
+    %s
+'''.lstrip()
+
+
 class MercurialSetupWizard(object):
     """Command-line wizard to help users configure Mercurial."""
 
     def __init__(self, state_dir):
         # We use normpath since Mercurial expects the hgrc to use native path
         # separators, but state_dir uses unix style paths even on Windows.
         self.state_dir = os.path.normpath(state_dir)
         self.ext_dir = os.path.join(self.state_dir, 'mercurial', 'extensions')
@@ -399,16 +413,33 @@ class MercurialSetupWizard(object):
             if os.path.exists(path):
                 if self._prompt_yn('Would you like to remove the old and no '
                     'longer referenced repository at %s' % path):
                     print('Cleaning up old repository: %s' % path)
                     shutil.rmtree(path)
 
         c.add_mozilla_host_fingerprints()
 
+        # References to multiple version-control-tools checkouts can confuse
+        # version-control-tools, since various Mercurial extensions resolve
+        # dependencies via __file__ and repos could reference another copy.
+        seen_vct = set()
+        for k, v in c.config.get('extensions', {}).items():
+            if 'version-control-tools' not in v:
+                continue
+
+            i = v.index('version-control-tools')
+            vct = v[0:i + len('version-control-tools')]
+            seen_vct.add(os.path.realpath(os.path.expanduser(vct)))
+
+        if len(seen_vct) > 1:
+            print(MULTIPLE_VCT % c.config_path)
+
+        # At this point the config should be finalized.
+
         b = StringIO()
         c.write(b)
         new_lines = [line.rstrip() for line in b.getvalue().splitlines()]
         old_lines = []
 
         config_path = c.config_path
         if os.path.exists(config_path):
             with open(config_path, 'rt') as fh: