bug 665826 - Make Data Manager safe for IPv6 (and more tolerant against other errors), r+a=IanN
authorRobert Kaiser <kairo@kairo.at>
Sat, 30 Jul 2011 22:26:16 +0200
changeset 8322 8965e28225cc94daf2bb74646241d90a879f8888
parent 8321 e8172bb6b4f1427f53967292287d889706283994
child 8323 56aa3a1b61e2acd38d964655eeaa14a0faee646b
push id84
push userbugzilla@standard8.plus.com
push dateTue, 16 Aug 2011 21:25:04 +0000
treeherdercomm-beta@6970c86be3cd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs665826
bug 665826 - Make Data Manager safe for IPv6 (and more tolerant against other errors), r+a=IanN
suite/common/dataman/dataman.js
suite/common/dataman/tests/browser_dataman_basics.js
--- a/suite/common/dataman/dataman.js
+++ b/suite/common/dataman/dataman.js
@@ -437,33 +437,52 @@ var gDomains = {
 
   getDomainFromHost: function domain_getDomainFromHost(aHostname) {
     // Find the base domain name for the given host name.
     if (!this.xlcache[aHostname]) {
       // aHostname is not always an actual host name, but potentially something
       // URI-like, e.g. gopher://example.com and newURI doesn't work there as we
       // need to display entries for schemes that are not supported (any more).
       // nsIURLParser is a fast way to generically ensure a pure host name.
+      var hostName;
       // Return vars for nsIURLParser must all be objects,
       // see bug 568997 for improvements to that interface.
       var schemePos = {}, schemeLen = {}, authPos = {}, authLen = {}, pathPos = {},
           pathLen = {}, usernamePos = {}, usernameLen = {}, passwordPos = {},
           passwordLen = {}, hostnamePos = {}, hostnameLen = {}, port = {};
-      gLocSvc.url.parseURL(aHostname, -1, schemePos, schemeLen, authPos, authLen,
-                          pathPos, pathLen);
-      var auth = aHostname.substring(authPos.value, authPos.value + authLen.value);
-      gLocSvc.url.parseAuthority(auth, authLen.value, usernamePos, usernameLen,
-                                passwordPos, passwordLen, hostnamePos, hostnameLen, port);
-      var hostName = auth.substring(hostnamePos.value, hostnamePos.value + hostnameLen.value);
+      try {
+        gLocSvc.url.parseURL(aHostname, -1, schemePos, schemeLen, authPos, authLen,
+                             pathPos, pathLen);
+        var auth = aHostname.substring(authPos.value, authPos.value + authLen.value);
+        gLocSvc.url.parseAuthority(auth, authLen.value, usernamePos, usernameLen,
+                                   passwordPos, passwordLen, hostnamePos, hostnameLen, port);
+        hostName = auth.substring(hostnamePos.value, hostnamePos.value + hostnameLen.value);
+      }
+      catch (e) {
+        // IPv6 host names can come in without [] around them and therefore
+        // cause an error. Those consist of at least two colons and else only
+        // hexadecimal digits. Fix them by putting [] around them.
+        if (/^[a-f0-9]*:[a-f0-9]*:[a-f0-9:]*$/.test(aHostname)) {
+          gDataman.debugMsg("bare IPv6 address found: " + aHostname);
+          hostName = "[" + aHostname + "]";
+        }
+        else {
+          gDataman.debugError("Error while trying to get hostname from input: " + aHostname);
+          gDataman.debugError(e);
+          hostName = aHostname;
+        }
+      }
 
       var domain;
       try {
         domain = Services.eTLD.getBaseDomainFromHost(hostName);
       }
       catch (e) {
+        gDataman.debugError("Error while trying to get domain from host name: " + hostName);
+        gDataman.debugError(e);
         domain = hostName;
       }
       this.xlcache[aHostname] = domain;
       gDataman.debugMsg("cached: " + aHostname + " -> " + this.xlcache[aHostname]);
     }
     return this.xlcache[aHostname];
   },
 
@@ -1131,19 +1150,21 @@ var gCookies = {
         }
       }
     }
   },
 
   forget: function cookies_forget() {
     for (let i = 0; i < this.cookies.length; i++) {
       if (gDomains.hostMatchesSelected(this.cookies[i].rawHost)) {
-        Services.cookies.remove(this.cookies[i].host, this.cookies[i].name,
-                                this.cookies[i].path, false);
+        // Remove from internal list needs to be before actually deleting.
+        let delCookie = this.cookies[i];
         this.cookies.splice(i, 1);
+        Services.cookies.remove(delCookie.host, delCookie.name,
+                                delCookie.path, false);
       }
     }
     gDomains.removeDomainOrFlag(gDomains.selectedDomain.title, "hasCookies");
   },
 
   // nsITreeView
   __proto__: gBaseTreeView,
   get rowCount() {
@@ -2100,18 +2121,20 @@ var gPasswords = {
         }
       }
     }
   },
 
   forget: function passwords_forget() {
     for (let i = 0; i < this.allSignons.length; i++) {
       if (gDomains.hostMatchesSelected(this.allSignons[i].hostname)) {
-        Services.logins.removeLogin(this.allSignons[i]);
+        // Remove from internal list needs to be before actually deleting.
+        let delSignon = this.allSignons[i];
         this.allSignons.splice(i, 1);
+        Services.logins.removeLogin(delSignon);
       }
     }
     gDomains.removeDomainOrFlag(gDomains.selectedDomain.title, "hasPasswords");
   },
 
   // nsITreeView
   __proto__: gBaseTreeView,
   get rowCount() {
--- a/suite/common/dataman/tests/browser_dataman_basics.js
+++ b/suite/common/dataman/tests/browser_dataman_basics.js
@@ -40,16 +40,20 @@ function test() {
                        false, true, true, now_epoch + 600);
   // Add cookie: secure, HTTPOnly, session
   Services.cookies.add("secure.geckoisgecko.org", "", "name2", "value2",
                        true, true, true, now_epoch + 600);
   // Add cookie: secure, non-HTTPOnly, expiry in an hour
   Services.cookies.add("drumbeat.org", "", "name3", "value3",
                        true, false, false, now_epoch + 3600);
 
+  // Add a cookie for a pure IPv6 address.
+  Services.cookies.add("::1", "", "name4", "value4",
+                       false, false, true, now_epoch + 600);
+
   // Add a few form history entries
   gLocSvc.fhist.addEntry("akey", "value0");
   gLocSvc.fhist.addEntry("ekey", "value1");
   gLocSvc.fhist.addEntry("ekey", "value2");
   gLocSvc.fhist.addEntry("bkey", "value3");
   gLocSvc.fhist.addEntry("bkey", "value4");
   gLocSvc.fhist.addEntry("ckey", "value5");
 
@@ -59,16 +63,21 @@ function test() {
   loginInfo1.init("http://www.geckoisgecko.org", "http://www.geckoisgecko.org", null,
                   "dataman", "mysecret", "user", "pwd");
   Services.logins.addLogin(loginInfo1);
   let loginInfo2 = Components.classes["@mozilla.org/login-manager/loginInfo;1"]
                              .createInstance(Components.interfaces.nsILoginInfo);
   loginInfo2.init("gopher://geckoisgecko.org:4711", null, "foo",
                   "dataman", "mysecret", "", "");
   Services.logins.addLogin(loginInfo2);
+  let loginInfo3 = Components.classes["@mozilla.org/login-manager/loginInfo;1"]
+                             .createInstance(Components.interfaces.nsILoginInfo);
+  loginInfo3.init("https://[::1]", null, "foo",
+                  "dataman", "mysecret", "", "");
+  Services.logins.addLogin(loginInfo3);
 
   //Services.prefs.setBoolPref("data_manager.debug", true);
 
   gBrowser.addTab();
   // Open the Data Manager, testing the menu item.
   document.getElementById("tasksDataman").click();
 
   var testIndex = 0;
@@ -84,37 +93,37 @@ function test() {
         Services.obs.addObserver(testObs, TEST_DONE, false);
         // Trigger the first test now!
         Services.obs.notifyObservers(window, TEST_DONE, null);
       }
       else {
         // TEST_DONE triggered, run next test
         info("run test #" + (testIndex + 1) + " of " + testFuncs.length +
              " (" + testFuncs[testIndex].name + ")");
-        testFuncs[testIndex++](win);
+        setTimeout(testFuncs[testIndex++], 0, win);
 
         if (testIndex >= testFuncs.length) {
           // Finish this up!
           Services.obs.removeObserver(testObs, TEST_DONE);
           Services.cookies.removeAll();
           gLocSvc.fhist.removeAllEntries();
-          finish();
+          setTimeout(finish, 0);
         }
       }
     }
   };
   waitForExplicitFinish();
   Services.obs.addObserver(testObs, DATAMAN_LOADED, false);
 }
 
 var testFuncs = [
 function test_open_state(aWin) {
   is(aWin.document.documentElement.id, "dataman-page",
      "The active tab is the Data Manager");
-  is(aWin.gDomains.tree.view.rowCount, gPreexistingDomains + 5,
+  is(aWin.gDomains.tree.view.rowCount, gPreexistingDomains + 6,
      "The correct number of domains is listed");
   is(aWin.gTabs.activePanel, "formdataPanel",
      "Form data panel is selected");
 
   aWin.document.getElementById("domainSearch").value = "mo";
   aWin.document.getElementById("domainSearch").doCommand();
   is(aWin.gDomains.tree.view.selection.count, 0,
      "In search, non-matching selection is lost");
@@ -123,22 +132,47 @@ function test_open_state(aWin) {
   is(aWin.gDomains.displayedDomains.map(function(aDom) { return aDom.title; })
                                    .join(","),
      "mochi.test,mozilla.com,mozilla.org",
      "In search, the correct domains are listed");
 
   aWin.gDomains.tree.view.selection.select(0);
   aWin.document.getElementById("domainSearch").value = "";
   aWin.document.getElementById("domainSearch").doCommand();
-  is(aWin.gDomains.tree.view.rowCount, gPreexistingDomains + 5,
+  is(aWin.gDomains.tree.view.rowCount, gPreexistingDomains + 6,
      "After search, the correct number of domains is listed");
   is(aWin.gDomains.tree.view.selection.count, 1,
      "After search, number of selections is correct");
   is(aWin.gDomains.selectedDomain.title, "mochi.test",
      "After search, matching selection is kept correctly");
+  Services.obs.notifyObservers(window, TEST_DONE, null);
+},
+
+function test_forget_ipv6(aWin) {
+  // The main purpose of IPv6 entries is that things load, delete them ASAP.
+  // Better forget panel tests (more checks) are in test_prefs_panel below.
+  aWin.gDomains.tree.view.selection.select(1);
+  is(aWin.gDomains.selectedDomain.title, "[::1]",
+     "IPv6 domain is selected");
+  aWin.document.getElementById("domain-context-forget").click();
+  is(aWin.gTabs.activePanel, "forgetPanel",
+     "Forget panel is selected");
+
+  aWin.document.getElementById("forgetCookies").click();
+  aWin.document.getElementById("forgetPasswords").click();
+  aWin.document.getElementById("forgetButton").click();
+  is(aWin.document.getElementById("forgetTab").hidden, true,
+     "Forget tab is hidden again");
+  is(aWin.document.getElementById("forgetTab").disabled, true,
+     "Forget panel is disabled again");
+
+  is(aWin.gDomains.tree.view.rowCount, gPreexistingDomains + 5,
+     "The IPv6 domain has been removed from the list");
+  is(aWin.gDomains.tree.view.selection.count, 0,
+     "No domain is selected");
 
   aWin.gDomains.tree.view.selection.select(0);
   is(aWin.gDomains.selectedDomain.title, "*",
      "* domain is selected again");
   Services.obs.notifyObservers(window, TEST_DONE, null);
 },
 
 function test_fdata_panel(aWin) {
@@ -253,17 +287,17 @@ function test_cookies_panel(aWin) {
   is(aWin.document.getElementById("cookies-context-selectall").disabled, false,
      "The select all context menu item is enabled");
   is(aWin.document.getElementById("cookies-context-remove").disabled, true,
      "The remove context menu item is disabled");
 
   aWin.document.getElementById("cookies-context-selectall").click();
   is(aWin.document.getElementById("cookieInfoSendType").value,
      "Encrypted connections only",
-     "Correct send type for third cookie");
+     "Correct send type for fourth cookie");
   isnot(aWin.document.getElementById("cookieInfoExpires").value,
         "At end of session",
         "Expiry label for this cookie is not session");
   aWin.gCookies.updateContext(); // don't actually open it, would be async
   is(aWin.document.getElementById("cookies-context-selectall").disabled, true,
      "After selecting, the select all context menu item is disabled");
   is(aWin.document.getElementById("cookies-context-remove").disabled, false,
      "After selecting, the remove context menu item is enabled");