diff --git a/b2g/config/aries/sources.xml b/b2g/config/aries/sources.xml
index c7160b0ccb23a58c5761b9723401f3b0037fc1f5..44f6ffead4c9d8dbf8959ef5629306d7b8514b2e 100644
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -15,7 +15,7 @@
   <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"/>
diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml
index 8060d4d57d5e1d92627b34436e5e7e457fe70eb6..881d3c78a816157ebdc68aa58a05ccd55a33c02c 100644
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -15,7 +15,7 @@
   <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"/>
diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index 886d615791315c9ef63e4af4c3183e5eab29a8c4..98ff122f0466bd1da576e89abddc5f4c30ae9ab7 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -19,7 +19,7 @@
     <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"/>
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index 84e6706c9c896dc9b108925beaede65f47309a6e..d63adc161f5ee866f4b3a9124b835d577923dca1 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -17,7 +17,7 @@
   </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"/>
diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml
index 0a33e6d3f4767f831786438278c8b83cb49e0207..15ec68bdad744cf19803deb7a9b3546f7482f75c 100644
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -15,7 +15,7 @@
   <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"/>
diff --git a/b2g/config/emulator-l/sources.xml b/b2g/config/emulator-l/sources.xml
index 0b74b64cb2ae9201ab9b9cfd2cdcfd9023c16e59..e0ec32a104c14b881176955670ca391615d06139 100644
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -15,7 +15,7 @@
   <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"/>
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index 886d615791315c9ef63e4af4c3183e5eab29a8c4..98ff122f0466bd1da576e89abddc5f4c30ae9ab7 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -19,7 +19,7 @@
     <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"/>
diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml
index d5a1768478fc78ce9f814e73536456efdb57a075..8f678a4a410840daa026862b544ad4780b8cabb7 100644
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -15,7 +15,7 @@
   <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"/>
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index b8fe2d31cb3e62cd2a60fb273817e17bff2caada..520838b84ec0500035010580e4b8a254f7db9047 100644
--- 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"
 }
diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml
index 9e868f943d372d6b4275bfb6606db592219f56f4..2b96ad640746ae8c1587619f37725725ac1ce5fb 100644
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -17,7 +17,7 @@
   </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"/>
diff --git a/b2g/config/nexus-5-l/sources.xml b/b2g/config/nexus-5-l/sources.xml
index 16cb7adb690671182cb35d3b6cc0e1d249898af8..b0f6a16d6f496bafdd6347baf3794e73d3bf5377 100644
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -15,7 +15,7 @@
   <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"/>
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 5d3abf9a5f6aba08711e41c362af0c81e8b097cb..ebd6eae7430d6fec6b1134c0f5fc735c9f48f3e3 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -354,6 +354,10 @@ pref("browser.urlbar.suggest.searches",             true);
 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);
diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini
index 33805be1aedd4c519f762153f329bd51da3dc58e..3f185c5f31ea4a4e175eda29be0fb4d1ac90f4f4 100644
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -134,6 +134,7 @@ skip-if = e10s # Bug 1093153 - no about:home support yet
 [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]
diff --git a/browser/base/content/test/general/browser_autocomplete_edit_completed.js b/browser/base/content/test/general/browser_autocomplete_edit_completed.js
new file mode 100644
index 0000000000000000000000000000000000000000..40179d3284f9285f03764abf6f6db91f4fe1fd0c
--- /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);
+}
diff --git a/browser/components/migration/ChromeProfileMigrator.js b/browser/components/migration/ChromeProfileMigrator.js
index 767489cf41ca8b716b93d74f0edf3260f4920e77..ccb69bfad00ced108ccbf66eee1cb48c03dcc99e 100644
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -6,16 +6,19 @@
 
 "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");
@@ -25,6 +28,8 @@ 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
@@ -93,7 +98,11 @@ ChromeProfileMigrator.prototype.getResources =
       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)];
       }
     }
@@ -353,6 +362,101 @@ function GetCookiesResource(aProfileFolder) {
   }
 }
 
+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}");
diff --git a/browser/components/migration/tests/unit/AppData/Local/Google/Chrome/User Data/Default/Login Data b/browser/components/migration/tests/unit/AppData/Local/Google/Chrome/User Data/Default/Login Data
new file mode 100644
index 0000000000000000000000000000000000000000..914149c710a8c748b97543c541aaedfee74aa1f6
Binary files /dev/null and b/browser/components/migration/tests/unit/AppData/Local/Google/Chrome/User Data/Default/Login Data differ
diff --git a/browser/components/migration/tests/unit/test_Chrome_passwords.js b/browser/components/migration/tests/unit/test_Chrome_passwords.js
new file mode 100644
index 0000000000000000000000000000000000000000..dcd9a3aefd1f5e362bce38006ff53a1a44be21ea
--- /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);
+  }
+});
diff --git a/browser/components/migration/tests/unit/xpcshell.ini b/browser/components/migration/tests/unit/xpcshell.ini
index eaef34cbad2a0473b481c6e3d5aff65d6185393a..036562b5e590948644ae9348be8ce8b2feed47a5 100644
--- a/browser/components/migration/tests/unit/xpcshell.ini
+++ b/browser/components/migration/tests/unit/xpcshell.ini
@@ -5,9 +5,12 @@ 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"
diff --git a/browser/devtools/styleinspector/rule-view.js b/browser/devtools/styleinspector/rule-view.js
index 34dbe7bc80fe469e93243ba27d547db79f23c70d..c148ad1dcd57b4edd662370cfeacfbd36f281b44 100644
--- a/browser/devtools/styleinspector/rule-view.js
+++ b/browser/devtools/styleinspector/rule-view.js
@@ -2854,6 +2854,13 @@ TextPropertyEditor.prototype = {
       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.
    */
@@ -3004,7 +3011,7 @@ TextPropertyEditor.prototype = {
    * @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;
     }
@@ -3068,14 +3075,14 @@ TextPropertyEditor.prototype = {
 
     // 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", "");
@@ -3156,7 +3163,6 @@ TextPropertyEditor.prototype = {
   },
 
   _onStartEditing: function() {
-    this.element.classList.remove("ruleview-overridden");
     this._previewValue(this.prop.value);
   },
 
@@ -3294,23 +3300,38 @@ TextPropertyEditor.prototype = {
    *        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);
       }
     }
   },
@@ -3342,35 +3363,38 @@ TextPropertyEditor.prototype = {
    * @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.
@@ -3444,6 +3468,9 @@ TextPropertyEditor.prototype = {
       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);
diff --git a/browser/devtools/styleinspector/test/browser.ini b/browser/devtools/styleinspector/test/browser.ini
index e1e29c01da3af1d8615070a6be5a156657380876..fd6625ab68ce5247a1c02d120777771ddb5328d6 100644
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -92,6 +92,10 @@ skip-if = e10s # Bug 1039528: "inspect element" contextual-menu doesn't work wit
 [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]
diff --git a/browser/devtools/styleinspector/test/browser_ruleview_edit-property_04.js b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_04.js
new file mode 100644
index 0000000000000000000000000000000000000000..886d32490a9020d1c350c422b75af96de681a8fe
--- /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.");
+}
diff --git a/browser/devtools/styleinspector/test/browser_ruleview_edit-property_05.js b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_05.js
new file mode 100644
index 0000000000000000000000000000000000000000..583704906f403bcfaa0ce2bb0e7b77faccecc894
--- /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;
+}
diff --git a/browser/devtools/styleinspector/test/browser_ruleview_edit-property_06.js b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_06.js
new file mode 100644
index 0000000000000000000000000000000000000000..f15cf518ff0f1ce82fcef0171ee1e7bc8bfe1927
--- /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.");
+}
diff --git a/browser/devtools/styleinspector/test/browser_ruleview_edit-property_07.js b/browser/devtools/styleinspector/test/browser_ruleview_edit-property_07.js
new file mode 100644
index 0000000000000000000000000000000000000000..72364c5d45573a6de9eeb526197264c45b874fbd
--- /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.");
+}
diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css
index a805935a4ab0d7accd4a4601349d7e3fd8ff5117..1df1947100969ce0af5874adaed988106b9cec8d 100644
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2028,6 +2028,14 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
   .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+ */
diff --git a/browser/themes/windows/jar.mn b/browser/themes/windows/jar.mn
index cece366c69aeb307cfff22960f5bff596c52533f..96b55fb892b1076d0b3cfdc917f29dea04146ec0 100644
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -330,8 +330,10 @@ browser.jar:
         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)
@@ -346,6 +348,12 @@ browser.jar:
         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)
 
@@ -704,3 +712,9 @@ browser.jar:
 % 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
diff --git a/browser/themes/windows/tabbrowser/connecting@2x.png b/browser/themes/windows/tabbrowser/connecting@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..97e2b2eb67ecc58e98dd839187143ab6870699e5
Binary files /dev/null and b/browser/themes/windows/tabbrowser/connecting@2x.png differ
diff --git a/browser/themes/windows/tabbrowser/loading.png b/browser/themes/windows/tabbrowser/loading.png
index ba54836e9834f2c495bcfb05738b56481b2dbed0..b886c73eff9239196fbe3db01116c220f726469b 100644
Binary files a/browser/themes/windows/tabbrowser/loading.png and b/browser/themes/windows/tabbrowser/loading.png differ
diff --git a/browser/themes/windows/tabbrowser/loading@2x.png b/browser/themes/windows/tabbrowser/loading@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac928c585372bd72707d895c37c9e11931b327bd
Binary files /dev/null and b/browser/themes/windows/tabbrowser/loading@2x.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-end-preWin10.png b/browser/themes/windows/tabbrowser/tab-background-end-preWin10.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb353b17e70657d85e501a30ca6e2e6249e83a76
Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-end-preWin10.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-end-preWin10@2x.png b/browser/themes/windows/tabbrowser/tab-background-end-preWin10@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..eefb6ac47cd820af31556f1d8c6631ad36396baa
Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-end-preWin10@2x.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-end.png b/browser/themes/windows/tabbrowser/tab-background-end.png
index fb353b17e70657d85e501a30ca6e2e6249e83a76..d68ea6da6a9834dfed10a88e73afa10ba1933099 100644
Binary files a/browser/themes/windows/tabbrowser/tab-background-end.png and b/browser/themes/windows/tabbrowser/tab-background-end.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-end@2x.png b/browser/themes/windows/tabbrowser/tab-background-end@2x.png
index eefb6ac47cd820af31556f1d8c6631ad36396baa..8ed84ab37969668dcee44c1f5915cde2a48e59a0 100644
Binary files a/browser/themes/windows/tabbrowser/tab-background-end@2x.png and b/browser/themes/windows/tabbrowser/tab-background-end@2x.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-middle-preWin10.png b/browser/themes/windows/tabbrowser/tab-background-middle-preWin10.png
new file mode 100644
index 0000000000000000000000000000000000000000..51e066c2e9c6d71f5ce4846b1b030d2f111127ef
Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-middle-preWin10.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-middle-preWin10@2x.png b/browser/themes/windows/tabbrowser/tab-background-middle-preWin10@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..b26cb95de6ab3231f676a4c939a2bbf210557771
Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-middle-preWin10@2x.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-middle.png b/browser/themes/windows/tabbrowser/tab-background-middle.png
index 51e066c2e9c6d71f5ce4846b1b030d2f111127ef..faaf7e38e5d9726af460735604497ee0e8eed164 100644
Binary files a/browser/themes/windows/tabbrowser/tab-background-middle.png and b/browser/themes/windows/tabbrowser/tab-background-middle.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-middle@2x.png b/browser/themes/windows/tabbrowser/tab-background-middle@2x.png
index b26cb95de6ab3231f676a4c939a2bbf210557771..c9d245f4f4459fe4e1265354b87f47b1e3a3ce32 100644
Binary files a/browser/themes/windows/tabbrowser/tab-background-middle@2x.png and b/browser/themes/windows/tabbrowser/tab-background-middle@2x.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-start-preWin10.png b/browser/themes/windows/tabbrowser/tab-background-start-preWin10.png
new file mode 100644
index 0000000000000000000000000000000000000000..cf0dc852ac52f8b6a1d9fdd0b6211494263aa60b
Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-start-preWin10.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-start-preWin10@2x.png b/browser/themes/windows/tabbrowser/tab-background-start-preWin10@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..bbfc77dd189d6885201c4d9e923836485cfd8ad1
Binary files /dev/null and b/browser/themes/windows/tabbrowser/tab-background-start-preWin10@2x.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-start.png b/browser/themes/windows/tabbrowser/tab-background-start.png
index cf0dc852ac52f8b6a1d9fdd0b6211494263aa60b..d1f0b5561034d68884c0c6e48f0697da4da86474 100644
Binary files a/browser/themes/windows/tabbrowser/tab-background-start.png and b/browser/themes/windows/tabbrowser/tab-background-start.png differ
diff --git a/browser/themes/windows/tabbrowser/tab-background-start@2x.png b/browser/themes/windows/tabbrowser/tab-background-start@2x.png
index bbfc77dd189d6885201c4d9e923836485cfd8ad1..e860275a6435b42e181b6de2e60a0be01b031e55 100644
Binary files a/browser/themes/windows/tabbrowser/tab-background-start@2x.png and b/browser/themes/windows/tabbrowser/tab-background-start@2x.png differ
diff --git a/mobile/android/chrome/content/aboutLogins.js b/mobile/android/chrome/content/aboutLogins.js
index a0e41de3a15e4e721a01acdb18da9d70c77e1177..49682a0d62379100dd4c0838324fcba68d80e0cf 100644
--- a/mobile/android/chrome/content/aboutLogins.js
+++ b/mobile/android/chrome/content/aboutLogins.js
@@ -7,6 +7,7 @@ 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)
@@ -53,7 +54,9 @@ let Logins = {
     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);
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index 56174c037a0fba3426385ae5d4a45407b06046cf..7c4e5c6fdebd39d0d459b3a3aa288d9556d5e99e 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4737,11 +4737,11 @@ pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,tes
 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");
 
diff --git a/testing/taskcluster/tasks/branches/base_jobs.yml b/testing/taskcluster/tasks/branches/base_jobs.yml
index 02ece18afd2359524f34b7f80e4bdf67f86e893e..240080db4cd10adcb47f1a5139eb10ac54fa71e0 100644
--- a/testing/taskcluster/tasks/branches/base_jobs.yml
+++ b/testing/taskcluster/tasks/branches/base_jobs.yml
@@ -110,6 +110,8 @@ tests:
     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:
diff --git a/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml b/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml
index c69c5e1f4a9c1c476082e114296aa739efae4545..a7a880137b834eade4d855ca8f807f748a9abf9e 100644
--- a/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml
+++ b/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml
@@ -1,7 +1,6 @@
 ---
 $inherits:
   from: 'tasks/test.yml'
-reruns: 2
 task:
   metadata:
     name: '[TC] Mulet Gaia JS Integration Test'
@@ -18,9 +17,9 @@ task:
         --config-file b2g/gaia_integration_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
         --config-file ./mozharness_configs/remove_executables.py
-        --installer-url {{build_url}}
         --no-pull
-        --test-url {{tests_url}}
+        --installer-url {{build_url}}
+        --test-packages-url {{test_packages_url}}
         --download-symbols ondemand
         --total-chunk {{total_chunks}}
         --this-chunk {{chunk}}
@@ -35,8 +34,11 @@ task:
   extra:
     chunks:
       total: 20
+    treeherderEnv:
+      - production
+      - staging
     treeherder:
       groupName: Gaia JS Integration Test
-      groupSymbol: tc-Gij
+      groupSymbol: Gij
       symbol: '{{chunk}}'
       productName: b2g
diff --git a/toolkit/components/autocomplete/nsAutoCompleteController.cpp b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
index 2fc86b9570eb838d0e993561851d2902e24188f6..fa23861de14b1097313c33bd0648c02739d62219 100644
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -1344,7 +1344,9 @@ nsAutoCompleteController::EnterMatch(bool aIsPopupSelection)
         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
diff --git a/toolkit/components/passwordmgr/OSCrypto.jsm b/toolkit/components/passwordmgr/OSCrypto.jsm
new file mode 100644
index 0000000000000000000000000000000000000000..306e6b4b13bf331c94dd48375c2af22c826a58ee
--- /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");
+}
diff --git a/toolkit/components/passwordmgr/OSCrypto_win.js b/toolkit/components/passwordmgr/OSCrypto_win.js
new file mode 100644
index 0000000000000000000000000000000000000000..61d4deb87a13d286ed3fff9376e864140acd5bed
--- /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();
+  },
+};
diff --git a/toolkit/components/passwordmgr/moz.build b/toolkit/components/passwordmgr/moz.build
index 08c8dce981a262e11b1b70649dedf48e6b4e94c1..fc6aa6ae442d17f404a658a45c1931bb1f9cfd00 100644
--- a/toolkit/components/passwordmgr/moz.build
+++ b/toolkit/components/passwordmgr/moz.build
@@ -41,10 +41,10 @@ EXTRA_PP_JS_MODULES += [
 
 EXTRA_JS_MODULES += [
     'InsecurePasswordUtils.jsm',
-    'LoginDoorhangers.jsm',
     'LoginHelper.jsm',
     'LoginManagerContent.jsm',
     'LoginRecipes.jsm',
+    'OSCrypto.jsm',
 ]
 
 if CONFIG['OS_TARGET'] == 'Android':
@@ -56,10 +56,16 @@ else:
         '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('**'):
diff --git a/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm b/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm
index 2a1a274be27c5c96fedc55ef808318a3fb87f150..00342aa56d973fd26c570f73bac26ae83df167e6 100644
--- a/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm
+++ b/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm
@@ -133,7 +133,9 @@ function SearchSuggestionControllerWrapper(engine, searchToken,
   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.
@@ -164,6 +166,14 @@ SearchSuggestionControllerWrapper.prototype = {
             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.
    */
diff --git a/toolkit/components/places/UnifiedComplete.js b/toolkit/components/places/UnifiedComplete.js
index a1f1f66230d4e64cceb26b7bd37765f024a165f3..85a84260282a363e2d2ca8c09a64d32f01b0336f 100644
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -38,6 +38,8 @@ 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;
@@ -416,6 +418,7 @@ XPCOMUtils.defineLazyGetter(this, "Prefs", () => {
     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) {
@@ -598,9 +601,11 @@ function makeActionURL(action, params) {
  *        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();
@@ -632,6 +637,8 @@ function Search(searchString, searchParam, autocompleteListener,
     this._trimmedOriginalSearchString.slice(pathIndex)
   );
 
+  this._prohibitSearchSuggestions = prohibitSearchSuggestions;
+
   this._listener = autocompleteListener;
   this._autocompleteSearch = autocompleteSearch;
 
@@ -909,18 +916,29 @@ Search.prototype = {
   }),
 
   *_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)
@@ -940,6 +958,28 @@ Search.prototype = {
     }
   },
 
+  _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("/");
@@ -1701,8 +1741,15 @@ UnifiedComplete.prototype = {
     // 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.
@@ -1745,6 +1792,7 @@ UnifiedComplete.prototype = {
     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)
diff --git a/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js b/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js
index 290d44c2b212580d2734d880af077b04d453a687..6d51c045de43c95b31b18e1ff4d8717507c28c2d 100644
--- a/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_searchSuggestions.js
@@ -414,3 +414,71 @@ add_task(function* mixup_frecency() {
 
   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();
+});
diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
index 8287e4b0e25353011e0d5994bdefd60be799747d..73b66cf3d74da02806b725c36c9877921df158ae 100644
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8223,6 +8223,14 @@
     "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",
diff --git a/toolkit/components/url-classifier/SafeBrowsing.jsm b/toolkit/components/url-classifier/SafeBrowsing.jsm
index 8d6bbfe9fdd3dd76e48ed6de5bc5cf008809638c..9038a9c02d90a22fa906506aa4016d8dd568e172 100644
--- a/toolkit/components/url-classifier/SafeBrowsing.jsm
+++ b/toolkit/components/url-classifier/SafeBrowsing.jsm
@@ -214,6 +214,10 @@ this.SafeBrowsing = {
     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" +
@@ -224,6 +228,11 @@ this.SafeBrowsing = {
     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"].
@@ -238,7 +247,8 @@ this.SafeBrowsing = {
     };
 
     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();
diff --git a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
index 7f2a4ce9ec530606a936a06904d88e8fe9207668..90475235e077d3ed1fb22b3d3857f88f3b22cd05 100644
--- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
+++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
@@ -54,17 +54,20 @@ function cleanUp() {
   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"]
@@ -144,8 +147,7 @@ function doSimpleUpdate(updateText, success, failure) {
     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();
@@ -187,7 +189,7 @@ function doStreamUpdate(updateText, success, failure, downloadFailure) {
     downloadFailure = failure;
   }
 
-  streamUpdater.downloadUpdates("test-phish-simple,test-malware-simple,test-unwanted-simple", "",
+  streamUpdater.downloadUpdates(allTables, "",
                                 dataUpdate, success, failure, downloadFailure);
 }
 
diff --git a/tools/mercurial/hgsetup/wizard.py b/tools/mercurial/hgsetup/wizard.py
index 548323b1f1528def29bb72e52331994031e12162..98f83cf49123ef38cb925af576835bd5385446cd 100644
--- a/tools/mercurial/hgsetup/wizard.py
+++ b/tools/mercurial/hgsetup/wizard.py
@@ -213,6 +213,20 @@ 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."""
 
@@ -404,6 +418,23 @@ class MercurialSetupWizard(object):
 
         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()]