merge fx-team to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 17 Sep 2015 14:42:06 +0200
changeset 295460 45922303c4ee1fea78cba83475dadb1b59ef1a16
parent 295447 8d4c37a86b576db4ef150dff715a89ff77e84bf5 (current diff)
parent 295459 888d001bcc3c17043e0800bc13c08274a1fec0ae (diff)
child 295583 de0e763b521062d9a9da0ce029792be026886223
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.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 fx-team to mozilla-central a=merge
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6978,26 +6978,24 @@ var gIdentityHandler = {
 
     // NOTE: We do NOT update the identity popup (the control center) when
     // we receive a new security state. If the user opened the popup and looks
     // at the provided information we don't want to suddenly change the panel
     // contents.
   },
 
   /**
-   * Return the eTLD+1 version of the current hostname
+   * Attempt to provide proper IDN treatment for host names
    */
-  getEffectiveHost : function() {
+  getEffectiveHost: function() {
     if (!this._IDNService)
       this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
                          .getService(Ci.nsIIDNService);
     try {
-      let baseDomain =
-        Services.eTLD.getBaseDomainFromHost(this._uri.host);
-      return this._IDNService.convertToDisplayIDN(baseDomain, {});
+      return this._IDNService.convertToDisplayIDN(this._uri.host, {});
     } catch (e) {
       // If something goes wrong (e.g. host is an IP address) just fail back
       // to the full domain.
       return this._uri.host;
     }
   },
 
   /**
@@ -7157,16 +7155,17 @@ var gIdentityHandler = {
       updateAttribute(element, "isbroken", isBroken);
     }
 
     // Initialize the optional strings to empty values
     let supplemental = "";
     let verifier = "";
     let host = "";
     let owner = "";
+    let crop = "start";
 
     try {
       host = this.getEffectiveHost();
     } catch (e) {
       // Some URIs might have no hosts.
     }
 
     // Fallback for special protocols.
@@ -7176,16 +7175,18 @@ var gIdentityHandler = {
 
     // Fill in the CA name if we have a valid TLS certificate.
     if (isSecure) {
       verifier = this._identityBox.tooltipText;
     }
 
     // Fill in organization information if we have a valid EV certificate.
     if (isEV) {
+      crop = "end";
+
       let iData = this.getIdentityData();
       host = owner = iData.subjectOrg;
       verifier = this._identityBox.tooltipText;
 
       // Build an appropriate supplemental block out of whatever location data we have
       if (iData.city)
         supplemental += iData.city + "\n";
       if (iData.state && iData.country)
@@ -7195,16 +7196,17 @@ var gIdentityHandler = {
         supplemental += iData.state;
       else if (iData.country) // Country only
         supplemental += iData.country;
     }
 
     // Push the appropriate strings out to the UI. Need to use |value| for the
     // host as it's a <label> that will be cropped if too long. Using
     // |textContent| would simply wrap the value.
+    this._identityPopupContentHost.setAttribute("crop", crop);
     this._identityPopupContentHost.setAttribute("value", host);
     this._identityPopupContentOwner.textContent = owner;
     this._identityPopupContentSupp.textContent = supplemental;
     this._identityPopupContentVerif.textContent = verifier;
 
     // Update per-site permissions section.
     this.updateSitePermissions();
   },
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5481,16 +5481,17 @@
           context.fillRect(0, 0, canvas.width, canvas.height);
           // Create a panel to use it in setDragImage
           // which will tell xul to render a panel that follows
           // the pointer while a dnd session is on.
           if (!this._dndPanel) {
             this._dndCanvas = canvas;
             this._dndPanel = document.createElement("panel");
             this._dndPanel.setAttribute("type", "drag");
+            this._dndPanel.setAttribute("mousethrough", "always");
             this._dndPanel.appendChild(canvas);
             document.documentElement.appendChild(this._dndPanel);
           }
           // PageThumb is async with e10s but that's fine
           // since we can update the panel during the dnd.
           PageThumbs.captureToCanvas(browser, canvas);
           toDrag = this._dndPanel;
         } else {
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -465,16 +465,17 @@ skip-if = e10s # Bug 1100700 - test reli
 [browser_urlbarAutoFillTrimURLs.js]
 [browser_urlbarCopying.js]
 [browser_urlbarDelete.js]
 [browser_urlbarEnter.js]
 [browser_urlbarEnterAfterMouseOver.js]
 skip-if = os == "linux" || e10s # Bug 1073339 - Investigate autocomplete test unreliability on Linux/e10s
 [browser_urlbarRevert.js]
 [browser_urlbarSearchSingleWordNotification.js]
+[browser_urlbarSearchSuggestions.js]
 [browser_urlbarSearchSuggestionsNotification.js]
 [browser_urlbarStop.js]
 [browser_urlbarTrimURLs.js]
 [browser_urlbar_autoFill_backspaced.js]
 [browser_urlbar_search_healthreport.js]
 [browser_urlbar_searchsettings.js]
 [browser_utilityOverlay.js]
 [browser_viewSourceInTabOnViewSource.js]
--- a/browser/base/content/test/general/browser_identity_UI.js
+++ b/browser/base/content/test/general/browser_identity_UI.js
@@ -12,43 +12,43 @@ function test() {
 }
 
 // Greek IDN for 'example.test'.
 var idnDomain = "\u03C0\u03B1\u03C1\u03AC\u03B4\u03B5\u03B9\u03B3\u03BC\u03B1.\u03B4\u03BF\u03BA\u03B9\u03BC\u03AE";
 var tests = [
   {
     name: "normal domain",
     location: "http://test1.example.org/",
-    effectiveHost: "example.org"
+    effectiveHost: "test1.example.org"
   },
   {
     name: "view-source",
     location: "view-source:http://example.com/",
     effectiveHost: null
   },
   {
     name: "normal HTTPS",
     location: "https://example.com/",
     effectiveHost: "example.com",
     isHTTPS: true
   },
   {
     name: "IDN subdomain",
     location: "http://sub1." + idnDomain + "/",
-    effectiveHost: idnDomain
+    effectiveHost: "sub1." + idnDomain
   },
   {
     name: "subdomain with port",
     location: "http://sub1.test1.example.org:8000/",
-    effectiveHost: "example.org"
+    effectiveHost: "sub1.test1.example.org"
   },
   {
     name: "subdomain HTTPS",
     location: "https://test1.example.com/",
-    effectiveHost: "example.com",
+    effectiveHost: "test1.example.com",
     isHTTPS: true
   },
   {
     name: "view-source HTTPS",
     location: "view-source:https://example.com/",
     effectiveHost: null,
     isHTTPS: true
   },
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_urlbarSearchSuggestions.js
@@ -0,0 +1,63 @@
+const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
+const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
+
+// Must run first.
+add_task(function* prepare() {
+  Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true);
+  let engine = yield promiseNewSearchEngine(TEST_ENGINE_BASENAME);
+  let oldCurrentEngine = Services.search.currentEngine;
+  Services.search.currentEngine = engine;
+  registerCleanupFunction(function () {
+    Services.prefs.clearUserPref(SUGGEST_URLBAR_PREF);
+    Services.search.currentEngine = oldCurrentEngine;
+
+    // Clicking suggestions causes visits to search results pages, so clear that
+    // history now.
+    yield PlacesTestUtils.clearHistory();
+
+    // Make sure the popup is closed for the next test.
+    gURLBar.blur();
+    Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
+  });
+});
+
+add_task(function* clickSuggestion() {
+  gURLBar.focus();
+  yield promiseAutocompleteResultPopup("foo");
+  let [idx, suggestion] = yield promiseFirstSuggestion();
+  let item = gURLBar.popup.richlistbox.getItemAtIndex(idx);
+  let loadPromise = promiseTabLoaded(gBrowser.selectedTab);
+  item.click();
+  yield loadPromise;
+  let uri = Services.search.currentEngine.getSubmission(suggestion).uri;
+  Assert.ok(uri.equals(gBrowser.currentURI),
+            "The search results page should have loaded");
+});
+
+function getFirstSuggestion() {
+  let controller = gURLBar.popup.input.controller;
+  let matchCount = controller.matchCount;
+  let present = false;
+  for (let i = 0; i < matchCount; i++) {
+    let url = controller.getValueAt(i);
+    let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/);
+    if (mozActionMatch) {
+      let [, type, paramStr] = mozActionMatch;
+      let params = JSON.parse(paramStr);
+      if (type == "searchengine" && "searchSuggestion" in params) {
+        return [i, params.searchSuggestion];
+      }
+    }
+  }
+  return [-1, null];
+}
+
+function promiseFirstSuggestion() {
+  return new Promise(resolve => {
+    let pair;
+    waitForCondition(() => {
+      pair = getFirstSuggestion();
+      return pair[0] >= 0;
+    }, () => resolve(pair));
+  });
+}
--- a/browser/base/content/test/general/browser_urlbarSearchSuggestionsNotification.js
+++ b/browser/base/content/test/general/browser_urlbarSearchSuggestionsNotification.js
@@ -178,16 +178,29 @@ add_task(function* multipleWindows() {
   gURLBar.focus();
   yield promiseAutocompleteResultPopup("win1done");
   assertVisible(false);
 
   yield BrowserTestUtils.closeWindow(win2);
   yield BrowserTestUtils.closeWindow(win3);
 });
 
+add_task(function* enableOutsideNotification() {
+  // Setting the suggest.searches pref outside the notification (e.g., by
+  // ticking the checkbox in the preferences window) should hide it.
+  Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true);
+  Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, false);
+  yield setUserMadeChoicePref(false);
+
+  Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true);
+  gURLBar.focus();
+  yield promiseAutocompleteResultPopup("foo");
+  assertVisible(false);
+});
+
 /**
  * Setting the choice pref triggers a pref observer in the urlbar, which hides
  * the notification if it's present.  This function returns a promise that's
  * resolved once the observer fires.
  *
  * @param userMadeChoice  A boolean, the pref's new value.
  * @return A Promise that's resolved when the observer fires -- or, if the pref
  *         is currently the given value, that's resolved immediately.
@@ -209,23 +222,23 @@ function setUserMadeChoicePref(userMadeC
 }
 
 function suggestionsPresent() {
   let controller = gURLBar.popup.input.controller;
   let matchCount = controller.matchCount;
   let present = false;
   for (let i = 0; i < matchCount; i++) {
     let url = controller.getValueAt(i);
-    let [, type, paramStr] = url.match(/^moz-action:([^,]+),(.*)$/);
-    let params = {};
-    try {
-      params = JSON.parse(paramStr);
-    } catch (err) {}
-    if (type == "searchengine" && "searchSuggestion" in params) {
-      return true;
+    let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/);
+    if (mozActionMatch) {
+      let [, type, paramStr] = mozActionMatch;
+      let params = JSON.parse(paramStr);
+      if (type == "searchengine" && "searchSuggestion" in params) {
+        return true;
+      }
     }
   }
   return false;
 }
 
 function promiseSuggestionsPresent() {
   return new Promise(resolve => {
     waitForCondition(suggestionsPresent, resolve);
--- a/browser/base/content/test/general/searchSuggestionEngine.xml
+++ b/browser/base/content/test/general/searchSuggestionEngine.xml
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- Any copyright is dedicated to the Public Domain.
    - http://creativecommons.org/publicdomain/zero/1.0/ -->
 
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>browser_searchSuggestionEngine searchSuggestionEngine.xml</ShortName>
 <Url type="application/x-suggestions+json" method="GET" template="http://mochi.test:8888/browser/browser/base/content/test/general/searchSuggestionEngine.sjs?{searchTerms}"/>
-<Url type="text/html" method="GET" template="http://www.browser-searchSuggestionEngine.com/searchSuggestionEngine&amp;terms={searchTerms}" rel="searchform"/>
+<Url type="text/html" method="GET" template="http://mochi.test:8888/browser/browser/base/content/test/general/searchSuggestionEngine.sjs?{searchTerms}" rel="searchform"/>
 </SearchPlugin>
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -61,19 +61,17 @@ file, You can obtain one at http://mozil
 
         this._prefs.addObserver("", this, false);
         this.clickSelectsAll = this._prefs.getBoolPref("clickSelectsAll");
         this.doubleClickSelectsAll = this._prefs.getBoolPref("doubleClickSelectsAll");
         this.completeDefaultIndex = this._prefs.getBoolPref("autoFill");
         this.timeout = this._prefs.getIntPref("delay");
         this._formattingEnabled = this._prefs.getBoolPref("formatting.enabled");
         this._mayTrimURLs = this._prefs.getBoolPref("trimURLs");
-        this._userMadeSearchSuggestionsChoice =
-          this._prefs.getBoolPref("userMadeSearchSuggestionsChoice");
-
+        this._cacheUserMadeSearchSuggestionsChoice();
         this.inputField.controllers.insertControllerAt(0, this._copyCutController);
         this.inputField.addEventListener("paste", this, false);
         this.inputField.addEventListener("mousedown", this, false);
         this.inputField.addEventListener("mousemove", this, false);
         this.inputField.addEventListener("mouseout", this, false);
         this.inputField.addEventListener("overflow", this, false);
         this.inputField.addEventListener("underflow", this, false);
 
@@ -658,21 +656,29 @@ file, You can obtain one at http://mozil
                 break;
               case "delay":
                 this.timeout = this._prefs.getIntPref(aData);
                 break;
               case "formatting.enabled":
                 this._formattingEnabled = this._prefs.getBoolPref(aData);
                 break;
               case "userMadeSearchSuggestionsChoice":
-                this._userMadeSearchSuggestionsChoice =
-                  this._prefs.getBoolPref(aData);
-                this.popup.searchSuggestionsNotificationWasDismissed(
-                  this._prefs.getBoolPref("suggest.searches")
-                );
+              case "suggest.searches":
+                this._cacheUserMadeSearchSuggestionsChoice();
+                // Make sure the urlbar is focused.  It won't be, for example,
+                // if the user used an accesskey to make an opt-in choice.
+                // mIgnoreFocus prevents the text from being selected.
+                this.mIgnoreFocus = true;
+                this.focus();
+                this.mIgnoreFocus = false;
+                if (this._userMadeSearchSuggestionsChoice) {
+                  this.popup.searchSuggestionsNotificationWasDismissed(
+                    this._prefs.getBoolPref("suggest.searches")
+                  );
+                }
                 break;
               case "trimURLs":
                 this._mayTrimURLs = this._prefs.getBoolPref(aData);
                 break;
               case "unifiedcomplete":
                 let useUnifiedComplete = false;
                 try {
                   useUnifiedComplete = this._prefs.getBoolPref(aData);
@@ -820,16 +826,17 @@ file, You can obtain one at http://mozil
             // a URL.
             action.params = {
               url: params,
             }
             return action;
           }
 
           for (let key of [
+            "engineName",
             "input",
             "searchQuery",
             "searchSuggestion",
           ]) {
             if (action.params[key]) {
               action.params[key] = decodeURIComponent(action.params[key]);
             }
           }
@@ -918,16 +925,24 @@ file, You can obtain one at http://mozil
           return this.mController.handleDelete();
         ]]></body>
       </method>
 
       <field name="_userMadeSearchSuggestionsChoice"><![CDATA[
         false
       ]]></field>
 
+      <method name="_cacheUserMadeSearchSuggestionsChoice">
+        <body><![CDATA[
+          this._userMadeSearchSuggestionsChoice =
+            this._prefs.getBoolPref("userMadeSearchSuggestionsChoice") ||
+            this._prefs.getBoolPref("suggest.searches");
+        ]]></body>
+      </method>
+
       <property name="shouldShowSearchSuggestionsNotification" readonly="true">
         <getter><![CDATA[
           return !this._userMadeSearchSuggestionsChoice &&
                  !this.inPrivateContext &&
                  // When _urlbarFocused is true, tabbrowser would close the
                  // popup if it's opened here, so don't show the notification.
                  !gBrowser.selectedBrowser._urlbarFocused &&
                  Services.prefs.getBoolPref("browser.search.suggest.enabled") &&
--- a/browser/components/controlcenter/content/panel.inc.xul
+++ b/browser/components/controlcenter/content/panel.inc.xul
@@ -5,17 +5,17 @@
 <panel id="identity-popup"
        type="arrow"
        hidden="true"
        onpopupshown="gIdentityHandler.onPopupShown(event);"
        onpopuphidden="gIdentityHandler.onPopupHidden(event);"
        orient="vertical">
 
   <broadcasterset>
-    <broadcaster id="identity-popup-content-host" class="identity-popup-headline" crop="end"/>
+    <broadcaster id="identity-popup-content-host" class="identity-popup-headline" crop="start"/>
     <broadcaster id="identity-popup-mcb-learn-more" class="text-link plain" value="&identity.learnMore;"/>
   </broadcasterset>
 
   <panelmultiview id="identity-popup-multiView"
                   mainViewId="identity-popup-mainView">
     <panelview id="identity-popup-mainView" flex="1">
 
       <!-- Security Section -->
--- a/browser/components/loop/ui/ui-showcase.css
+++ b/browser/components/loop/ui/ui-showcase.css
@@ -1,13 +1,13 @@
 /* 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/. */
 
-html[dir="rtl"]:not(.outer-html) {
+html[dir="rtl"]:not(#outer-html) {
   /* Temporary work around and visual hack -
    * Scope the root body overflow visible to remove the scroll bars that appear
    * inside an iFrame. Can remove this rule after core layout fix for
    * https://bugzilla.mozilla.org/show_bug.cgi?id=1204680 has landed.
    */
   overflow: hidden;
 }
 
--- a/browser/components/migration/EdgeProfileMigrator.js
+++ b/browser/components/migration/EdgeProfileMigrator.js
@@ -14,16 +14,20 @@ function EdgeProfileMigrator() {
 
 EdgeProfileMigrator.prototype = Object.create(MigratorPrototype);
 
 EdgeProfileMigrator.prototype.getResources = function() {
   let resources = [
     MSMigrationUtils.getBookmarksMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE),
     MSMigrationUtils.getCookiesMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE),
   ];
+  let windowsVaultFormPasswordsMigrator =
+    MSMigrationUtils.getWindowsVaultFormPasswordsMigrator();
+  windowsVaultFormPasswordsMigrator.name = "EdgeVaultFormPasswords";
+  resources.push(windowsVaultFormPasswordsMigrator);
   return resources.filter(r => r.exists);
 };
 
 /* Somewhat counterintuitively, this returns:
  * - |null| to indicate "There is only 1 (default) profile" (on win10+)
  * - |[]| to indicate "There are no profiles" (on <=win8.1) which will avoid using this migrator.
  * See MigrationUtils.jsm for slightly more info on how sourceProfiles is used.
  */
--- a/browser/components/migration/IEProfileMigrator.js
+++ b/browser/components/migration/IEProfileMigrator.js
@@ -13,28 +13,32 @@ const kLoginsKey = "Software\\Microsoft\
 const kMainKey = "Software\\Microsoft\\Internet Explorer\\Main";
 
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm");
 Cu.import("resource:///modules/MSMigrationUtils.jsm");
+Cu.import("resource://gre/modules/LoginHelper.jsm");
+
 
 XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
                                   "resource://gre/modules/ctypes.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
                                   "resource://gre/modules/OSCrypto.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
                                   "resource://gre/modules/WindowsRegistry.jsm");
 
 Cu.importGlobalProperties(["URL"]);
 
+let CtypesKernelHelpers = MSMigrationUtils.CtypesKernelHelpers;
+
 ////////////////////////////////////////////////////////////////////////////////
 //// Resources
 
 
 function History() {
 }
 
 History.prototype = {
@@ -121,22 +125,29 @@ History.prototype = {
         aCallback(this._success);
       }
     });
   }
 };
 
 // IE form password migrator supporting windows from XP until 7 and IE from 7 until 11
 function IE7FormPasswords () {
+  // used to distinguish between this migrator and other passwords migrators in tests.
+  this.name = "IE7FormPasswords";
 }
 
 IE7FormPasswords.prototype = {
   type: MigrationUtils.resourceTypes.PASSWORDS,
 
   get exists() {
+    // work only on windows until 7
+    if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
+      return false;
+    }
+
     try {
       let nsIWindowsRegKey = Ci.nsIWindowsRegKey;
       let key = Cc["@mozilla.org/windows-registry-key;1"].
                 createInstance(nsIWindowsRegKey);
       key.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, kLoginsKey,
                nsIWindowsRegKey.ACCESS_READ);
       let count = key.valueCount;
       key.close();
@@ -166,17 +177,17 @@ IE7FormPasswords.prototype = {
     aCallback(true);
   },
 
   /**
    * Migrate the logins that were saved for the uris arguments.
    * @param {nsIURI[]} uris - the uris that are going to be migrated.
    */
   _migrateURIs(uris) {
-    this.ctypesHelpers = new MSMigrationUtils.CtypesHelpers();
+    this.ctypesKernelHelpers = new MSMigrationUtils.CtypesKernelHelpers();
     this._crypto = new OSCrypto();
     let nsIWindowsRegKey = Ci.nsIWindowsRegKey;
     let key = Cc["@mozilla.org/windows-registry-key;1"].
               createInstance(nsIWindowsRegKey);
     key.open(nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, kLoginsKey,
              nsIWindowsRegKey.ACCESS_READ);
 
     let urlsSet = new Set(); // set of the already processed urls.
@@ -235,72 +246,36 @@ IE7FormPasswords.prototype = {
       Cu.reportError("We failed to decrypt and import some logins. " +
                      "This is likely because we didn't find the URLs where these " +
                      "passwords were submitted in the IE history and which are needed to be used " +
                      "as keys in the decryption.");
     }
 
     key.close();
     this._crypto.finalize();
-    this.ctypesHelpers.finalize();
+    this.ctypesKernelHelpers.finalize();
   },
 
   _crypto: null,
 
   /**
    * Add the logins to the password manager.
    * @param {Object[]} logins - array of the login details.
    */
   _addLogins(ieLogins) {
-    function addLogin(login, existingLogins) {
-      // Add the login only if it doesn't already exist
-      // if the login is not already available, it s going to be added or merged with another
-      // login
-      if (existingLogins.some(l => login.matches(l, true))) {
-        return;
-      }
-      let isUpdate = false; // the login is just an update for an old one
-      for (let existingLogin of existingLogins) {
-        if (login.username == existingLogin.username && login.password != existingLogin.password) {
-          // if a login with the same username and different password already exists and it's older
-          // than the current one, that login needs to be updated using the current one details
-          if (login.timePasswordChanged > existingLogin.timePasswordChanged) {
-            // Bug 1187190: Password changes should be propagated depending on timestamps.
-
-            // the existing login password and timestamps should be updated
-            let propBag = Cc["@mozilla.org/hash-property-bag;1"].
-                          createInstance(Ci.nsIWritablePropertyBag);
-            propBag.setProperty("password", login.password);
-            propBag.setProperty("timePasswordChanged", login.timePasswordChanged);
-            Services.logins.modifyLogin(existingLogin, propBag);
-            // make sure not to add the new login
-            isUpdate = true;
-          }
-        }
-      }
-      // if the new login is not an update, add it.
-      if (!isUpdate) {
-        Services.logins.addLogin(login);
-      }
-    }
-
     for (let ieLogin of ieLogins) {
       try {
-        let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
-
-        login.init(ieLogin.url, "", null,
-                   ieLogin.username, ieLogin.password, "", "");
-        login.QueryInterface(Ci.nsILoginMetaInfo);
-        login.timeCreated = ieLogin.creation;
-        login.timeLastUsed = ieLogin.creation;
-        login.timePasswordChanged = ieLogin.creation;
-        // login.timesUsed is going to set to the default value 1
-        // Add the login only if there's not an existing entry
-        let existingLogins = Services.logins.findLogins({}, login.hostname, "", null);
-        addLogin(login, existingLogins);
+        // create a new login
+        let login = {
+          username: ieLogin.username,
+          password: ieLogin.password,
+          hostname: ieLogin.url,
+          timeCreated: ieLogin.creation,
+          };
+        LoginHelper.maybeImportLogin(login);
       } catch (e) {
         Cu.reportError(e);
       }
     }
   },
 
   /**
    * Extract the details of one or more logins from the raw decrypted data.
@@ -360,17 +335,17 @@ IE7FormPasswords.prototype = {
                                               loginItem.ptr);
     // currentLoginData.dataMax is the data count: each username and password is considered as
     // a data. So, the number of logins is the number of data dived by 2
     let numLogins = currentLoginData.dataMax / 2;
     for (let n = 0; n < numLogins; n++) {
       // Bytes 0-31 starting from currentInfoIndex contain the loginItem data structure for the
       // current login
       let currentLoginItem = currentLoginItemPointer.contents;
-      let creation = this.ctypesHelpers.
+      let creation = this.ctypesKernelHelpers.
                      fileTimeToSecondsSinceEpoch(currentLoginItem.hiDateTime,
                                                  currentLoginItem.loDateTime) * 1000;
       let currentResult = {
         creation: creation,
         url: url,
       };
       // The username is UTF-16 and null-terminated.
       currentResult.username =
@@ -526,16 +501,20 @@ IEProfileMigrator.prototype.getResources
   , new History()
   , MSMigrationUtils.getCookiesMigrator()
   , new Settings()
   ];
   // Only support the form password migrator for Windows XP to 7.
   if (AppConstants.isPlatformAndVersionAtMost("win", "6.1")) {
     resources.push(new IE7FormPasswords());
   }
+  let windowsVaultFormPasswordsMigrator =
+    MSMigrationUtils.getWindowsVaultFormPasswordsMigrator();
+  windowsVaultFormPasswordsMigrator.name = "IEVaultFormPasswords";
+  resources.push(windowsVaultFormPasswordsMigrator);
   return [r for each (r in resources) if (r.exists)];
 };
 
 Object.defineProperty(IEProfileMigrator.prototype, "sourceHomePageURL", {
   get: function IE_get_sourceHomePageURL() {
     let defaultStartPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
                                                       kMainKey, "Default_Page_URL");
     let startPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
--- a/browser/components/migration/MSMigrationUtils.jsm
+++ b/browser/components/migration/MSMigrationUtils.jsm
@@ -3,92 +3,116 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["MSMigrationUtils"];
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
+Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource:///modules/MigrationUtils.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/LoginHelper.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
                                   "resource://gre/modules/WindowsRegistry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
                                   "resource://gre/modules/ctypes.jsm");
 
 const EDGE_COOKIE_PATH_OPTIONS = ["", "#!001\\", "#!002\\"];
 const EDGE_COOKIES_SUFFIX = "MicrosoftEdge\\Cookies";
 const EDGE_FAVORITES = "AC\\MicrosoftEdge\\User\\Default\\Favorites";
 const EDGE_READINGLIST = "AC\\MicrosoftEdge\\User\\Default\\DataStore\\Data\\";
+const FREE_CLOSE_FAILED = 0;
+const INTERNET_EXPLORER_EDGE_GUID = [0x3CCD5499,
+                                     0x4B1087A8,
+                                     0x886015A2,
+                                     0x553BDD88];
+const RESULT_SUCCESS = 0;
+const VAULT_ENUMERATE_ALL_ITEMS = 512;
+const WEB_CREDENTIALS_VAULT_ID = [0x4BF4C442,
+                                  0x41A09B8A,
+                                  0x4ADD80B3,
+                                  0x28DB4D70];
 
 Cu.importGlobalProperties(["File"]);
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Helpers.
 
-function CtypesHelpers() {
+const wintypes = {
+  BOOL: ctypes.int,
+  DWORD: ctypes.uint32_t,
+  DWORDLONG: ctypes.uint64_t,
+  CHAR: ctypes.char,
+  PCHAR: ctypes.char.ptr,
+  LPCWSTR: ctypes.char16_t.ptr,
+  PDWORD: ctypes.uint32_t.ptr,
+  VOIDP: ctypes.voidptr_t,
+  WORD: ctypes.uint16_t,
+}
+
+// TODO: Bug 1202978 - Refactor MSMigrationUtils ctypes helpers
+function CtypesKernelHelpers() {
   this._structs = {};
   this._functions = {};
   this._libs = {};
 
-  const WORD = ctypes.uint16_t;
-  const DWORD = ctypes.uint32_t;
-  const BOOL = ctypes.int;
-
-  this._structs.SYSTEMTIME = new ctypes.StructType('SYSTEMTIME', [
-    {wYear: WORD},
-    {wMonth: WORD},
-    {wDayOfWeek: WORD},
-    {wDay: WORD},
-    {wHour: WORD},
-    {wMinute: WORD},
-    {wSecond: WORD},
-    {wMilliseconds: WORD}
+  this._structs.SYSTEMTIME = new ctypes.StructType("SYSTEMTIME", [
+    {wYear: wintypes.WORD},
+    {wMonth: wintypes.WORD},
+    {wDayOfWeek: wintypes.WORD},
+    {wDay: wintypes.WORD},
+    {wHour: wintypes.WORD},
+    {wMinute: wintypes.WORD},
+    {wSecond: wintypes.WORD},
+    {wMilliseconds: wintypes.WORD}
   ]);
 
-  this._structs.FILETIME = new ctypes.StructType('FILETIME', [
-    {dwLowDateTime: DWORD},
-    {dwHighDateTime: DWORD}
+  this._structs.FILETIME = new ctypes.StructType("FILETIME", [
+    {dwLowDateTime: wintypes.DWORD},
+    {dwHighDateTime: wintypes.DWORD}
   ]);
 
   try {
     this._libs.kernel32 = ctypes.open("Kernel32");
+
     this._functions.FileTimeToSystemTime =
       this._libs.kernel32.declare("FileTimeToSystemTime",
                                   ctypes.default_abi,
-                                  BOOL,
+                                  wintypes.BOOL,
                                   this._structs.FILETIME.ptr,
                                   this._structs.SYSTEMTIME.ptr);
   } catch (ex) {
     this.finalize();
   }
 }
 
-CtypesHelpers.prototype = {
+CtypesKernelHelpers.prototype = {
   /**
    * Must be invoked once after last use of any of the provided helpers.
    */
   finalize() {
     this._structs = {};
     this._functions = {};
     for each (let lib in this._libs) {
       try {
         lib.close();
       } catch (ex) {}
     }
     this._libs = {};
   },
 
-  /**
+   /**
    * Converts a FILETIME struct (2 DWORDS), to a SYSTEMTIME struct,
    * and then deduces the number of seconds since the epoch (which
    * is the data we want for the cookie expiry date).
    *
    * @param aTimeHi
    *        Least significant DWORD.
    * @param aTimeLo
    *        Most significant DWORD.
@@ -111,16 +135,145 @@ CtypesHelpers.prototype = {
                                systemTime.wDay,
                                systemTime.wHour,
                                systemTime.wMinute,
                                systemTime.wSecond,
                                systemTime.wMilliseconds) / 1000);
   }
 };
 
+function CtypesVaultHelpers() {
+  this._structs = {};
+  this._functions = {};
+  // the size of the vault handle in 32 bits version is 32 and 64 in 64 bits version
+  if (wintypes.VOIDP.size == 4) {
+    this._vaultHandleType = wintypes.DWORD;
+  } else {
+    this._vaultHandleType = wintypes.DWORDLONG;
+  }
+
+  this._structs.GUID = new ctypes.StructType("GUID", [
+    {id: wintypes.DWORD.array(4)},
+  ]);
+
+  this._structs.VAULT_ITEM_ELEMENT = new ctypes.StructType("VAULT_ITEM_ELEMENT", [
+    // not documented
+    {schemaElementId: wintypes.DWORD},
+    // not documented
+    {unknown1: wintypes.DWORD},
+    // vault type
+    {type: wintypes.DWORD},
+    // not documented
+    {unknown2: wintypes.DWORD},
+    // value of the item
+    {itemValue: wintypes.LPCWSTR},
+    // not documented
+    {unknown3: wintypes.CHAR.array(12)},
+  ]);
+
+  this._structs.VAULT_ELEMENT = new ctypes.StructType("VAULT_ELEMENT", [
+    // vault item schemaId
+    {schemaId: this._structs.GUID},
+    // a pointer to the name of the browser VAULT_ITEM_ELEMENT
+    {pszCredentialFriendlyName: wintypes.LPCWSTR},
+    // a pointer to the url VAULT_ITEM_ELEMENT
+    {pResourceElement: this._structs.VAULT_ITEM_ELEMENT.ptr},
+    // a pointer to the username VAULT_ITEM_ELEMENT
+    {pIdentityElement: this._structs.VAULT_ITEM_ELEMENT.ptr},
+    // not documented
+    {pAuthenticatorElement: this._structs.VAULT_ITEM_ELEMENT.ptr},
+    // not documented
+    {pPackageSid: this._structs.VAULT_ITEM_ELEMENT.ptr},
+    // time stamp in local format
+    {lowLastModified: wintypes.DWORD},
+    {highLastModified: wintypes.DWORD},
+    // not documented
+    {flags: wintypes.DWORD},
+    // not documented
+    {dwPropertiesCount: wintypes.DWORD},
+    // not documented
+    {pPropertyElements: this._structs.VAULT_ITEM_ELEMENT.ptr},
+  ]);
+
+  try {
+    this._vaultcliLib = ctypes.open("vaultcli.dll");
+
+    this._functions.VaultOpenVault =
+      this._vaultcliLib.declare("VaultOpenVault",
+                                ctypes.winapi_abi,
+                                wintypes.DWORD,
+                                // GUID
+                                this._structs.GUID.ptr,
+                                // Flags
+                                wintypes.DWORD,
+                                // Vault Handle
+                                this._vaultHandleType.ptr);
+    this._functions.VaultEnumerateItems =
+      this._vaultcliLib.declare("VaultEnumerateItems",
+                                ctypes.winapi_abi,
+                                wintypes.DWORD,
+                                // Vault Handle
+                                this._vaultHandleType,
+                                // Flags
+                                wintypes.DWORD,
+                                // Items Count
+                                wintypes.PDWORD,
+                                // Items
+                                ctypes.voidptr_t);
+    this._functions.VaultCloseVault =
+      this._vaultcliLib.declare("VaultCloseVault",
+                                ctypes.winapi_abi,
+                                wintypes.DWORD,
+                                // Vault Handle
+                                this._vaultHandleType);
+    this._functions.VaultGetItem =
+      this._vaultcliLib.declare("VaultGetItem",
+                                ctypes.winapi_abi,
+                                wintypes.DWORD,
+                                // Vault Handle
+                                this._vaultHandleType,
+                                // Schema Id
+                                this._structs.GUID.ptr,
+                                // Resource
+                                this._structs.VAULT_ITEM_ELEMENT.ptr,
+                                // Identity
+                                this._structs.VAULT_ITEM_ELEMENT.ptr,
+                                // Package Sid
+                                this._structs.VAULT_ITEM_ELEMENT.ptr,
+                                // HWND Owner
+                                wintypes.DWORD,
+                                // Flags
+                                wintypes.DWORD,
+                                // Items
+                                this._structs.VAULT_ELEMENT.ptr.ptr);
+    this._functions.VaultFree =
+      this._vaultcliLib.declare("VaultFree",
+                                ctypes.winapi_abi,
+                                wintypes.DWORD,
+                                // Memory
+                                this._structs.VAULT_ELEMENT.ptr);
+  } catch (ex) {
+    this.finalize();
+  }
+}
+
+CtypesVaultHelpers.prototype = {
+  /**
+   * Must be invoked once after last use of any of the provided helpers.
+   */
+  finalize() {
+    this._structs = {};
+    this._functions = {};
+    try {
+      this._vaultcliLib.close();
+    } catch (ex) {}
+    this._vaultcliLib = null;
+  }
+}
+
 /**
  * Checks whether an host is an IP (v4 or v6) address.
  *
  * @param aHost
  *        The host to check.
  * @return whether aHost is an IP address.
  */
 function hostIsIPAddress(aHost) {
@@ -157,17 +310,17 @@ function getEdgeLocalDataFolder() {
         gEdgeDir = subDir;
         return subDir.clone();
       }
     }
   } catch (ex) {
     Cu.reportError("Exception trying to find the Edge favorites directory: " + ex);
   }
   return null;
-}
+};
 
 
 function Bookmarks(migrationType) {
   this._migrationType = migrationType;
 }
 
 Bookmarks.prototype = {
   type: MigrationUtils.resourceTypes.BOOKMARKS,
@@ -425,17 +578,17 @@ Cookies.prototype = {
           folders.push(folder);
         }
       }
     }
     return this.__cookiesFolders = folders.length ? folders : null;
   },
 
   migrate(aCallback) {
-    this.ctypesHelpers = new CtypesHelpers();
+    this.ctypesKernelHelpers = new CtypesKernelHelpers();
 
     let cookiesGenerator = (function genCookie() {
       let success = false;
       let folders = this._migrationType == MSMigrationUtils.MIGRATION_TYPE_EDGE ?
                       this.__cookiesFolders : [this.__cookiesFolder];
       for (let folder of folders) {
         let entries = folder.directoryEntries;
         while (entries.hasMoreElements()) {
@@ -452,17 +605,17 @@ Cookies.prototype = {
               cookiesGenerator.next();
             } catch (ex) {}
           });
 
           yield undefined;
         }
       }
 
-      this.ctypesHelpers.finalize();
+      this.ctypesKernelHelpers.finalize();
 
       aCallback(success);
     }).apply(this);
     cookiesGenerator.next();
   },
 
   _readCookieFile(aFile, aCallback) {
     let fileReader = Cc["@mozilla.org/files/filereader;1"].
@@ -528,34 +681,173 @@ Cookies.prototype = {
       if (host.length > 0) {
         // Fist delete any possible extant matching host cookie.
         Services.cookies.remove(host, name, path, false);
         // Now make it a domain cookie.
         if (host[0] != "." && !hostIsIPAddress(host))
           host = "." + host;
       }
 
-      let expireTime = this.ctypesHelpers.fileTimeToSecondsSinceEpoch(Number(expireTimeHi),
+      let expireTime = this.ctypesKernelHelpers.fileTimeToSecondsSinceEpoch(Number(expireTimeHi),
                                                                       Number(expireTimeLo));
       Services.cookies.add(host,
                            path,
                            name,
                            value,
                            Number(flags) & 0x1, // secure
                            false, // httpOnly
                            false, // session
                            expireTime);
     }
   }
 };
 
+// Migrator for form passwords on Windows 8 and higher.
+function WindowsVaultFormPasswords () {
+}
+
+WindowsVaultFormPasswords.prototype = {
+  type: MigrationUtils.resourceTypes.PASSWORDS,
+
+  get exists() {
+    // work only on windows 8+
+    if (AppConstants.isPlatformAndVersionAtLeast("win", "6.2")) {
+      // check if there are passwords available for migration.
+      return this.migrate(() => {}, true);
+    }
+    return false;
+  },
+
+  /**
+   * If aOnlyCheckExists is false, import the form passwords on Windows 8 and higher from the vault
+   * and then call the aCallback.
+   * Otherwise, check if there are passwords in the vault.
+   * @param {function} aCallback - a callback called when the migration is done.
+   * @param {boolean} [aOnlyCheckExists=false] - if aOnlyCheckExists is true, just check if there are some
+   * passwords to migrate. Import the passwords from the vault and call aCallback otherwise.
+   * @return true if there are passwords in the vault and aOnlyCheckExists is set to true,
+   * false if there is no password in the vault and aOnlyCheckExists is set to true, undefined if
+   * aOnlyCheckExists is set to false.
+   */
+  migrate(aCallback, aOnlyCheckExists = false) {
+    // check if the vault item is an IE/Edge one
+    function _isIEOrEdgePassword(id) {
+      return id[0] == INTERNET_EXPLORER_EDGE_GUID[0] &&
+             id[1] == INTERNET_EXPLORER_EDGE_GUID[1] &&
+             id[2] == INTERNET_EXPLORER_EDGE_GUID[2] &&
+             id[3] == INTERNET_EXPLORER_EDGE_GUID[3];
+    }
+
+    let ctypesVaultHelpers = new CtypesVaultHelpers();
+    let ctypesKernelHelpers = new CtypesKernelHelpers();
+    let migrationSucceeded = true;
+    let successfulVaultOpen = false;
+    let error, vault;
+    try {
+
+      // web credentials vault id
+      let vaultGuid = new ctypesVaultHelpers._structs.GUID(WEB_CREDENTIALS_VAULT_ID);
+      // number of available vaults
+      let vaultCount = new wintypes.DWORD;
+      error = new wintypes.DWORD;
+      // web credentials vault
+      vault = new ctypesVaultHelpers._vaultHandleType;
+      // open the current vault using the vaultGuid
+      error = ctypesVaultHelpers._functions.VaultOpenVault(vaultGuid.address(), 0, vault.address());
+      if (error != RESULT_SUCCESS) {
+        throw new Error("Unable to open Vault: " + error);
+      }
+      successfulVaultOpen = true;
+
+      let item = new ctypesVaultHelpers._structs.VAULT_ELEMENT.ptr;
+      let itemCount = new wintypes.DWORD;
+      // enumerate all the available items. This api is going to return a table of all the
+      // available items and item is going to point to the first element of this table.
+      error = ctypesVaultHelpers._functions.VaultEnumerateItems(vault, VAULT_ENUMERATE_ALL_ITEMS,
+                                                                itemCount.address(),
+                                                                item.address());
+      if (error != RESULT_SUCCESS) {
+        throw new Error("Unable to enumerate Vault items: " + error);
+      }
+      for (let j = 0; j < itemCount.value; j++) {
+        try {
+          // if it's not an ie/edge password, skip it
+          if (!_isIEOrEdgePassword(item.contents.schemaId.id)) {
+            continue;
+          }
+          // if aOnlyCheckExists is set to true, the purpose of the call is to return true if there is at
+          // least a password which is true in this case because a password was by now already found
+          if (aOnlyCheckExists) {
+            return true;
+          }
+          let url = item.contents.pResourceElement.contents.itemValue.readString();
+          let username = item.contents.pIdentityElement.contents.itemValue.readString();
+          // the current login credential object
+          let credential = new ctypesVaultHelpers._structs.VAULT_ELEMENT.ptr;
+          error = ctypesVaultHelpers._functions.VaultGetItem(vault,
+                                                             item.contents.schemaId.address(),
+                                                             item.contents.pResourceElement,
+                                                             item.contents.pIdentityElement, null,
+                                                             0, 0, credential.address());
+          if (error != RESULT_SUCCESS) {
+            throw new Error("Unable to get item: " + error);
+          }
+
+          let password = credential.contents.pAuthenticatorElement.contents.itemValue.readString();
+          let creation = ctypesKernelHelpers.
+                         fileTimeToSecondsSinceEpoch(item.contents.highLastModified,
+                                                     item.contents.lowLastModified) * 1000;
+          // create a new login
+          let login = {
+            username, password,
+            hostname: NetUtil.newURI(url).prePath,
+            timeCreated: creation,
+          };
+          LoginHelper.maybeImportLogin(login);
+
+          // close current item
+          error = ctypesVaultHelpers._functions.VaultFree(credential);
+          if (error == FREE_CLOSE_FAILED) {
+            throw new Error("Unable to free item: " + error);
+          }
+        } catch (e) {
+          migrationSucceeded = false;
+          Cu.reportError(e);
+        } finally {
+          // move to next item in the table returned by VaultEnumerateItems
+          item = item.increment();
+        }
+      }
+    } catch (e) {
+      Cu.reportError(e);
+      migrationSucceeded = false;
+    } finally {
+      if (successfulVaultOpen) {
+        // close current vault
+        error = ctypesVaultHelpers._functions.VaultCloseVault(vault);
+        if (error == FREE_CLOSE_FAILED) {
+          Cu.reportError("Unable to close vault: " + error);
+        }
+      }
+      ctypesKernelHelpers.finalize();
+      ctypesVaultHelpers.finalize();
+      aCallback(migrationSucceeded);
+    }
+    if (aOnlyCheckExists) {
+      return false;
+    }
+  }
+};
 
 var MSMigrationUtils = {
   MIGRATION_TYPE_IE: 1,
   MIGRATION_TYPE_EDGE: 2,
-  CtypesHelpers: CtypesHelpers,
+  CtypesKernelHelpers: CtypesKernelHelpers,
   getBookmarksMigrator(migrationType = this.MIGRATION_TYPE_IE) {
     return new Bookmarks(migrationType);
   },
   getCookiesMigrator(migrationType = this.MIGRATION_TYPE_IE) {
     return new Cookies(migrationType);
   },
+  getWindowsVaultFormPasswordsMigrator() {
+    return new WindowsVaultFormPasswords();
+  },
 };
--- a/browser/components/migration/tests/unit/test_Edge_availability.js
+++ b/browser/components/migration/tests/unit/test_Edge_availability.js
@@ -1,11 +1,12 @@
 const EDGE_AVAILABLE_MIGRATIONS = 
   MigrationUtils.resourceTypes.COOKIES |
-  MigrationUtils.resourceTypes.BOOKMARKS;
+  MigrationUtils.resourceTypes.BOOKMARKS |
+  MigrationUtils.resourceTypes.PASSWORDS;
 
 add_task(function* () {
   let migrator = MigrationUtils.getMigrator("edge");
   Cu.import("resource://gre/modules/AppConstants.jsm");
   Assert.equal(!!(migrator && migrator.sourceExists), AppConstants.isPlatformAndVersionAtLeast("win", "10"),
                "Edge should be available for migration if and only if we're on Win 10+");
   if (migrator) {
     let migratableData = migrator.getMigrateData(null, false);
--- a/browser/components/migration/tests/unit/test_IE7_passwords.js
+++ b/browser/components/migration/tests/unit/test_IE7_passwords.js
@@ -2,16 +2,17 @@ Cu.import("resource://gre/modules/AppCon
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
                                   "resource://gre/modules/WindowsRegistry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto",
                                   "resource://gre/modules/OSCrypto.jsm");
 
 const CRYPT_PROTECT_UI_FORBIDDEN = 1;
+const IE7_FORM_PASSWORDS_MIGRATOR_NAME = "IE7FormPasswords";
 const LOGINS_KEY =  "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2";
 const EXTENSION = "-backup";
 const TESTED_WEBSITES = {
   twitter: {
     uri: makeURI("https://twitter.com"),
     hash: "A89D42BC6406E27265B1AD0782B6F376375764A301",
     data: [12, 0, 0, 0, 56, 0, 0, 0, 38, 0, 0, 0, 87, 73, 67, 75, 24, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 36, 67, 124, 118, 212, 208, 1, 8, 0, 0, 0, 18, 0, 0, 0, 68, 36, 67, 124, 118, 212, 208, 1, 9, 0, 0, 0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, 0, 0, 49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 55, 0, 56, 0, 57, 0, 0, 0],
     logins: [
@@ -268,17 +269,17 @@ function createRegistryPath(path) {
 }
 
 function getFirstResourceOfType(type) {
   let migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=ie"]
                  .createInstance(Ci.nsISupports)
                  .wrappedJSObject;
   let migrators = migrator.getResources();
   for (let m of migrators) {
-    if (m.type == type) {
+    if (m.name == IE7_FORM_PASSWORDS_MIGRATOR_NAME && m.type == type) {
       return m;
     }
   }
   throw new Error("failed to find the " + type + " migrator");
 }
 
 function makeURI(aURL) {
   return Services.io.newURI(aURL, null, null);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2798,17 +2798,17 @@ var DefaultBrowserCheck = {
       ShellService.setDefaultBrowser(claimAllTypes, false);
 
       if (this._setAsDefaultTimer) {
         this._setAsDefaultTimer.cancel();
       }
 
       this._setAsDefaultButtonClickStartTime = Math.floor(Date.now() / 1000);
       this._setAsDefaultTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-      this._setAsDefaultTimer.init(function() {
+      this._setAsDefaultTimer.init(() => {
         let isDefault = false;
         let isDefaultError = false;
         try {
           isDefault = ShellService.isDefaultBrowser(true, false);
         } catch (ex) {
           isDefaultError = true;
         }
 
--- a/browser/components/preferences/in-content/search.js
+++ b/browser/components/preferences/in-content/search.js
@@ -56,25 +56,45 @@ var gSearchPane = {
 
     Services.obs.addObserver(this, "browser-search-engine-modified", false);
     window.addEventListener("unload", () => {
       Services.obs.removeObserver(this, "browser-search-engine-modified", false);
     });
 
     this._initAutocomplete();
 
+    let suggestsPref =
+      document.getElementById("browser.search.suggest.enabled");
+    suggestsPref.addEventListener("change", () => {
+      this.updateSuggestsCheckbox();
+    });
+    this.updateSuggestsCheckbox();
+  },
+
+  updateSuggestsCheckbox() {
     let urlbarSuggests = document.getElementById("urlBarSuggestion");
-    urlbarSuggests.hidden = !Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
+    urlbarSuggests.hidden =
+      !Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
 
-    let suggestsPref = document.getElementById("browser.search.suggest.enabled")
-    let updateSuggestsCheckbox = () => {
-      urlbarSuggests.disabled = !suggestsPref.value;
+    let suggestsPref =
+      document.getElementById("browser.search.suggest.enabled");
+    let permanentPB =
+      Services.prefs.getBoolPref("browser.privatebrowsing.autostart");
+    urlbarSuggests.disabled = !suggestsPref.value || permanentPB;
+
+    let urlbarSuggestsPref =
+      document.getElementById("browser.urlbar.suggest.searches");
+    urlbarSuggests.checked = urlbarSuggestsPref.value;
+    if (urlbarSuggests.disabled) {
+      urlbarSuggests.checked = false;
     }
-    suggestsPref.addEventListener("change", updateSuggestsCheckbox);
-    updateSuggestsCheckbox();
+
+    let permanentPBLabel =
+      document.getElementById("urlBarSuggestionPermanentPBLabel");
+    permanentPBLabel.hidden = urlbarSuggests.hidden || !permanentPB;
   },
 
   buildDefaultEngineDropDown: function() {
     // This is called each time something affects the list of engines.
     let list = document.getElementById("defaultEngine");
     let currentEngine;
 
     // First, try to preserve the current selection.
--- a/browser/components/preferences/in-content/search.xul
+++ b/browser/components/preferences/in-content/search.xul
@@ -36,22 +36,25 @@
       <label>&chooseYourDefaultSearchEngine.label;</label>
       <menulist id="defaultEngine">
         <menupopup/>
       </menulist>
       <checkbox id="suggestionsInSearchFieldsCheckbox"
                 label="&provideSearchSuggestions.label;"
                 accesskey="&provideSearchSuggestions.accesskey;"
                 preference="browser.search.suggest.enabled"/>
-      <hbox class="indent">
+      <vbox class="indent">
         <checkbox id="urlBarSuggestion" label="&showURLBarSuggestions.label;"
-                  hidden="true"
                   accesskey="&showURLBarSuggestions.accesskey;"
                   preference="browser.urlbar.suggest.searches"/>
-      </hbox>
+        <hbox id="urlBarSuggestionPermanentPBLabel"
+              align="center" class="indent">
+          <label flex="1">&urlBarSuggestionsPermanentPB.label;</label>
+        </hbox>
+      </vbox>
       <checkbox id="redirectSearchCheckbox"
                 label="&redirectWindowsSearch.label;"
                 accesskey="&redirectWindowsSearch.accesskey;"
                 preference="browser.search.redirectWindowsSearch"/>
     </groupbox>
 
     <groupbox id="oneClickSearchProvidersGroup" data-category="paneSearch">
       <caption label="&oneClickSearchEngines.label;"/>
--- a/browser/locales/en-US/chrome/browser/preferences/search.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/search.dtd
@@ -6,16 +6,17 @@
 
 <!ENTITY chooseYourDefaultSearchEngine.label   "Choose your default search engine. &brandShortName; uses it in the location bar, search bar, and start page.">
 
 <!ENTITY provideSearchSuggestions.label        "Provide search suggestions">
 <!ENTITY provideSearchSuggestions.accesskey    "s">
 
 <!ENTITY showURLBarSuggestions.label           "Show search suggestions in location bar results">
 <!ENTITY showURLBarSuggestions.accesskey       "l">
+<!ENTITY urlBarSuggestionsPermanentPB.label    "Search suggestions will not be shown in location bar results because you have configured &brandShortName; to never remember history.">
 
 <!ENTITY redirectWindowsSearch.label "Use this search engine for searches from Windows">
 <!ENTITY redirectWindowsSearch.accesskey "W">
 
 <!ENTITY oneClickSearchEngines.label           "One-click search engines">
 
 <!ENTITY chooseWhichOneToDisplay.label         "The search bar lets you search alternate engines directly. Choose which ones to display.">
 
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -13,32 +13,34 @@
 #include "mozilla/dom/StructuredCloneHelper.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/Maybe.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDocument.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsGlobalWindow.h"
 #include "nsJSUtils.h"
+#include "nsNetUtil.h"
 #include "nsPerformance.h"
 #include "ScriptSettings.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "xpcprivate.h"
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsProxyRelease.h"
 #include "mozilla/ConsoleTimelineMarker.h"
 #include "mozilla/TimestampTimelineMarker.h"
 
 #include "nsIConsoleAPIStorage.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILoadContext.h"
 #include "nsIProgrammingLanguage.h"
+#include "nsISensitiveInfoHiddenURI.h"
 #include "nsIServiceManager.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIWebNavigation.h"
 #include "nsIXPConnect.h"
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
 #include "nsIProfiler.h"
 #endif
@@ -1205,16 +1207,29 @@ Console::ProcessCallData(ConsoleCallData
   } else {
     MOZ_ASSERT(aData->mIDType == ConsoleCallData::eNumber);
     event.mID.Value().SetAsUnsignedLongLong() = aData->mOuterIDNumber;
     event.mInnerID.Value().SetAsUnsignedLongLong() = aData->mInnerIDNumber;
   }
 
   event.mLevel = aData->mMethodString;
   event.mFilename = frame.mFilename;
+
+  nsCOMPtr<nsIURI> filenameURI;
+  nsAutoCString pass;
+  if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(filenameURI), frame.mFilename)) &&
+      NS_SUCCEEDED(filenameURI->GetPassword(pass)) && !pass.IsEmpty()) {
+    nsCOMPtr<nsISensitiveInfoHiddenURI> safeURI = do_QueryInterface(filenameURI);
+    nsAutoCString spec;
+    if (safeURI &&
+        NS_SUCCEEDED(safeURI->GetSensitiveInfoHiddenSpec(spec))) {
+      CopyUTF8toUTF16(spec, event.mFilename);
+    }
+  }
+
   event.mLineNumber = frame.mLineNumber;
   event.mColumnNumber = frame.mColumnNumber;
   event.mFunctionName = frame.mFunctionName;
   event.mTimeStamp = aData->mTimeStamp;
   event.mPrivate = aData->mPrivate;
 
   switch (aData->mMethodName) {
     case MethodLog:
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -6544,25 +6544,24 @@ var IdentityHandler = {
                                                   (this._lastLocation.port || 443),
                                                   iData.cert, {}, {}))
       result.verifier = Strings.browser.GetStringFromName("identity.identified.verified_by_you");
 
     return result;
   },
 
   /**
-   * Return the eTLD+1 version of the current hostname
+   * Attempt to provide proper IDN treatment for host names
    */
   getEffectiveHost: function getEffectiveHost() {
     if (!this._IDNService)
       this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
                          .getService(Ci.nsIIDNService);
     try {
-      let baseDomain = Services.eTLD.getBaseDomainFromHost(this._lastLocation.hostname);
-      return this._IDNService.convertToDisplayIDN(baseDomain, {});
+      return this._IDNService.convertToDisplayIDN(this._uri.host, {});
     } catch (e) {
       // If something goes wrong (e.g. hostname is an IP address) just fail back
       // to the full domain.
       return this._lastLocation.hostname;
     }
   }
 };
 
--- a/toolkit/components/passwordmgr/LoginHelper.jsm
+++ b/toolkit/components/passwordmgr/LoginHelper.jsm
@@ -337,9 +337,69 @@ this.LoginHelper = {
         fieldType == "email" ||
         fieldType == "url"   ||
         fieldType == "tel"   ||
         fieldType == "number") {
       return true;
     }
     return false;
   },
+
+  /**
+   * Add the login to the password manager if a similar one doesn't already exist. Merge it
+   * otherwise with the similar existing ones.
+   * @param {Object} loginData - the data about the login that needs to be added.
+   */
+  maybeImportLogin(loginData) {
+    // create a new login
+    let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo);
+    login.init(loginData.hostname,
+               loginData.submitURL || (typeof(loginData.httpRealm) == "string" ? null : ""),
+               typeof(loginData.httpRealm) == "string" ? loginData.httpRealm : null,
+               loginData.username,
+               loginData.password,
+               loginData.usernameElement || "",
+               loginData.passwordElement || "");
+
+    login.QueryInterface(Ci.nsILoginMetaInfo);
+    login.timeCreated = loginData.timeCreated;
+    login.timeLastUsed = loginData.timeLastUsed || loginData.timeCreated;
+    login.timePasswordChanged = loginData.timePasswordChanged  || loginData.timeCreated;
+    login.timesUsed = loginData.timesUsed || 1;
+    // While here we're passing formSubmitURL and httpRealm, they could be empty/null and get
+    // ignored in that case, leading to multiple logins for the same username.
+    let existingLogins = Services.logins.findLogins({}, login.hostname,
+                                                    login.formSubmitURL,
+                                                    login.httpRealm);
+    // Add the login only if it doesn't already exist
+    // if the login is not already available, it's going to be added or merged with other
+    // logins
+    if (existingLogins.some(l => login.matches(l, true))) {
+      return;
+    }
+    // the login is just an update for an old one or the login is older than an existing one
+    let foundMatchingLogin = false;
+    for (let existingLogin of existingLogins) {
+      if (login.username == existingLogin.username) {
+        // Bug 1187190: Password changes should be propagated depending on timestamps.
+        // this an old login or a just an update, so make sure not to add it
+        foundMatchingLogin = true;
+        if(login.password != existingLogin.password &
+           login.timePasswordChanged > existingLogin.timePasswordChanged) {
+          // if a login with the same username and different password already exists and it's older
+          // than the current one, that login needs to be updated using the current one details
+
+          // the existing login password and timestamps should be updated
+          let propBag = Cc["@mozilla.org/hash-property-bag;1"].
+                        createInstance(Ci.nsIWritablePropertyBag);
+          propBag.setProperty("password", login.password);
+          propBag.setProperty("timePasswordChanged", login.timePasswordChanged);
+          Services.logins.modifyLogin(existingLogin, propBag);
+        }
+      }
+    }
+    // if the new login is an update or is older than an exiting login, don't add it.
+    if (foundMatchingLogin) {
+      return;
+    }
+    Services.logins.addLogin(login);
+  }
 };
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -7385,16 +7385,22 @@
   "BROWSER_SET_DEFAULT_DIALOG_PROMPT_RAWCOUNT": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "250",
     "n_buckets": "15",
     "releaseChannelCollection": "opt-out",
     "description": "The number of times that a profile has seen the 'Set Default Browser' dialog."
   },
+  "BROWSER_SET_DEFAULT_ALWAYS_CHECK": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "releaseChannelCollection": "opt-out",
+    "description": "True if the profile has `browser.shell.checkDefaultBrowser` set to true."
+  },
   "BROWSER_SET_DEFAULT_RESULT": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": "4",
     "releaseChannelCollection": "opt-out",
     "description": "Result of the Set Default Browser dialog (0=Use Firefox + 'Always perform check' unchecked, 1=Use Firefox + 'Always perform check' checked, 2=Not Now + 'Always perform check' unchecked, 3=Not Now + 'Always perform check' checked)"
   },
   "BROWSER_SET_DEFAULT_ERROR": {