Bug 665580 - Hide http:// and single trailing slashes in the location bar. ui-r=faaborg r=sdwilsh
authorDão Gottwald <dao@mozilla.com>
Thu, 23 Jun 2011 10:13:41 +0200
changeset 71560 2af378ef814c
parent 71559 48e72227c2fa
child 71561 47fb7e9c7c6a
push id20564
push userdgottwald@mozilla.com
push date2011-06-23 08:15 +0000
treeherdermozilla-central@2af378ef814c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfaaborg, sdwilsh
bugs665580
milestone7.0a1
first release with
nightly win64
2af378ef814c / 7.0a1 / 20110623030205 / files
nightly linux32
nightly linux64
nightly mac
nightly win32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly win64
Bug 665580 - Hide http:// and single trailing slashes in the location bar. ui-r=faaborg r=sdwilsh
browser/app/profile/firefox.js
browser/base/content/test/Makefile.in
browser/base/content/test/browser_bug304198.js
browser/base/content/test/browser_bug556061.js
browser/base/content/test/browser_urlHighlight.js
browser/base/content/test/browser_urlbarTrimURLs.js
browser/base/content/urlbarBindings.xml
browser/base/content/utilityOverlay.js
browser/components/sessionstore/test/browser/browser_522545.js
toolkit/content/widgets/autocomplete.xml
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -292,16 +292,17 @@ pref("browser.urlbar.match.url", "@");
 // more (intersection). Add the following values to set the behavior as the
 // default: 1: history, 2: bookmark, 4: tag, 8: title, 16: url, 32: typed,
 //          64: javascript, 128: tabs
 // E.g., 0 = show all results (no filtering), 1 = only visited pages in history,
 // 2 = only bookmarks, 3 = visited bookmarks, 1+16 = history matching in the url
 pref("browser.urlbar.default.behavior", 0);
 
 pref("browser.urlbar.formatting.enabled", true);
+pref("browser.urlbar.trimURLs", true);
 
 // Number of milliseconds to wait for the http headers (and thus
 // the Content-Disposition filename) before giving up and falling back to 
 // picking a filename without that info in hand so that the user sees some
 // feedback from their action.
 pref("browser.download.saveLinkAsFilenameTimeout", 1000);
 
 pref("browser.download.useDownloadDir", true);
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -221,16 +221,17 @@ endif
                  browser_scope.js \
                  browser_selectTabAtIndex.js \
                  browser_tab_dragdrop.js \
                  browser_tab_dragdrop2.js \
                  browser_tab_dragdrop2_frame1.xul \
                  browser_tabfocus.js \
                  browser_tabs_isActive.js \
                  browser_tabs_owner.js \
+                 browser_urlbarTrimURLs.js \
                  browser_urlHighlight.js \
                  browser_visibleFindSelection.js \
                  browser_visibleTabs.js \
                  browser_visibleTabs_contextMenu.js \
                  browser_visibleTabs_bookmarkAllPages.js \
                  browser_visibleTabs_bookmarkAllTabs.js \
                  browser_visibleTabs_tabPreview.js \
                  bug592338.html \
--- a/browser/base/content/test/browser_bug304198.js
+++ b/browser/base/content/test/browser_bug304198.js
@@ -41,18 +41,16 @@ function test() {
   let charsToDelete, deletedURLTab, fullURLTab, partialURLTab, testPartialURL, testURL;
 
   charsToDelete = 5;
   deletedURLTab = gBrowser.addTab();
   fullURLTab = gBrowser.addTab();
   partialURLTab = gBrowser.addTab();
   testURL = "http://example.org/browser/browser/base/content/test/dummy_page.html";
 
-  testPartialURL = testURL.substr(0, (testURL.length - charsToDelete));
-
   function cleanUp() {
     gBrowser.removeTab(fullURLTab);
     gBrowser.removeTab(partialURLTab);
     gBrowser.removeTab(deletedURLTab);
   }
 
   function cycleTabs() {
     gBrowser.selectedTab = fullURLTab;
@@ -130,16 +128,19 @@ function test() {
         if (gPrefService.prefHasUserValue("browser.urlbar.clickSelectsAll"))
           gPrefService.clearUserPref("browser.urlbar.clickSelectsAll");
         cb();
       }
     });
   }
 
   function runTests() {
+    testURL = gURLBar.trimValue(testURL);
+    testPartialURL = testURL.substr(0, (testURL.length - charsToDelete));
+
     // prepare the three tabs required by this test
     prepareFullURLTab(function () {
       preparePartialURLTab(function () {
         prepareDeletedURLTab(function () {
           // now cycle the tabs and make sure everything looks good
           cycleTabs();
           cleanUp();
           finish();
--- a/browser/base/content/test/browser_bug556061.js
+++ b/browser/base/content/test/browser_bug556061.js
@@ -32,16 +32,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 let testURL = "http://example.org/browser/browser/base/content/test/dummy_page.html";
 let testActionURL = "moz-action:switchtab," + testURL;
+testURL = gURLBar.trimValue(testURL);
 let testTab;
 
 function runNextTest() {
   if (tests.length) {
     let t = tests.shift();
     waitForClipboard(t.expected, t.setup, function() {
       t.success();
       runNextTest();
--- a/browser/base/content/test/browser_urlHighlight.js
+++ b/browser/base/content/test/browser_urlHighlight.js
@@ -22,46 +22,53 @@ function test() {
     Services.prefs.clearUserPref(prefname);
     URLBarSetURI();
   });
 
   Services.prefs.setBoolPref(prefname, true);
 
   gURLBar.focus();
 
-  testVal("http://mozilla.org/");
+  testVal("https://mozilla.org");
 
   gBrowser.selectedBrowser.focus();
 
-  testVal("<http://>mozilla.org");
-  testVal("<http://>mozilla.org</>");
-  testVal("<http://>mözilla.org</>");
-  testVal("<http://>mozilla.imaginatory</>");
-  testVal("<http://www.>mozilla.org</>");
-  testVal("<http://sub.>mozilla.org</>");
-  testVal("<http://sub1.sub2.sub3.>mozilla.org</>");
-  testVal("<http://ftp.>mozilla.org</>");
-  testVal("<ftp://ftp.>mozilla.org</>");
+  testVal("<https://>mozilla.org");
+  testVal("<https://>mözilla.org");
+  testVal("<https://>mozilla.imaginatory");
+
+  testVal("<https://www.>mozilla.org");
+  testVal("<https://sub.>mozilla.org");
+  testVal("<https://sub1.sub2.sub3.>mozilla.org");
+  testVal("<www.>mozilla.org");
+  testVal("<sub.>mozilla.org");
+  testVal("<sub1.sub2.sub3.>mozilla.org");
+
+  testVal("<http://ftp.>mozilla.org");
+  testVal("<ftp://ftp.>mozilla.org");
 
-  testVal("<https://sub.>mozilla.org</>");
-  testVal("<https://sub1.sub2.sub3.>mozilla.org</>");
-  testVal("<https://user:pass@sub1.sub2.sub3.>mozilla.org</>");
-  testVal("<https://user:pass@>mozilla.org</>");
+  testVal("<https://sub.>mozilla.org");
+  testVal("<https://sub1.sub2.sub3.>mozilla.org");
+  testVal("<https://user:pass@sub1.sub2.sub3.>mozilla.org");
+  testVal("<https://user:pass@>mozilla.org");
 
-  testVal("<http://>mozilla.org</file.ext>");
-  testVal("<http://>mozilla.org</sub/file.ext>");
-  testVal("<http://>mozilla.org</sub/file.ext?foo>");
-  testVal("<http://>mozilla.org</sub/file.ext?foo&bar>");
-  testVal("<http://>mozilla.org</sub/file.ext?foo&bar#top>");
+  testVal("<https://>mozilla.org</file.ext>");
+  testVal("<https://>mozilla.org</sub/file.ext>");
+  testVal("<https://>mozilla.org</sub/file.ext?foo>");
+  testVal("<https://>mozilla.org</sub/file.ext?foo&bar>");
+  testVal("<https://>mozilla.org</sub/file.ext?foo&bar#top>");
+  testVal("<https://>mozilla.org</sub/file.ext?foo&bar#top>");
 
-  testVal("<http://sub.>mozilla.org<:666/file.ext>");
+  testVal("<https://sub.>mozilla.org<:666/file.ext>");
 
-  testVal("<http://>[fe80::222:19ff:fe11:8c76]</file.ext>");
+  testVal("<https://>[fe80::222:19ff:fe11:8c76]</file.ext>");
+  testVal("[fe80::222:19ff:fe11:8c76]</file.ext>");
+  testVal("<https://user:pass@>[fe80::222:19ff:fe11:8c76]<:666/file.ext>");
   testVal("<http://user:pass@>[fe80::222:19ff:fe11:8c76]<:666/file.ext>");
 
   testVal("mailto:admin@mozilla.org");
   testVal("gopher://mozilla.org/");
   testVal("about:config");
 
   Services.prefs.setBoolPref(prefname, false);
 
-  testVal("http://mozilla.org/");
+  testVal("https://mozilla.org");
 }
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_urlbarTrimURLs.js
@@ -0,0 +1,75 @@
+function testVal(originalValue, tragetValue) {
+  gURLBar.value = originalValue;
+  is(gURLBar.value, tragetValue || originalValue, "original value: " + originalValue);
+}
+
+function test() {
+  const prefname = "browser.urlbar.trimURLs";
+
+  gBrowser.selectedTab = gBrowser.addTab();
+
+  registerCleanupFunction(function () {
+    gBrowser.removeCurrentTab();
+    Services.prefs.clearUserPref(prefname);
+    URLBarSetURI();
+  });
+
+  Services.prefs.setBoolPref(prefname, true);
+
+  testVal("http://mozilla.org/", "mozilla.org");
+  testVal("https://mozilla.org/", "https://mozilla.org");
+  testVal("http://mözilla.org/", "mözilla.org");
+  testVal("http://mozilla.imaginatory/", "mozilla.imaginatory");
+  testVal("http://www.mozilla.org/", "www.mozilla.org");
+  testVal("http://sub.mozilla.org/", "sub.mozilla.org");
+  testVal("http://sub1.sub2.sub3.mozilla.org/", "sub1.sub2.sub3.mozilla.org");
+  testVal("http://mozilla.org/file.ext", "mozilla.org/file.ext");
+  testVal("http://mozilla.org/sub/", "mozilla.org/sub/");
+
+  testVal("http://ftp.mozilla.org/", "http://ftp.mozilla.org");
+  testVal("ftp://ftp.mozilla.org/", "ftp://ftp.mozilla.org");
+
+  testVal("https://user:pass@mozilla.org/", "https://user:pass@mozilla.org");
+  testVal("http://user:pass@mozilla.org/", "http://user:pass@mozilla.org");
+  testVal("http://sub.mozilla.org:666/", "sub.mozilla.org:666");
+
+  testVal("https://[fe80::222:19ff:fe11:8c76]/file.ext");
+  testVal("http://[fe80::222:19ff:fe11:8c76]/", "[fe80::222:19ff:fe11:8c76]");
+  testVal("https://user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext");
+  testVal("http://user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext");
+
+  testVal("mailto:admin@mozilla.org");
+  testVal("gopher://mozilla.org/");
+  testVal("about:config");
+
+  Services.prefs.setBoolPref(prefname, false);
+
+  testVal("http://mozilla.org/");
+
+  Services.prefs.setBoolPref(prefname, true);
+
+  waitForExplicitFinish();
+
+  gBrowser.selectedBrowser.addEventListener("load", function () {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+
+    is(gBrowser.currentURI.spec, "http://example.com/", "expected page should have loaded");
+
+    testCopy("example.com", "http://example.com/", function () {
+      SetPageProxyState("invalid");
+      testCopy("example.com", "example.com", finish);
+    });
+  }, true);
+
+  gBrowser.loadURI("http://example.com/");
+}
+
+function testCopy(originalValue, targetValue, cb) {
+  waitForClipboard(targetValue, function () {
+    is(gURLBar.value, originalValue);
+
+    gURLBar.focus();
+    gURLBar.select();
+    goDoCommand("cmd_copy");
+  }, cb, cb);
+}
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -86,16 +86,17 @@
                                 .QueryInterface(Components.interfaces.nsIPrefBranch2);
 
         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._urlTooltip = document.getElementById("urlTooltip");
 
         this.inputField.controllers.insertControllerAt(0, this._copyCutController);
         this.inputField.addEventListener("mousedown", this, false);
         this.inputField.addEventListener("mousemove", this, false);
         this.inputField.addEventListener("mouseout", this, false);
         this.inputField.addEventListener("overflow", this, false);
@@ -175,49 +176,65 @@
             this.setAttribute("actiontype", action.type);
           } else {
             this.removeAttribute("actiontype");
           }
           return returnValue;
         ]]></body>
       </method>
 
+      <field name="_mayTrimURLs">true</field>
+      <method name="trimValue">
+        <parameter name="aURL"/>
+        <body><![CDATA[
+          return this._mayTrimURLs ? trimURL(aURL) : aURL;
+        ]]></body>
+      </method>
+
       <field name="_formattingEnabled">true</field>
       <method name="formatValue">
         <body><![CDATA[
           if (!this._formattingEnabled || this.focused)
             return;
 
           let controller = this.editor.selectionController;
           let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
           selection.removeAllRanges();
 
           let textNode = this.editor.rootElement.firstChild;
           let value = textNode.textContent;
-          let matchedURL = value.match(/^((?:http|https|ftp):\/\/(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
+
+          let protocol = value.match(/^[a-z]+:/);
+          if (protocol &&
+              ["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1)
+            return;
+          let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
           if (!matchedURL)
             return;
 
           let [, preDomain, domain] = matchedURL;
           let baseDomain = domain;
           try {
             baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
           } catch (e) {}
           let segments = function (s) s.replace(/[^.]*/g, "").length + 1;
           let subSegments = segments(domain) - segments(baseDomain);
           let subDomain = domain.match(new RegExp("(?:[^.]*\.){" + subSegments + "}"))[0];
 
-          let range = document.createRange();
-          range.setStart(textNode, 0);
-          range.setEnd(textNode, preDomain.length + subDomain.length);
-          selection.addRange(range);
+          let rangeLength = preDomain.length + subDomain.length;
+          if (rangeLength) {
+            let range = document.createRange();
+            range.setStart(textNode, 0);
+            range.setEnd(textNode, rangeLength);
+            selection.addRange(range);
+          }
 
           let startRest = preDomain.length + domain.length;
           if (startRest < value.length) {
-            range = document.createRange();
+            let range = document.createRange();
             range.setStart(textNode, startRest);
             range.setEnd(textNode, value.length);
             selection.addRange(range);
           }
         ]]></body>
       </method>
 
       <method name="_clearFormatting">
@@ -462,17 +479,17 @@
           // The URL bar automatically handles inputs with newline characters,
           // so we can get away with treating text/x-moz-url flavours as text/plain.
           if (url) {
             aEvent.preventDefault();
             this.value = url;
             SetPageProxyState("invalid");
             this.focus();
             try {
-              urlSecurityCheck(this.value,
+              urlSecurityCheck(url,
                                gBrowser.contentPrincipal,
                                Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
             } catch (ex) {
               return;
             }
             this.handleCommand();
           }
         ]]></body>
@@ -483,20 +500,17 @@
           // Grab the actual input field's value, not our value, which could include moz-action:
           var inputVal = this.inputField.value;
           var val = inputVal.substring(this.selectionStart, this.selectionEnd);
 
           // If the entire value is selected and it's a valid non-javascript,
           // non-data URI, encode it.
           if (val == inputVal &&
               this.getAttribute("pageproxystate") == "valid") {
-            let uri;
-            try {
-              uri = makeURI(val);
-            } catch (e) {}
+            let uri = gBrowser.currentURI;
 
             if (uri && !uri.schemeIs("javascript") && !uri.schemeIs("data")) {
               val = uri.spec;
 
               // Parentheses are known to confuse third-party applications (bug 458565).
               val = val.replace(/[()]/g, function (c) escape(c));
             }
           }
@@ -560,16 +574,19 @@
                 this.completeDefaultIndex = this._prefs.getBoolPref(aData);
                 break;
               case "delay":
                 this.timeout = this._prefs.getIntPref(aData);
                 break;
               case "formatting.enabled":
                 this._formattingEnabled = this._prefs.getBoolPref(aData);
                 break;
+              case "trimURLs":
+                this._mayTrimURLs = this._prefs.getBoolPref(aData);
+                break;
             }
           }
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -18,16 +18,17 @@
 # Netscape Communications Corporation.
 # Portions created by the Initial Developer are Copyright (C) 1998
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Alec Flett <alecf@netscape.com>
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Gavin Sharp <gavin@gavinsharp.com>
+#   Dão Gottwald <dao@design-noir.de>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -612,8 +613,15 @@ function openHelpLink(aHelpTopic, aCalle
 function openPrefsHelp() {
   // non-instant apply prefwindows are usually modal, so we can't open in the topmost window, 
   // since its probably behind the window.
   var instantApply = getBoolPref("browser.preferences.instantApply");
 
   var helpTopic = document.getElementsByTagName("prefwindow")[0].currentPane.helpTopic;
   openHelpLink(helpTopic, !instantApply);
 }
+
+function trimURL(aURL) {
+  return aURL /* remove single trailing slash for http/https/ftp URLs */
+             .replace(/^((?:http|https|ftp):\/\/[^/]+)\/$/, "$1")
+              /* remove http:// unless the host starts with "ftp." or contains "@" */
+             .replace(/^http:\/\/((?!ftp\.)[^\/@]+(?:\/|$))/, "$1");
+}
--- a/browser/components/sessionstore/test/browser/browser_522545.js
+++ b/browser/components/sessionstore/test/browser/browser_522545.js
@@ -277,17 +277,17 @@ function test() {
     waitForBrowserState(state, function() {
       let browser = gBrowser.selectedBrowser;
       is(browser.currentURI.spec, "http://example.com/",
          "userTypedClear=2 caused userTypedValue to be loaded");
       is(browser.userTypedValue, null,
          "userTypedValue was null after loading a URI");
       is(browser.userTypedClear, 0,
          "userTypeClear reset to 0");
-      is(gURLBar.value, "http://example.com/",
+      is(gURLBar.value, gURLBar.trimValue("http://example.com/"),
          "Address bar's value set after loading URI");
       runNextTest();
     });
   }
 
 
   let tests = [test_newTabFocused, test_newTabNotFocused,
                test_existingSHEnd_noClear, test_existingSHMiddle_noClear,
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -275,19 +275,24 @@
           return this.inputField.value;
         ]]></getter>
         <setter><![CDATA[
           this.mIgnoreInput = true;
 
           if (typeof this.onBeforeValueSet == "function")
             val = this.onBeforeValueSet(val);
 
+          if (typeof this.trimValue == "function")
+            val = this.trimValue(val);
+
           this.inputField.value = val;
+
           if (typeof this.formatValue == "function")
             this.formatValue();
+
           this.mIgnoreInput = false;
           var event = document.createEvent('Events');
           event.initEvent('ValueChange', true, true);
           this.inputField.dispatchEvent(event);
           return val;
         ]]></setter>
       </property>
 
@@ -1086,16 +1091,19 @@
             // trim the leading/trailing whitespace
             var trimmedSearchString = controller.searchString.replace(/^\s+/, "").replace(/\s+$/, "");
 
             // Unescape the URI spec for showing as an entry in the popup
             let url = Components.classes["@mozilla.org/intl/texttosuburi;1"].
               getService(Components.interfaces.nsITextToSubURI).
               unEscapeURIForUI("UTF-8", controller.getValueAt(this._currentIndex));
 
+            if (typeof this.input.trimValue == "function")
+              url = this.input.trimValue(url);
+
             if (this._currentIndex < existingItemsCount) {
               // re-use the existing item
               item = this.richlistbox.childNodes[this._currentIndex];
 
               // Completely re-use the existing richlistitem if it's the same
               if (item.getAttribute("text") == trimmedSearchString &&
                   item.getAttribute("url") == url) {
                 item.collapsed = false;