Bug 811406 - Work - Trim http:// and single trailing slash from location text in the Firefox app bar. r=fryn
authorJonathan Wilde <hello@jwilde.me>
Fri, 14 Jun 2013 16:05:09 -0700
changeset 135164 459cc5f50007139874d78066fbe0f177cb69cf73
parent 135163 436af1f4e151b1cf41d99842095d591b262c01f0
child 135165 a6d08264b017a6d80f79574f54aaa54165251afa
push id24830
push userryanvm@gmail.com
push dateSun, 16 Jun 2013 01:34:51 +0000
treeherdermozilla-central@36da3cb92193 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfryn
bugs811406
milestone24.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
Bug 811406 - Work - Trim http:// and single trailing slash from location text in the Firefox app bar. r=fryn
browser/metro/base/content/bindings/autocomplete.xml
browser/metro/base/content/browser-ui.js
browser/metro/profile/metro.js
browser/metro/theme/browser.css
--- a/browser/metro/base/content/bindings/autocomplete.xml
+++ b/browser/metro/base/content/bindings/autocomplete.xml
@@ -41,16 +41,23 @@
 
       <method name="formatValue">
         <body>
           <![CDATA[
             BrowserUI.formatURI();
           ]]>
         </body>
       </method>
+
+      <method name="trimValue">
+        <parameter name="aURL"/>
+        <body><![CDATA[
+          return BrowserUI.trimURL(aURL);
+        ]]></body>
+      </method>
     </implementation>
 
     <handlers>
       <handler event="dblclick" phase="capturing">
         <![CDATA[
           let selectAll = Services.prefs.getBoolPref("browser.urlbar.doubleClickSelectsAll");
           if (selectAll)
             this.select();
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -93,17 +93,20 @@ var BrowserUI = {
     // listening escape to dismiss dialog on VK_ESCAPE
     window.addEventListener("keypress", this, true);
 
     window.addEventListener("MozPrecisePointer", this, true);
     window.addEventListener("MozImprecisePointer", this, true);
 
     Services.prefs.addObserver("browser.cache.disk_cache_ssl", this, false);
     Services.prefs.addObserver("browser.urlbar.formatting.enabled", this, false);
+    Services.prefs.addObserver("browser.urlbar.trimURLs", this, false);
     Services.obs.addObserver(this, "metro_viewstate_changed", false);
+    
+    this._edit.inputField.controllers.insertControllerAt(0, this._copyCutURIController);
 
     // Init core UI modules
     ContextUI.init();
     StartUI.init();
     PanelUI.init();
     FlyoutPanelsUI.init();
     PageThumbs.init();
     SettingsCharm.init();
@@ -559,16 +562,19 @@ var BrowserUI = {
       case "nsPref:changed":
         switch (aData) {
           case "browser.cache.disk_cache_ssl":
             this._sslDiskCacheEnabled = Services.prefs.getBoolPref(aData);
             break;
           case "browser.urlbar.formatting.enabled":
             this._formattingEnabled = Services.prefs.getBookPref(aData);
             break;
+          case "browser.urlbar.trimURLs":
+            this._mayTrimURLs = Services.prefs.getBoolPref(aData);
+            break;
         }
         break;
       case "metro_viewstate_changed":
         this._adjustDOMforViewState();
         let autocomplete = document.getElementById("start-autocomplete");
         if (aData == "snapped") {
           FlyoutPanelsUI.hide();
           // Order matters (need grids to get dimensions, etc), now
@@ -649,21 +655,130 @@ var BrowserUI = {
     let isLoading = Browser.selectedTab.isLoading();
 
     if (isLoading && mode != "loading")
       Elements.urlbarState.setAttribute("mode", "loading");
     else if (!isLoading && mode != "edit")
       Elements.urlbarState.setAttribute("mode", "view");
   },
 
+  _trimURL: function _trimURL(aURL) {
+    // This function must not modify the given URL such that calling
+    // nsIURIFixup::createFixupURI with the result will produce a different URI.
+    return aURL /* remove single trailing slash for http/https/ftp URLs */
+               .replace(/^((?:http|https|ftp):\/\/[^/]+)\/$/, "$1")
+                /* remove http:// unless the host starts with "ftp\d*\." or contains "@" */
+               .replace(/^http:\/\/((?!ftp\d*\.)[^\/@]+(?:\/|$))/, "$1");
+  },
+
+  trimURL: function trimURL(aURL) {
+    return this.mayTrimURLs ? this._trimURL(aURL) : aURL;
+  },
+
   _setURI: function _setURI(aURL) {
     this._edit.value = aURL;
     this.lastKnownGoodURL = aURL;
   },
 
+  _getSelectedURIForClipboard: function _getSelectedURIForClipboard() {
+    // Grab the actual input field's value, not our value, which could include moz-action:
+    let inputVal = this._edit.inputField.value;
+    let selectedVal = inputVal.substring(this._edit.selectionStart, this._edit.electionEnd);
+
+    // If the selection doesn't start at the beginning or doesn't span the full domain or
+    // the URL bar is modified, nothing else to do here.
+    if (this._edit.selectionStart > 0 || this._edit.valueIsTyped)
+      return selectedVal;
+    // The selection doesn't span the full domain if it doesn't contain a slash and is
+    // followed by some character other than a slash.
+    if (!selectedVal.contains("/")) {
+      let remainder = inputVal.replace(selectedVal, "");
+      if (remainder != "" && remainder[0] != "/")
+        return selectedVal;
+    }
+
+    let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
+
+    let uri;
+    try {
+      uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_USE_UTF8);
+    } catch (e) {}
+    if (!uri)
+      return selectedVal;
+
+    // Only copy exposable URIs
+    try {
+      uri = uriFixup.createExposableURI(uri);
+    } catch (ex) {}
+
+    // If the entire URL is selected, just use the actual loaded URI.
+    if (inputVal == selectedVal) {
+      // ... but only if  isn't a javascript: or data: URI, since those
+      // are hard to read when encoded
+      if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) {
+        // Parentheses are known to confuse third-party applications (bug 458565).
+        selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c));
+      }
+
+      return selectedVal;
+    }
+
+    // Just the beginning of the URL is selected, check for a trimmed value
+    let spec = uri.spec;
+    let trimmedSpec = this.trimURL(spec);
+    if (spec != trimmedSpec) {
+      // Prepend the portion that trimURL removed from the beginning.
+      // This assumes trimURL will only truncate the URL at
+      // the beginning or end (or both).
+      let trimmedSegments = spec.split(trimmedSpec);
+      selectedVal = trimmedSegments[0] + selectedVal;
+    }
+
+    return selectedVal;
+  },
+
+  _copyCutURIController: {
+    doCommand: function(aCommand) {
+      let urlbar = BrowserUI._edit;
+      let val = BrowserUI._getSelectedURIForClipboard();
+      if (!val)
+        return;
+
+      if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) {
+        let start = urlbar.selectionStart;
+        let end = urlbar.selectionEnd;
+        urlbar.inputField.value = urlbar.inputField.value.substring(0, start) +
+                                  urlbar.inputField.value.substring(end);
+        urlbar.selectionStart = urlbar.selectionEnd = start;
+      }
+
+      Cc["@mozilla.org/widget/clipboardhelper;1"]
+        .getService(Ci.nsIClipboardHelper)
+        .copyString(val, document);
+    },
+
+    supportsCommand: function(aCommand) {
+      switch (aCommand) {
+        case "cmd_copy":
+        case "cmd_cut":
+          return true;
+      }
+      return false;
+    },
+
+    isCommandEnabled: function(aCommand) {
+      let urlbar = BrowserUI._edit;
+      return this.supportsCommand(aCommand) &&
+             (aCommand != "cmd_cut" || !urlbar.readOnly) &&
+             urlbar.selectionStart < urlbar.selectionEnd;
+    },
+
+    onEvent: function(aEventName) {}
+  },
+
   _urlbarClicked: function _urlbarClicked() {
     // If the urlbar is not already focused, focus it and select the contents.
     if (Elements.urlbarState.getAttribute("mode") != "edit")
       this._editURI(true);
   },
 
   _editURI: function _editURI(aShouldDismiss) {
     this._clearURIFormatting();
@@ -1034,16 +1149,25 @@ var BrowserUI = {
 
   get formattingEnabled() {
     if (this._formattingEnabled === null) {
       this._formattingEnabled = Services.prefs.getBoolPref("browser.urlbar.formatting.enabled");
     }
     return this._formattingEnabled;
   },
 
+  _mayTrimURLs: null,
+
+  get mayTrimURLs() {
+    if (this._mayTrimURLs === null) {
+      this._mayTrimURLs = Services.prefs.getBoolPref("browser.urlbar.trimURLs");
+    }
+    return this._mayTrimURLs;
+  },
+
   supportsCommand : function(cmd) {
     var isSupported = false;
     switch (cmd) {
       case "cmd_back":
       case "cmd_forward":
       case "cmd_reload":
       case "cmd_forceReload":
       case "cmd_stop":
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -256,16 +256,17 @@ pref("browser.urlbar.default.behavior", 
 pref("browser.urlbar.default.behavior.emptyRestriction", 0);
 
 // Let the faviconservice know that we display favicons as 25x25px so that it
 // uses the right size when optimizing favicons
 pref("places.favicons.optimizeToDimension", 25);
 
 // various and sundry awesomebar prefs (should remove/re-evaluate
 // these once bug 447900 is fixed)
+pref("browser.urlbar.trimURLs", true);
 pref("browser.urlbar.formatting.enabled", true);
 pref("browser.urlbar.clickSelectsAll", true);
 pref("browser.urlbar.doubleClickSelectsAll", true);
 pref("browser.urlbar.autoFill", false);
 pref("browser.urlbar.matchOnlyTyped", false);
 pref("browser.urlbar.matchBehavior", 1);
 pref("browser.urlbar.filter.javascript", true);
 pref("browser.urlbar.maxRichResults", 8);
--- a/browser/metro/theme/browser.css
+++ b/browser/metro/theme/browser.css
@@ -303,16 +303,22 @@ documenttab[selected] .documenttab-selec
   margin: 0 !important;
   min-height: @urlbar_edit_height@;
   -moz-appearance: none !important;
   border-radius: 0;
   border: 0 none !important;
   padding: 0 !important;
 }
 
+#urlbar-edit :invalid {
+  /* Hide error glow around the address bar that shows by default
+   * when URLs are made invalid by trmming. */
+  box-shadow: none !important;
+}
+
 /* Combined stop-reload button */
 #tool-reload {
   list-style-image: url("chrome://browser/skin/images/reload.png");
 }
 
 #tool-stop {
   list-style-image: url("chrome://browser/skin/images/stop-hdpi.png");
 }