Bug 1200472 - Broaden search criteria to include subdomains in context menu. r=MattN draft
authorSaad Quadri <saad@saadquadri.com>
Tue, 23 Aug 2016 15:26:39 -0700
changeset 404658 1643a50ca69492469956a4b25078edb3510052bd
parent 399190 2f24daceada71e15c03b5f90765264c720ed035a
child 404659 4cf47eab22d7f39e5a5612264d664f9458b5fcbc
push id27268
push usersaad@saadquadri.com
push dateTue, 23 Aug 2016 22:28:18 +0000
reviewersMattN
bugs1200472
milestone51.0a1
Bug 1200472 - Broaden search criteria to include subdomains in context menu. r=MattN MozReview-Commit-ID: 7x1HZXUF9Ul
toolkit/components/passwordmgr/LoginHelper.jsm
toolkit/components/passwordmgr/LoginManagerContextMenu.jsm
toolkit/components/passwordmgr/storage-json.js
toolkit/components/passwordmgr/test/unit/test_context_menu.js
--- a/toolkit/components/passwordmgr/LoginHelper.jsm
+++ b/toolkit/components/passwordmgr/LoginHelper.jsm
@@ -175,37 +175,60 @@ this.LoginHelper = {
    *                                 from a form or page that we are considering.
    * @param {nsILoginFindOptions} aOptions - Options to affect whether the origin
    *                                         from the login (aLoginOrigin) is a
    *                                         match for the origin we're looking
    *                                         for (aSearchOrigin).
    */
   isOriginMatching(aLoginOrigin, aSearchOrigin, aOptions = {
     schemeUpgrades: false,
+    includeETLDPlusOne: false
   }) {
     if (aLoginOrigin == aSearchOrigin) {
       return true;
     }
 
-    if (!aOptions) {
+    let { schemeUpgrades, includeETLDPlusOne } = aOptions;
+
+    if (!schemeUpgrades && !includeETLDPlusOne) {
       return false;
     }
 
-    if (aOptions.schemeUpgrades) {
-      try {
-        let loginURI = Services.io.newURI(aLoginOrigin, null, null);
-        let searchURI = Services.io.newURI(aSearchOrigin, null, null);
-        if (loginURI.scheme == "http" && searchURI.scheme == "https" &&
-            loginURI.hostPort == searchURI.hostPort) {
+    try {
+      let loginURI = Services.io.newURI(aLoginOrigin, null, null);
+      let searchURI = Services.io.newURI(aSearchOrigin, null, null);
+      let loginBase, searchBase;
+
+      if (includeETLDPlusOne) {
+        let eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"]
+                     .getService(Ci.nsIEffectiveTLDService);
+        loginBase = eTLDService.getBaseDomain(loginURI);
+        searchBase = eTLDService.getBaseDomain(searchURI);
+      }
+
+      let hostPortMatches = () => {
+        return includeETLDPlusOne
+          ? loginURI.port == searchURI.port && loginBase == searchBase
+          : loginURI.hostPort == searchURI.hostPort;
+      };
+
+      let schemeMatches = () => {
+        if (loginURI.scheme == searchURI.scheme) {
           return true;
         }
-      } catch (ex) {
-        // newURI will throw for some values e.g. chrome://FirefoxAccounts
-        return false;
+
+        return schemeUpgrades && loginURI.scheme == "http" && searchURI.scheme == "https";
+      };
+
+      if (hostPortMatches() && schemeMatches()) {
+        return true;
       }
+    } catch (ex) {
+      // newURI will throw for some values e.g. chrome://FirefoxAccounts
+      return false;
     }
 
     return false;
   },
 
   doLoginsMatch(aLogin1, aLogin2, {
     ignorePassword = false,
     ignoreSchemes = false,
--- a/toolkit/components/passwordmgr/LoginManagerContextMenu.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContextMenu.jsm
@@ -93,16 +93,17 @@ var LoginManagerContextMenu = {
    *        when subframes are involved.
    *
    * @returns {nsILoginInfo[]} a login list
    */
   _findLogins(documentURI) {
     let searchParams = {
       hostname: documentURI.prePath,
       schemeUpgrades: LoginHelper.schemeUpgrades,
+      includeETLDPlusOne: true
     };
     let logins = LoginHelper.searchLoginsWithObject(searchParams);
     let resolveBy = [
       "scheme",
       "timePasswordChanged",
     ];
     logins = LoginHelper.dedupeLogins(logins, ["username", "password"], resolveBy, documentURI.prePath);
 
--- a/toolkit/components/passwordmgr/storage-json.js
+++ b/toolkit/components/passwordmgr/storage-json.js
@@ -246,16 +246,17 @@ this.LoginManagerStorage_json.prototype 
     let realMatchData = {};
     let options = {};
     // Convert nsIPropertyBag to normal JS object
     let propEnum = matchData.enumerator;
     while (propEnum.hasMoreElements()) {
       let prop = propEnum.getNext().QueryInterface(Ci.nsIProperty);
       switch (prop.name) {
         // Some property names aren't field names but are special options to affect the search.
+        case "includeETLDPlusOne":
         case "schemeUpgrades": {
           options[prop.name] = prop.value;
           break;
         }
         default: {
           realMatchData[prop.name] = prop.value;
           break;
         }
@@ -276,16 +277,17 @@ this.LoginManagerStorage_json.prototype 
    * left to the caller.
    *
    * Returns [logins, ids] for logins that match the arguments, where logins
    * is an array of encrypted nsLoginInfo and ids is an array of associated
    * ids in the database.
    */
   _searchLogins(matchData, aOptions = {
     schemeUpgrades: false,
+    includeETLDPlusOne: false
   }) {
     this._store.ensureDataReady();
 
     let conditions = [];
 
     function match(aLogin) {
       for (let field in matchData) {
         let wantedValue = matchData[field];
--- a/toolkit/components/passwordmgr/test/unit/test_context_menu.js
+++ b/toolkit/components/passwordmgr/test/unit/test_context_menu.js
@@ -1,16 +1,17 @@
 /*
  * Test the password manager context menu.
  */
 
 "use strict";
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/LoginManagerContextMenu.jsm");
+Cu.import("resource://gre/modules/LoginHelper.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "_stringBundle", function() {
   return Services.strings.
          createBundle("chrome://passwordmgr/locale/passwordmgr.properties");
 });
 
 /**
  * Prepare data for the following tests.
@@ -127,17 +128,25 @@ function checkLoginItems(logins, items) 
   }
   return true;
 }
 
 /**
  * Gets the list of expected logins for a hostname.
  */
 function getExpectedLogins(hostname) {
-  return Services.logins.getAllLogins().filter(entry => entry["hostname"] === hostname);
+  let uri = Services.io.newURI(hostname, null, null);
+
+  let searchParams = {
+    hostname: uri.prePath,
+    schemeUpgrades: LoginHelper.schemeUpgrades,
+    includeETLDPlusOne: true
+  };
+
+  return LoginHelper.searchLoginsWithObject(searchParams);
 }
 
 function loginList() {
   return [
       new LoginInfo("http://www.example.com", "http://www.example.com", null,
                     "username1", "password",
                     "form_field_username", "form_field_password"),