Bug 596678 - Link hover in location bar should not appear instantly, without fade, if a link is previously moused over too quickly to show it. r=dao, a=gavin
authorDrew Willcoxon <adw@mozilla.com>
Sat, 18 Sep 2010 20:09:17 -0700
changeset 54335 7640eb022be6465490c307fc5614fbd0cde63907
parent 54334 1d2729c14e468d2fef04bb49f46d397585a80ad8
child 54336 df5f947d36e9b1947907d2c6925c713a934fc1d8
push id15855
push userdwillcoxon@mozilla.com
push dateSun, 19 Sep 2010 03:09:40 +0000
treeherdermozilla-central@7640eb022be6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdao, gavin
bugs596678
milestone2.0b7pre
first release with
nightly linux32
7640eb022be6 / 4.0b7pre / 20100919030835 / files
nightly linux64
7640eb022be6 / 4.0b7pre / 20100919030707 / files
nightly mac
7640eb022be6 / 4.0b7pre / 20100919030705 / files
nightly win32
7640eb022be6 / 4.0b7pre / 20100919042023 / 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 596678 - Link hover in location bar should not appear instantly, without fade, if a link is previously moused over too quickly to show it. r=dao, a=gavin
browser/base/content/browser.css
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
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -153,48 +153,51 @@ toolbar[mode="icons"] > #reload-button[d
 }
 
 html|*.urlbar-input {
   direction: ltr;
 }
 
 /* over-link in location bar */
 
+/* Delay transitions on mouseout.  (Mouseover transitions are delayed by a
+   timeout in urlbarBindings.xml.) */
+.urlbar-textbox-container:not([overlinkstate]),
+.urlbar-over-link-layer:not([overlinkstate]),
+.urlbar-textbox-container-children:not([overlinkstate]),
+.urlbar-over-link-box:not([overlinkstate]) {
+  -moz-transition-delay: 100ms;
+}
+
 .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"] {
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -208,17 +208,16 @@ endif
                  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 += \
deleted file mode 100644
--- a/browser/base/content/test/browser_overLinkInLocationBar.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<html>
-  <body>
-    <p>
-      <a href="http://example.com/" id="link">LINK</a>
-    </p>
-  </body>
-</html>
--- a/browser/base/content/test/browser_overLinkInLocationBar.js
+++ b/browser/base/content/test/browser_overLinkInLocationBar.js
@@ -35,38 +35,31 @@
  *
  * ***** 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;
+  if (ensureOverLinkHidden())
+    yield;
 
-  let contentDoc = gBrowser.contentDocument;
-  let link = contentDoc.getElementById("link");
-
-  mouseover(link);
+  setOverLink("http://example.com/");
   yield;
   checkURLBar(true);
 
-  mouseout(link);
+  setOverLink("");
   yield;
   checkURLBar(false);
-
-  gBrowser.removeTab(tab);
 }
 
 function test() {
   waitForExplicitFinish();
   gTestIter = smokeTestGenerator();
   cont();
 }
 
@@ -107,71 +100,41 @@ function checkURLBar(shouldShowOverLink)
        "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.
+ * Sets the over-link.  This assumes that aStr will cause the over-link to fade
+ * in or out.  When its transition has finished, the test iterator is
+ * incremented, 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.
+ * @param aStr
+ *        The over-link will be set to this string or cleared if this is falsey.
  */
-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) {
+function setOverLink(aStr) {
   let overLink = gURLBar._overLinkBox;
   overLink.addEventListener("transitionend", function onTrans(event) {
-    if (event.target == overLink) {
+    if (event.target == overLink && event.propertyName == "opacity") {
       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);
+  gURLBar.setOverLink(aStr);
 }
+
+/**
+ * If the over-link is hidden, returns false.  Otherwise, hides the overlink and
+ * returns true.  When the overlink is hidden, the test iterator is incremented,
+ * so if this function returns true, you should yield after calling.
+ *
+ * @return True if you should yield and calling and false if not.
+ */
+function ensureOverLinkHidden() {
+  let overLink = gURLBar._overLinkBox;
+  if (window.getComputedStyle(overLink, null).opacity == 0)
+    return false;
+
+  setOverLink("");
+  return true;
+}
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -118,17 +118,16 @@
         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);
 
         const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
         var textBox = document.getAnonymousElementByAttribute(this,
                                                 "anonid", "textbox-input-box");
         var cxmenu = document.getAnonymousElementByAttribute(textBox,
                                             "anonid", "input-box-contextmenu");
         var insertLocation = cxmenu.firstChild;
         while (insertLocation.nextSibling &&
@@ -149,17 +148,16 @@
         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.
@@ -519,22 +517,16 @@
               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[
@@ -591,25 +583,38 @@
                                                 "over-link-path-label");
       ]]></field>
 
       <field name="_textboxContainer" readonly="true"><![CDATA[
         document.getAnonymousElementByAttribute(this, "anonid",
                                                 "textbox-container");
       ]]></field>
 
+      <field name="_overLinkDelay" readonly="true"><![CDATA[
+        100
+      ]]></field>
+
+      <field name="_overLinkDelayTimer"><![CDATA[
+        undefined
+      ]]></field>
+
       <method name="setOverLink">
         <parameter name="aURL"/>
         <body><![CDATA[
+          // If the over-link is already scheduled to fade-in, cancel it.
+          if (this._overLinkDelayTimer) {
+            clearTimeout(this._overLinkDelayTimer);
+            delete this._overLinkDelayTimer;
+          }
+
           // Hide the over-link if aURL is falsey or if the URL bar is focused.
           if (!aURL || this.focused) {
-            if (this.hasAttribute("overlinkstate")) {
+            // Over-link is fading in or showing.  Fade out.
+            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
@@ -646,23 +651,29 @@
             }
             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)
+          // Finally, show the over-link.
+          if (window.getComputedStyle(overLink, null).opacity != 0) {
+            // It's currently becoming transparent, becoming opaque, or is
+            // opaque.  Show it immediately.
             this.setAttribute("overlinkstate", "showing");
+          }
           else {
-            this.setAttribute("overlinkstate", "fade-in");
-            this._overLinkTransitioning = true;
+            // It's not showing at all.  Start fade-in after some delay.
+            this._overLinkDelayTimer =
+              setTimeout(function overLinkDelayTimer(self) {
+                delete self._overLinkDelayTimer;
+                self.setAttribute("overlinkstate", "fade-in");
+              }, this._overLinkDelay, this);
           }
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="draggesture" phase="capturing"><![CDATA[
         // TODO: This should use dragstart but editor code is still using