Bug 587908 - On hyperlink hover, display the target in the location bar. r=dao, a=blocking-beta7
authorDrew Willcoxon <adw@mozilla.com>
Tue, 14 Sep 2010 22:41:16 -0700
changeset 53893 0caec4ddff7470c35e29285703bc1e98bc8770a4
parent 53892 c02f848067382239bf3245e2b2f1083709e68144
child 53895 6697846d4ae8c6fd874b7da5fe332d56d190a3a4
push id15733
push userdietrich@mozilla.com
push dateWed, 15 Sep 2010 09:50:10 +0000
treeherdermozilla-central@0caec4ddff74 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdao, blocking-beta7
bugs587908
milestone2.0b7pre
first release with
nightly linux32
0caec4ddff74 / 4.0b7pre / 20100915030129 / files
nightly linux64
0caec4ddff74 / 4.0b7pre / 20100915030816 / files
nightly mac
0caec4ddff74 / 4.0b7pre / 20100915030819 / files
nightly win32
0caec4ddff74 / 4.0b7pre / 20100915073756 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 587908 - On hyperlink hover, display the target in the location bar. r=dao, a=blocking-beta7
browser/base/content/browser.css
browser/base/content/browser.js
browser/base/content/test/Makefile.in
browser/base/content/test/browser_overLinkInLocationBar.html
browser/base/content/test/browser_overLinkInLocationBar.js
browser/base/content/urlbarBindings.xml
browser/themes/gnomestripe/browser/browser.css
browser/themes/gnomestripe/browser/jar.mn
browser/themes/gnomestripe/browser/urlbar-over-link-arrow.png
browser/themes/pinstripe/browser/browser.css
browser/themes/pinstripe/browser/jar.mn
browser/themes/pinstripe/browser/urlbar-over-link-arrow.png
browser/themes/winstripe/browser/browser-aero.css
browser/themes/winstripe/browser/browser.css
browser/themes/winstripe/browser/jar.mn
browser/themes/winstripe/browser/urlbar-over-link-arrow.png
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -137,24 +137,73 @@ toolbar[mode="icons"] > #reload-button[d
 
 /* ::::: location bar ::::: */
 #urlbar {
   -moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
 }
 
 /* Some child nodes want to be ordered based on the locale's direction, while
    everything else should be ltr. */
-#urlbar:-moz-locale-dir(rtl) > .autocomplete-textbox-container > .textbox-input-box {
+.urlbar-input-box:-moz-locale-dir(rtl) {
   direction: rtl;
 }
 
-#urlbar html|*.autocomplete-textbox {
+html|*.urlbar-input {
   direction: ltr;
 }
 
+/* over-link in location bar */
+
+.urlbar-over-link-layer[overlinkstate="fade-in"],
+.urlbar-textbox-container:not([overlinkstate]) {
+  -moz-transition-property: color;
+  -moz-transition-duration: 150ms;
+  -moz-transition-delay: 50ms;
+  -moz-transition-timing-function: cubic-bezier(0.0, 0.6, 1.0, 1.0);
+}
+
+.urlbar-textbox-container[overlinkstate="fade-in"],
+.urlbar-over-link-layer:not([overlinkstate]) {
+  -moz-transition-property: color;
+  -moz-transition-duration: 150ms;
+  -moz-transition-delay: 50ms;
+  -moz-transition-timing-function: linear;
+  color: transparent;
+}
+
+.urlbar-over-link-box[overlinkstate="fade-in"],
+.urlbar-textbox-container-children:not([overlinkstate]) {
+  -moz-transition-property: opacity;
+  -moz-transition-duration: 150ms;
+  -moz-transition-delay: 50ms;
+  -moz-transition-timing-function: cubic-bezier(0.0, 0.6, 1.0, 1.0);
+  opacity: 1;
+}
+
+.urlbar-textbox-container-children[overlinkstate="fade-in"],
+.urlbar-over-link-box:not([overlinkstate]) {
+  -moz-transition-property: opacity;
+  -moz-transition-duration: 150ms;
+  -moz-transition-delay: 50ms;
+  -moz-transition-timing-function: linear;
+  opacity: 0;
+}
+
+.urlbar-textbox-container[overlinkstate="showing"] {
+  color: transparent;
+}
+
+.urlbar-over-link-box[overlinkstate="showing"] {
+  opacity: 1;
+}
+
+.urlbar-textbox-container-children[overlinkstate="showing"] {
+  opacity: 0;
+}
+
 /* For results that are actions, their description text is shown instead of
    the URL - this needs to follow the locale's direction, unlike URLs. */
 richlistitem[type~="action"]:-moz-locale-dir(rtl) > .ac-url-box {
   direction: rtl;
 }
 
 #urlbar:not([actiontype]) > #urlbar-display {
   display: none;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4003,17 +4003,16 @@ function mimeTypeIsTextBased(aMimeType)
 }
 
 var XULBrowserWindow = {
   // Stored Status, Link and Loading values
   status: "",
   defaultStatus: "",
   jsStatus: "",
   jsDefaultStatus: "",
-  overLink: "",
   startTime: 0,
   statusText: "",
   isBusy: false,
 
   _progressCollapseTimer: 0,
 
   QueryInterface: function (aIID) {
     if (aIID.equals(Ci.nsIWebProgressListener) ||
@@ -4086,26 +4085,26 @@ var XULBrowserWindow = {
     this.updateStatusField();
   },
 
   setDefaultStatus: function (status) {
     this.defaultStatus = status;
     this.updateStatusField();
   },
 
-  setOverLink: function (link, b) {
+  setOverLink: function (link) {
     // Encode bidirectional formatting characters.
     // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
-    this.overLink = link.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
-                                 encodeURIComponent);
-    this.updateStatusField();
+    link = link.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
+                        encodeURIComponent);
+    gURLBar.setOverLink(link);
   },
 
   updateStatusField: function () {
-    var text = this.overLink || this.status || this.jsStatus || this.jsDefaultStatus || this.defaultStatus;
+    var text = this.status || this.jsStatus || this.jsDefaultStatus || this.defaultStatus;
 
     // check the current value so we don't trigger an attribute change
     // and cause needless (slow!) UI updates
     if (this.statusText != text) {
       this.statusTextField.label = text;
       this.statusText = text;
     }
   },
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -203,16 +203,18 @@ endif
                  plugin_both.html \
                  plugin_both2.html \
                  alltabslistener.html \
                  zoom_test.html \
                  dummy_page.html \
                  browser_tabMatchesInAwesomebar.js \
                  file_bug550565_popup.html \
                  file_bug550565_favicon.ico \
+                 browser_overLinkInLocationBar.js \
+                 browser_overLinkInLocationBar.html \
                  $(NULL)
 
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _BROWSER_FILES += \
 		browser_bug462289.js \
 		$(NULL)
 else
 _BROWSER_FILES += \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_overLinkInLocationBar.html
@@ -0,0 +1,7 @@
+<html>
+  <body>
+    <p>
+      <a href="http://example.com/" id="link">LINK</a>
+    </p>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_overLinkInLocationBar.js
@@ -0,0 +1,177 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is browser test code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Drew Willcoxon <adw@mozilla.com>
+ *
+ * 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
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+/**
+ * This tests the "over-link" that appears in the location bar when the user
+ * mouses over a link.  See bug 587908.
+ */
+
+const TEST_URL = "http://mochi.test:8888/browser/browser/base/content/test/browser_overLinkInLocationBar.html";
+
+var gTestIter;
+
+// TESTS //////////////////////////////////////////////////////////////////////
+
+function smokeTestGenerator() {
+  let tab = openTestPage();
+  yield;
+
+  let contentDoc = gBrowser.contentDocument;
+  let link = contentDoc.getElementById("link");
+
+  mouseover(link);
+  yield;
+  checkURLBar(true);
+
+  mouseout(link);
+  yield;
+  checkURLBar(false);
+
+  gBrowser.removeTab(tab);
+}
+
+function test() {
+  waitForExplicitFinish();
+  gTestIter = smokeTestGenerator();
+  cont();
+}
+
+// HELPERS ////////////////////////////////////////////////////////////////////
+
+/**
+ * Advances the test iterator.  When all iterations have completed, the entire
+ * suite is finish()ed.
+ */
+function cont() {
+  try {
+    gTestIter.next();
+  }
+  catch (err if err instanceof StopIteration) {
+    finish();
+  }
+}
+
+/**
+ * Asserts that the location bar looks like it should.
+ *
+ * @param shouldShowOverLink
+ *        True if you expect the over-link to be showing and false otherwise.
+ */
+function checkURLBar(shouldShowOverLink) {
+  let overLink = window.getComputedStyle(gURLBar._overLinkBox, null);
+  let origin = window.getComputedStyle(gURLBar._originLabel, null);
+  let editLayer = window.getComputedStyle(gURLBar._textboxContainer, null);
+  if (shouldShowOverLink) {
+    isnot(origin.color, "transparent",
+          "Origin color in over-link layer should not be transparent");
+    is(overLink.opacity, 1, "Over-link should be opaque");
+    is(editLayer.color, "transparent",
+       "Edit layer color should be transparent");
+  }
+  else {
+    is(origin.color, "transparent",
+       "Origin color in over-link layer should be transparent");
+    is(overLink.opacity, 0, "Over-link should be transparent");
+    isnot(editLayer.color, "transparent",
+          "Edit layer color should not be transparent");
+  }
+}
+
+/**
+ * Opens the test URL in a new foreground tab.  When the page has finished
+ * loading, the test iterator is advanced, so you should yield after calling.
+ *
+ * @return The opened <tab>.
+ */
+function openTestPage() {
+  gBrowser.addEventListener("load", function onLoad(event) {
+    if (event.target.URL == TEST_URL) {
+      gBrowser.removeEventListener("load", onLoad, true);
+      cont();
+    }
+  }, true);
+  return gBrowser.loadOneTab(TEST_URL, { inBackground: false });
+}
+
+/**
+ * Sends a mouseover event to a given anchor node.  When the over-link fade-in
+ * transition has completed, the test iterator is advanced, so you should yield
+ * after calling.
+ *
+ * @param anchorNode
+ *        An anchor node.
+ */
+function mouseover(anchorNode) {
+  mouseAnchorNode(anchorNode, true);
+}
+
+/**
+ * Sends a mouseout event to a given anchor node.  When the over-link fade-out
+ * transition has completed, the test iterator is advanced, so you should yield
+ * after calling.
+ *
+ * @param anchorNode
+ *        An anchor node.
+ */
+function mouseout(anchorNode) {
+  mouseAnchorNode(anchorNode, false);
+}
+
+/**
+ * Helper for mouseover and mouseout.  Sends a mouse event to a given node.
+ * When the over-link fade-in or -out transition has completed, the test
+ * iterator is advanced, so you should yield after calling.
+ *
+ * @param node
+ *        An anchor node in a content document.
+ * @param over
+ *        True for "mouseover" and false for "mouseout".
+ */
+function mouseAnchorNode(node, over) {
+  let overLink = gURLBar._overLinkBox;
+  overLink.addEventListener("transitionend", function onTrans(event) {
+    if (event.target == overLink) {
+      overLink.removeEventListener("transitionend", onTrans, false);
+      cont();
+    }
+  }, false);
+  let offset = over ? 0 : -1;
+  let eventType = over ? "mouseover" : "mouseout";
+  EventUtils.synthesizeMouse(node, offset, offset,
+                             { type: eventType, clickCount: 0 },
+                             node.ownerDocument.defaultView);
+}
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -17,37 +17,91 @@
 # The Original Code is mozilla.org browser.
 #
 # The Initial Developer of the Original Code is
 # Simon Bünzli <zeniko@gmail.com>
 # Portions created by the Initial Developer are Copyright (C) 2006
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
-# Dão Gottwald <dao@design-noir.de>
+#   Dão Gottwald <dao@design-noir.de>
+#   Drew Willcoxon <adw@mozilla.com>
 #
 # 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
 # decision by deleting the provisions above and replace them with the notice
 # 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 *****
 
 <bindings id="urlbarBindings" xmlns="http://www.mozilla.org/xbl"
+          xmlns:html="http://www.w3.org/1999/xhtml"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
+
+    <content sizetopopup="pref">
+      <xul:hbox class="urlbar-frontcap-and-textbox" flex="1">
+        <xul:hbox class="urlbar-frontcap">
+          <children includes="image|deck|stack|box">
+            <xul:image class="autocomplete-icon" allowevents="true"/>
+          </children>
+        </xul:hbox>
+        <xul:stack anonid="stack" class="urlbar-stack" flex="1">
+          <xul:scrollbox class="urlbar-over-link-layer" flex="1"
+                         xbl:inherits="overlinkstate" align="center">
+            <xul:label anonid="origin-label" class="urlbar-origin-label" flex="1"
+                       crop="end"/>
+            <xul:hbox anonid="over-link-box" class="urlbar-over-link-box"
+                      xbl:inherits="overlinkstate" align="center">
+              <xul:label anonid="over-link-host-label"
+                         class="urlbar-over-link-host-label"/>
+              <xul:label anonid="over-link-path-label"
+                         class="urlbar-over-link-path-label" flex="1"/>
+            </xul:hbox>
+          </xul:scrollbox>
+          <xul:hbox anonid="textbox-container"
+                    class="autocomplete-textbox-container urlbar-textbox-container"
+                    flex="1" xbl:inherits="focused,overlinkstate">
+            <xul:hbox anonid="textbox-input-box"
+                      class="textbox-input-box urlbar-input-box"
+                      flex="1" xbl:inherits="tooltiptext=inputtooltiptext">
+              <xul:hbox class="urlbar-textbox-container-children"
+                        xbl:inherits="overlinkstate">
+                <children/>
+              </xul:hbox>
+              <html:input anonid="input"
+                          class="autocomplete-textbox urlbar-input textbox-input"
+                          flex="1" allowevents="true"
+                          xbl:inherits="tooltiptext=inputtooltiptext,onfocus,onblur,value,type,maxlength,disabled,size,readonly,placeholder,tabindex,accesskey"/>
+            </xul:hbox>
+            <xul:hbox class="urlbar-textbox-container-children"
+                      xbl:inherits="overlinkstate">
+              <children includes="hbox"/>
+            </xul:hbox>
+          </xul:hbox>
+        </xul:stack>
+      </xul:hbox>
+      <xul:dropmarker anonid="historydropmarker"
+                      class="autocomplete-history-dropmarker urlbar-history-dropmarker"
+                      allowevents="true"
+                      xbl:inherits="open,enablehistory,parentfocused=focused"/>
+      <xul:popupset anonid="popupset"
+                    class="autocomplete-result-popupset"/>
+      <children includes="toolbarbutton"/>
+    </content>
+
     <implementation implements="nsIObserver, nsIDOMEventListener">
       <constructor><![CDATA[
         this._prefs = Components.classes["@mozilla.org/preferences-service;1"]
                                 .getService(Components.interfaces.nsIPrefService)
                                 .getBranch("browser.urlbar.")
                                 .QueryInterface(Components.interfaces.nsIPrefBranch2);
 
         this._prefs.addObserver("", this, false);
@@ -59,27 +113,29 @@
         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);
         this.inputField.addEventListener("underflow", this, false);
+        this._overLinkBox.addEventListener("transitionend", this, false);
       ]]></constructor>
 
       <destructor><![CDATA[
         this._prefs.removeObserver("", this);
         this._prefs = null;
         this.inputField.controllers.removeController(this._copyCutController);
         this.inputField.removeEventListener("mousedown", this, false);
         this.inputField.removeEventListener("mousemove", this, false);
         this.inputField.removeEventListener("mouseout", this, false);
         this.inputField.removeEventListener("overflow", this, false);
         this.inputField.removeEventListener("underflow", this, false);
+        this._overLinkBox.removeEventListener("transitionend", this, false);
       ]]></destructor>
 
       <field name="_value"></field>
 
       <!--
         onBeforeValueGet is called by the base-binding's .value getter.
         It can return an object with a "value" property, to override the
         return value of the getter.
@@ -439,16 +495,22 @@
               break;
             case "overflow":
               this._contentIsCropped = true;
               break;
             case "underflow":
               this._contentIsCropped = false;
               this._hideURLTooltip();
               break;
+            case "transitionend":
+              if (aEvent.target == this._overLinkBox &&
+                  aEvent.propertyName == "opacity") {
+                this._overLinkTransitioning = false;
+              }
+              break;
           }
         ]]></body>
       </method>
 
       <property name="textValue"
                 onget="return this.value;">
         <setter>
           <![CDATA[
@@ -476,16 +538,110 @@
           if (!/^moz-action:/.test(aUrl))
             return null;
 
           // url is in the format moz-action:ACTION,PARAM
           let [, action, param] = aUrl.match(/^moz-action:([^,]+),(.*)$/);
           return {type: action, param: param};
         ]]></body>
       </method>
+
+      <field name="_stack" readonly="true"><![CDATA[
+        document.getAnonymousElementByAttribute(this, "anonid", "stack");
+      ]]></field>
+
+      <field name="_originLabel" readonly="true"><![CDATA[
+        document.getAnonymousElementByAttribute(this, "anonid", "origin-label");
+      ]]></field>
+
+      <field name="_overLinkBox" readonly="true"><![CDATA[
+        document.getAnonymousElementByAttribute(this, "anonid",
+                                                "over-link-box");
+      ]]></field>
+
+      <field name="_overLinkHostLabel" readonly="true"><![CDATA[
+        document.getAnonymousElementByAttribute(this, "anonid",
+                                                "over-link-host-label");
+      ]]></field>
+
+      <field name="_overLinkPathLabel" readonly="true"><![CDATA[
+        document.getAnonymousElementByAttribute(this, "anonid",
+                                                "over-link-path-label");
+      ]]></field>
+
+      <field name="_textboxContainer" readonly="true"><![CDATA[
+        document.getAnonymousElementByAttribute(this, "anonid",
+                                                "textbox-container");
+      ]]></field>
+
+      <method name="setOverLink">
+        <parameter name="aURL"/>
+        <body><![CDATA[
+          // Hide the over-link if aURL is falsey or if the URL bar is focused.
+          if (!aURL || this.focused) {
+            if (this.hasAttribute("overlinkstate")) {
+              this.removeAttribute("overlinkstate");
+              this._overLinkTransitioning = true;
+            }
+            return;
+          }
+
+          // Get the width of the bar before we go modifying it.
+          var barWidth = this._stack.boxObject.width;
+
+          // Determine the pre-path and path of the over-link.  Include the
+          // path's leading slash in the pre-path so that if the path is
+          // truncated its leading slash is visible.
+          var re = new RegExp("([a-z0-9+.-]+://[^/]+/)(.*)");
+          var match = re.exec(aURL);
+          var host = match ? match[1] : "";
+          var path = match ? match[2] : aURL;
+
+          var overLink = this._overLinkBox;
+          var overLinkHost = this._overLinkHostLabel;
+          var overLinkPath = this._overLinkPathLabel;
+
+          overLinkHost.value = host;
+          overLinkPath.value = path;
+
+          // Remove restrictions on the over-link's width.
+          overLinkHost.flex = 0;
+          overLinkHost.crop = "none";
+          overLinkPath.crop = "none";
+          overLink.style.minWidth = "";
+          overLink.style.maxWidth = "";
+
+          // Cap the width of the over-link to 2/3 of the location bar's.
+          var maxWidth = barWidth * 0.67;
+          var overLinkWidth = overLinkHost.boxObject.width +
+                              overLinkPath.boxObject.width;
+          if (overLinkWidth > maxWidth) {
+            // If the host is wider than the cap and therefore the path is not
+            // visible at all, crop the host at the end.
+            if (overLinkHost.boxObject.width > maxWidth) {
+              overLinkHost.flex = 1;
+              overLinkHost.crop = "end";
+            }
+            overLinkPath.crop = host ? "start" : "end";
+            overLink.style.minWidth = maxWidth + "px";
+            overLink.style.maxWidth = maxWidth + "px";
+          }
+
+          this._originLabel.value = this.value;
+
+          // Finally, show the over-link.  If its animation is currently in
+          // transition, show it immediately rather than animating it again.
+          if (this._overLinkTransitioning)
+            this.setAttribute("overlinkstate", "showing");
+          else {
+            this.setAttribute("overlinkstate", "fade-in");
+            this._overLinkTransitioning = true;
+          }
+        ]]></body>
+      </method>
     </implementation>
 
     <handlers>
       <handler event="draggesture" phase="capturing"><![CDATA[
         // TODO: This should use dragstart but editor code is still using
         //       the old drag & drop APIs, so we have to handle draggesture.
         //       This can be changed once editor code is updated to the new API.
         //       See bug 499008 for details.
@@ -508,17 +664,22 @@
         var dt = event.dataTransfer;
         dt.setData("text/x-moz-url", urlString + "\n" + title);
         dt.setData("text/unicode", urlString);
         dt.setData("text/html", htmlString);
 
         dt.effectAllowed = "copyLink";
         event.stopPropagation();
       ]]></handler>
-      <handler event="focus" phase="capturing" action="this._hideURLTooltip();"/>
+
+      <handler event="focus" phase="capturing"><![CDATA[
+        this._hideURLTooltip();
+        this.setOverLink(null);
+      ]]></handler>
+
       <handler event="dragover" phase="capturing" action="this.onDragOver(event, this);"/>
       <handler event="drop" phase="capturing" action="this.onDrop(event, this);"/>
       <handler event="select"><![CDATA[
         if (!Cc["@mozilla.org/widget/clipboard;1"]
                .getService(Ci.nsIClipboard)
                .supportsSelectionClipboard())
           return;
 
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -22,16 +22,17 @@
  * Contributor(s):
  *   Joe Hewitt (hewitt@netscape.com)
  *   Jason Kersey (kerz@netscape.com)
  *   Pierre Chanial (chanial@noos.fr)
  *   Dean Tessman (dean_tessman@hotmail.com)
  *   Blake Ross (blake@cs.stanford.edu)
  *   Pamela Greene (pamg.bugs@gmail.com)
  *   Dão Gottwald (dao@mozilla.com)
+ *   Drew Willcoxon (adw@mozilla.com)
  *
  * 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
@@ -844,23 +845,23 @@ toolbar[iconsize="small"] #zoom-in-butto
 /* Location bar */
 #urlbar {
   width: 7em;
   min-width: 7em;
   -moz-appearance: textfield;
   padding: 0;
 }
 
-#urlbar > .autocomplete-textbox-container {
+.urlbar-frontcap-and-textbox {
   -moz-appearance: none;
   /* keep the URL bar content LTR */
   direction: ltr;
 }
 
-#urlbar > .autocomplete-history-dropmarker {
+.urlbar-history-dropmarker {
   -moz-appearance: toolbarbutton-dropdown;
 }
 
 #urlbar-container {
   -moz-box-orient: horizontal;
   -moz-box-align: stretch;
 }
 
@@ -879,20 +880,16 @@ toolbar[iconsize="small"] #zoom-in-butto
   -moz-margin-start: -4px;
 }
 
 #urlbar-search-splitter + #urlbar-container > #urlbar ,
 #urlbar-search-splitter + #search-container > #searchbar > .searchbar-textbox {
   -moz-margin-start: 0;
 }
 
-#wrapper-urlbar-container #urlbar > .autocomplete-history-dropmarker {
-  display: none;
-}
-
 #urlbar-display {
   margin-top: -2px;
   margin-bottom: -2px;
   padding-top: 3px;
   padding-bottom: 2px;
   -moz-padding-end: 3px;
   color: GrayText;
   -moz-border-end: 1px solid #AAA;
@@ -907,16 +904,43 @@ toolbar[iconsize="small"] #zoom-in-butto
 #PopupAutoComplete:-moz-locale-dir(rtl) > tree > treerows {
   direction: rtl;
 }
 
 #PopupAutoComplete .autocomplete-treebody {
   direction: ltr;
 }
 
+/* over-link in location bar */
+
+.urlbar-over-link-layer {
+  margin: -2px 0;
+  -moz-margin-start: 0;
+}
+
+.urlbar-origin-label {
+  padding: 0 0 0 4px;
+  margin: 0;
+}
+
+.urlbar-over-link-box {
+  position: relative;
+  right: 0;
+  color: GrayText;
+  padding: 0 5px 0 18px;
+  min-height: 22px;
+  background: url(chrome://browser/skin/urlbar-over-link-arrow.png) no-repeat left center;
+}
+
+.urlbar-over-link-host-label,
+.urlbar-over-link-path-label {
+  padding: 0;
+  margin: 0;
+}
+
 /* Favicon */
 #page-proxy-favicon,
 #urlbar-throbber {
   width: 16px;
   height: 16px;
 }
 
 #page-proxy-stack {
@@ -1206,17 +1230,17 @@ richlistitem[type~="action"][actiontype=
   list-style-image: url("chrome://browser/skin/Go-arrow.png");
 }
 
 /* Combined go/reload/stop button in location bar */
 
 #urlbar > toolbarbutton {
   -moz-appearance: none;
   list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-  margin: -1px;
+  margin: -2px;
   -moz-margin-start: 0;
   padding: 0 3px;
   background-origin: border-box;
   border: none;
   border-left: 1px solid rgba(0,0,0,.35);
   border-top-right-radius: 2px;
   border-bottom-right-radius: 2px;
 }
--- a/browser/themes/gnomestripe/browser/jar.mn
+++ b/browser/themes/gnomestripe/browser/jar.mn
@@ -1,11 +1,12 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
 % override chrome://global/skin/icons/warning-16.png moz-icon://stock/gtk-dialog-warning?size=menu
+  skin/classic/browser/urlbar-over-link-arrow.png
   skin/classic/browser/sanitizeDialog.css             (sanitizeDialog.css)
 * skin/classic/browser/aboutPrivateBrowsing.css             (aboutPrivateBrowsing.css)
 * skin/classic/browser/aboutSessionRestore.css        (aboutSessionRestore.css)
   skin/classic/browser/aboutSessionRestore-window-icon.png
   skin/classic/browser/aboutCertError.css             (aboutCertError.css)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9493770ccc9105527150d4bc8f61848cce2700fc
GIT binary patch
literal 295
zc$@(z0oeYDP)<h;3K|Lk000e1NJLTq000pH000&U1^@s6CTMp;0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy$w@>(RCwB))JYD)FboAy+f3yIY+(u9
z|0*o$noBe|l8cPxI8GydMQWATOf>+&opX@vIXek)u%?s(YuqL;Ds<u;4fcZ|YU&FQ
z&e>o&$V_#GrV)q?_~Py+&vi6_A)=tG8r+4bvBV*Wg1t&Z)Hyi|p|{B);y~UhVHYtF
zI)N0TpiB3jL=6$^Mf_|=7<h6w*9kf4BL-L``gbmr1oYs)S%fob0E+?I%NYScijQ{+
tZ!@q7MD=8>FezYriBv$USbOa&zyS2`fZsAuma+f<002ovPDHLkV1jF$aVr1-
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -21,16 +21,17 @@
  * Contributor(s):
  *   Joe Hewitt (hewitt@netscape.com)
  *   Jason Kersey (kerz@netscape.com)
  *   Pierre Chanial (chanial@noos.fr)
  *   Kevin Gerich (kevin@kmgerich.com)
  *   Pamela Greene (pamg.bugs@gmail.com)
  *   Dão Gottwald (dao@mozilla.com)
  *   Stephen Horlander (stephen@noved.org)
+ *   Drew Willcoxon (adw@mozilla.com)
  *
  * 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
@@ -784,17 +785,17 @@ toolbar[mode="icons"] #zoom-in-button {
 #identity-box.verifiedIdentity {
   background-image: -moz-linear-gradient(#84BB40, #5BA020);
 }
 
 #identity-icon-labels {
   margin: 0 4px 1px;
 }
 
-#urlbar > .autocomplete-textbox-container > .textbox-input-box {
+.urlbar-input-box {
   -moz-margin-start: 0;
   padding: 3px 0 2px;
 }
 
 #urlbar-icons {
   -moz-box-align: center;
 }
 
@@ -843,21 +844,16 @@ toolbar[mode="icons"] #zoom-in-button {
 #urlbar-throbber {
   list-style-image: url("chrome://browser/skin/places/searching_16.png");
 }
 
 #wrapper-urlbar-container[place="palette"] {
   max-width: 20em;
 }
 
-#wrapper-urlbar-container > * > * > * > * > #identity-icon-labels,
-#wrapper-urlbar-container > * > * > .autocomplete-history-dropmarker {
-  display: none;
-}
-
 #urlbar-display {
   margin-top: -3px;
   margin-bottom: -2px;
   padding-top: 3px;
   padding-bottom: 2px;
   -moz-padding-end: 3px;
   color: GrayText;
   -moz-border-end: 1px solid #AAA;
@@ -873,16 +869,42 @@ statusbarpanel#statusbar-display {
   -moz-padding-start: 0;
 }
 
 .statusbarpanel-text {
   margin-top: 2px;
   margin-bottom: 0;
 }
 
+/* over-link in location bar */
+
+.urlbar-origin-label {
+  padding: 0 0 0 1px;
+  margin: 0;
+}
+
+.urlbar-origin-label:-moz-locale-dir(rtl) {
+  -moz-padding-start: 4px;
+}
+
+.urlbar-over-link-box {
+  position: relative;
+  right: 0;
+  color: GrayText;
+  padding: 0 5px 0 18px;
+  min-height: 20px;
+  background: url(chrome://browser/skin/urlbar-over-link-arrow.png) no-repeat left center;
+}
+
+.urlbar-over-link-host-label,
+.urlbar-over-link-path-label {
+  padding: 0;
+  margin: 0;
+}
+
 /* ----- AUTOCOMPLETE ----- */
 
 #treecolAutoCompleteImage {
   max-width: 36px;
 }
 
 .ac-result-type-bookmark,
 .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
--- a/browser/themes/pinstripe/browser/jar.mn
+++ b/browser/themes/pinstripe/browser/jar.mn
@@ -1,10 +1,11 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
+  skin/classic/browser/urlbar-over-link-arrow.png
   skin/classic/browser/sanitizeDialog.css                   (sanitizeDialog.css)
 * skin/classic/browser/aboutPrivateBrowsing.css             (aboutPrivateBrowsing.css)
 * skin/classic/browser/aboutSessionRestore.css              (aboutSessionRestore.css)
   skin/classic/browser/aboutSessionRestore-window-icon.png
   skin/classic/browser/aboutCertError.css                   (aboutCertError.css)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9493770ccc9105527150d4bc8f61848cce2700fc
GIT binary patch
literal 295
zc$@(z0oeYDP)<h;3K|Lk000e1NJLTq000pH000&U1^@s6CTMp;0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy$w@>(RCwB))JYD)FboAy+f3yIY+(u9
z|0*o$noBe|l8cPxI8GydMQWATOf>+&opX@vIXek)u%?s(YuqL;Ds<u;4fcZ|YU&FQ
z&e>o&$V_#GrV)q?_~Py+&vi6_A)=tG8r+4bvBV*Wg1t&Z)Hyi|p|{B);y~UhVHYtF
zI)N0TpiB3jL=6$^Mf_|=7<h6w*9kf4BL-L``gbmr1oYs)S%fob0E+?I%NYScijQ{+
tZ!@q7MD=8>FezYriBv$USbOa&zyS2`fZsAuma+f<002ovPDHLkV1jF$aVr1-
--- a/browser/themes/winstripe/browser/browser-aero.css
+++ b/browser/themes/winstripe/browser/browser-aero.css
@@ -116,17 +116,17 @@
 
   #urlbar,
   .searchbar-textbox {
     background-color: rgba(255,255,255,.725);
     @navbarTextboxCustomBorder@
     color: black;
   }
 
-  #urlbar > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input:-moz-placeholder,
+  html|*.urlbar-input:-moz-placeholder,
   .searchbar-textbox > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input:-moz-placeholder {
     color: #777;
   }
 
   #urlbar:hover,
   .searchbar-textbox:hover {
     background-color: rgba(255,255,255,.898);
   }
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -22,16 +22,17 @@
  *   Joe Hewitt (hewitt@netscape.com)
  *   Jason Kersey (kerz@netscape.com)
  *   Pierre Chanial (chanial@noos.fr)
  *   Dean Tessman (dean_tessman@hotmail.com)
  *   Blake Ross (blake@cs.stanford.edu)
  *   Pamela Greene (pamg.bugs@gmail.com)
  *   Dão Gottwald (dao@mozilla.com)
  *   Jim Mathies (jmathies@mozilla.com)
+ *   Drew Willcoxon (adw@mozilla.com)
  *
  * 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
@@ -957,39 +958,54 @@ toolbar:not([iconsize="small"])[mode="ic
   width: 7em;
   min-width: 7em;
 }
 
 #urlbar,
 .searchbar-textbox {
   -moz-appearance: none;
   margin: 1px 3px;
-  padding: 2px;
   background-clip: padding-box;
   border: 1px solid ThreeDDarkShadow;
   border-radius: 4px;
   box-shadow: 0 1px 0 rgba(0,0,0,.1) inset,
               0 1px 0 rgba(255,255,255,.4);
 }
 
+.urlbar-textbox-container {
+  margin-top: 2px;
+  margin-bottom: 2px;
+  margin-right: 2px;
+}
+
+.urlbar-frontcap {
+  margin-top: 2px;
+  margin-bottom: 2px;
+  margin-left: 2px;
+}
+
+.searchbar-textbox {
+  padding: 2px;
+}
+
 @media all and (-moz-windows-default-theme) {
   #urlbar,
   .searchbar-textbox {
     @navbarTextboxCustomBorder@
   }
 }
 
 #urlbar:-moz-lwtheme,
 .searchbar-textbox:-moz-lwtheme {
   background-color: rgba(255,255,255,.8);
   @navbarTextboxCustomBorder@
   color: black;
 }
 
-#urlbar:-moz-lwtheme > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input:-moz-placeholder,
+html|*.urlbar-input:-moz-lwtheme,
 .searchbar-textbox:-moz-lwtheme > .autocomplete-textbox-container > .textbox-input-box > html|*.textbox-input:-moz-placeholder {
   color: #777;
 }
 
 #urlbar:-moz-lwtheme[focused="true"],
 .searchbar-textbox:-moz-lwtheme[focused="true"] {
   background-color: white;
 }
@@ -1020,36 +1036,54 @@ toolbar:not([iconsize="small"])[mode="ic
   background: transparent;
 }
 
 #urlbar-search-splitter + #urlbar-container > #urlbar ,
 #urlbar-search-splitter + #search-container > #searchbar > .searchbar-textbox {
   -moz-margin-start: 0;
 }
 
-#wrapper-urlbar-container > #urlbar-container > #urlbar > .autocomplete-history-dropmarker {
-  display: none;
-}
-
-#urlbar > .autocomplete-textbox-container {
+.urlbar-frontcap-and-textbox {
   direction: ltr;
   -moz-box-align: stretch;
 }
 
 #urlbar-display {
   margin-top: -2px;
   margin-bottom: -2px;
   padding-top: 3px;
   padding-bottom: 2px;
   -moz-padding-end: 3px;
   color: GrayText;
   -moz-border-end: 1px solid #AAA;
   -moz-margin-end: 3px;
 }
 
+/* over-link in location bar */
+
+.urlbar-origin-label {
+  padding: 0 0 0 4px;
+  margin: 0;
+}
+
+.urlbar-over-link-box {
+  position: relative;
+  right: 0;
+  color: GrayText;
+  padding: 0 5px 0 18px;
+  min-height: 22px;
+  background: url(chrome://browser/skin/urlbar-over-link-arrow.png) no-repeat left center;
+}
+
+.urlbar-over-link-host-label,
+.urlbar-over-link-path-label {
+  padding: 0;
+  margin: 0;
+}
+
 /* identity box */
 
 #identity-box {
   background-color: -moz-dialog;
   background-image: -moz-linear-gradient(rgba(255,255,255,.25), rgba(0,0,0,.15));
   color: -moz-dialogText;
   border-radius: 2px;
 }
@@ -1091,32 +1125,32 @@ toolbar:not([iconsize="small"])[mode="ic
 #identity-icon-labels {
   -moz-margin-start: 1px;
   -moz-margin-end: 3px;
   -moz-transform: translate(0, -1px);
 }
 
 /* Location bar dropmarker */
 
-#urlbar > .autocomplete-history-dropmarker {
+.urlbar-history-dropmarker {
   -moz-appearance: none;
   padding: 0 1px;
   background-color: transparent;
   border: none;
   width: auto;
   list-style-image: url(mainwindow-dropdown-arrow.png);
   -moz-image-region: rect(0, 13px, 11px, 0);
 }
 
-#urlbar > .autocomplete-history-dropmarker:-moz-system-metric(touch-enabled) {
+.urlbar-history-dropmarker:-moz-system-metric(touch-enabled) {
   min-width: 6.4mozmm;
 }
 
-#urlbar > .autocomplete-history-dropmarker:hover:active,
-#urlbar > .autocomplete-history-dropmarker[open="true"] {
+.urlbar-history-dropmarker:hover:active,
+.urlbar-history-dropmarker[open="true"] {
   -moz-image-region: rect(0, 26px, 11px, 13px);
 }
 
 /* page proxy icon */
 
 #page-proxy-favicon,
 #urlbar-throbber {
   width: 16px;
@@ -1237,18 +1271,16 @@ richlistitem[type~="action"][actiontype=
   -moz-image-region: rect(16px, 16px, 32px, 0px);
 }
 
 /* combined go/reload/stop button in location bar */
 
 #urlbar > toolbarbutton {
   -moz-appearance: none;
   list-style-image: url("chrome://browser/skin/reload-stop-go.png");
-  margin: -2px;
-  -moz-margin-start: 0;
   padding: 0 3px;
   background-origin: border-box;
   border: none;
   border-left: 1px solid rgba(0,0,0,.25);
   border-top-right-radius: 2px;
   border-bottom-right-radius: 2px;
 }
 
--- a/browser/themes/winstripe/browser/jar.mn
+++ b/browser/themes/winstripe/browser/jar.mn
@@ -1,13 +1,14 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/ os=WINNT osversion<6
 % skin browser classic/1.0 %skin/classic/browser/ os!=WINNT
 # NOTE: If you add a new file here, you'll need to add it to the aero
 # section at the bottom of this file
+        skin/classic/browser/urlbar-over-link-arrow.png
         skin/classic/browser/sanitizeDialog.css                      (sanitizeDialog.css)
 *       skin/classic/browser/aboutPrivateBrowsing.css                (aboutPrivateBrowsing.css)
 *       skin/classic/browser/aboutSessionRestore.css                 (aboutSessionRestore.css)
         skin/classic/browser/aboutSessionRestore-window-icon.png     (aboutSessionRestore-window-icon.png)
         skin/classic/browser/aboutCertError.css                      (aboutCertError.css)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/aboutSyncTabs.css
 #endif
@@ -113,16 +114,17 @@ browser.jar:
         skin/classic/browser/sync-usedNever.png
         skin/classic/browser/syncSetup.css
         skin/classic/browser/syncCommon.css
 #endif
 
 #ifdef XP_WIN
 browser.jar:
 % skin browser classic/1.0 %skin/classic/aero/browser/ os=WINNT osversion>=6
+        skin/classic/aero/browser/urlbar-over-link-arrow.png
         skin/classic/aero/browser/sanitizeDialog.css                       (sanitizeDialog.css)
 *       skin/classic/aero/browser/aboutPrivateBrowsing.css           (aboutPrivateBrowsing.css)
 *       skin/classic/aero/browser/aboutSessionRestore.css            (aboutSessionRestore.css)
         skin/classic/aero/browser/aboutSessionRestore-window-icon.png (aboutSessionRestore-window-icon-aero.png)
         skin/classic/aero/browser/aboutCertError.css                 (aboutCertError.css)
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/aboutSyncTabs.css
 #endif
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9493770ccc9105527150d4bc8f61848cce2700fc
GIT binary patch
literal 295
zc$@(z0oeYDP)<h;3K|Lk000e1NJLTq000pH000&U1^@s6CTMp;0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy$w@>(RCwB))JYD)FboAy+f3yIY+(u9
z|0*o$noBe|l8cPxI8GydMQWATOf>+&opX@vIXek)u%?s(YuqL;Ds<u;4fcZ|YU&FQ
z&e>o&$V_#GrV)q?_~Py+&vi6_A)=tG8r+4bvBV*Wg1t&Z)Hyi|p|{B);y~UhVHYtF
zI)N0TpiB3jL=6$^Mf_|=7<h6w*9kf4BL-L``gbmr1oYs)S%fob0E+?I%NYScijQ{+
tZ!@q7MD=8>FezYriBv$USbOa&zyS2`fZsAuma+f<002ovPDHLkV1jF$aVr1-