Bug 646038 - URL destination flickers when the mouse pointer is over a link positioned at the bottom corner. r=gavin
authorDão Gottwald <dao@mozilla.com>
Thu, 12 Jan 2012 22:24:44 +0100
changeset 85591 35ca03af1b5e3f785e859bcde8659efd5b3a66cf
parent 85590 efdfbd08a4330368aef846bfc6f45d7275826bca
child 85592 c3956cae4197b331e8ef854d4a96d0bdd47e1a95
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgavin
bugs646038
milestone12.0a1
Bug 646038 - URL destination flickers when the mouse pointer is over a link positioned at the bottom corner. r=gavin
browser/base/content/browser.css
browser/base/content/browser.js
browser/base/content/tabbrowser.xml
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -474,16 +474,17 @@ browser[tabmodalPromptShowing] {
 /* Status panel */
 
 statuspanel {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#statuspanel");
   position: fixed;
   margin-top: -3em;
   left: 0;
   max-width: 50%;
+  pointer-events: none;
 }
 
 statuspanel:-moz-locale-dir(ltr)[mirror],
 statuspanel:-moz-locale-dir(rtl):not([mirror]) {
   left: auto;
   right: 0;
 }
 
@@ -500,17 +501,16 @@ statuspanel[type=status] {
 statuspanel[type=overLink] {
   -moz-transition: opacity 120ms ease-out;
   direction: ltr;
 }
 
 statuspanel[inactive] {
   -moz-transition: none;
   opacity: 0;
-  pointer-events: none;
 }
 
 statuspanel[inactive][previoustype=overLink] {
   -moz-transition: opacity 200ms ease-out;
 }
 
 .statuspanel-inner {
   height: 3em;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1746,16 +1746,19 @@ function delayedStartup(isLoadingBlank, 
   // If the user (or the locale) hasn't enabled the top-level "Character
   // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
   // hide it.
   if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
                                              Ci.nsIPrefLocalizedString).data)
     document.getElementById("appmenu_charsetMenu").hidden = true;
 #endif
 
+  window.addEventListener("mousemove", MousePosTracker, false);
+  window.addEventListener("dragover", MousePosTracker, false);
+
   Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
 }
 
 function BrowserShutdown() {
   // In certain scenarios it's possible for unload to be fired before onload,
   // (e.g. if the window is being closed after browser.js loads but before the
   // load completes). In that case, there's nothing to do here.
   if (!gStartupRan)
@@ -9051,15 +9054,78 @@ XPCOMUtils.defineLazyGetter(window, "gSh
   let sysInfo = Components.classes["@mozilla.org/system-info;1"]
                           .getService(Components.interfaces.nsIPropertyBag2);
   return parseFloat(sysInfo.getProperty("version")) < 6;
 #else
   return false;
 #endif
 });
 
+
+var MousePosTracker = {
+  _listeners: [],
+  _x: 0,
+  _y: 0,
+  get _windowUtils() {
+    delete this._windowUtils;
+    return this._windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
+  },
+
+  addListener: function (listener) {
+    if (this._listeners.indexOf(listener) >= 0)
+      return;
+
+    listener._hover = false;
+    this._listeners.push(listener);
+
+    this._callListener(listener);
+  },
+
+  removeListener: function (listener) {
+    var index = this._listeners.indexOf(listener);
+    if (index < 0)
+      return;
+
+    this._listeners.splice(index, 1);
+  },
+
+  handleEvent: function (event) {
+    var screenPixelsPerCSSPixel = this._windowUtils.screenPixelsPerCSSPixel;
+    this._x = event.screenX / screenPixelsPerCSSPixel - window.mozInnerScreenX;
+    this._y = event.screenY / screenPixelsPerCSSPixel - window.mozInnerScreenY;
+
+    this._listeners.forEach(function (listener) {
+      try {
+        this._callListener(listener);
+      } catch (e) {
+        Cu.reportError(e);
+      }
+    }, this);
+  },
+
+  _callListener: function (listener) {
+    let rect = listener.getMouseTargetRect();
+    let hover = this._x >= rect.left &&
+                this._x <= rect.right &&
+                this._y >= rect.top &&
+                this._y <= rect.bottom;
+
+    if (hover == listener._hover)
+      return;
+
+    listener._hover = hover;
+
+    if (hover) {
+      if (listener.onMouseEnter)
+        listener.onMouseEnter();
+    } else {
+      if (listener.onMouseLeave)
+        listener.onMouseLeave();
+    }
+  }
+};
 function focusNextFrame(event) {
   let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
   let dir = event.shiftKey ? fm.MOVEFOCUS_BACKWARDDOC : fm.MOVEFOCUS_FORWARDDOC;
   let element = fm.moveFocus(window, null, dir, fm.FLAG_BYKEY);
   if (element.ownerDocument == document)
     focusAndSelectUrlBar();
 }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4002,20 +4002,23 @@
                    flex="1"
                    crop="end"/>
       </xul:hbox>
     </content>
 
     <implementation implements="nsIDOMEventListener">
       <constructor><![CDATA[
         window.addEventListener("findbaropen", this, false);
+        window.addEventListener("resize", this, false);
       ]]></constructor>
 
       <destructor><![CDATA[
         window.removeEventListener("findbaropen", this, false);
+        window.removeEventListener("resize", this, false);
+        MousePosTracker.removeListener(this);
       ]]></destructor>
 
       <property name="label">
         <setter><![CDATA[
           if (!this.label) {
             if (window.gFindBarInitialized && !window.gFindBar.hidden)
               this.setAttribute("mirror", "true");
             else
@@ -4024,45 +4027,87 @@
 
           this.style.minWidth = this.getAttribute("type") == "status" &&
                                 this.getAttribute("previoustype") == "status"
                                   ? getComputedStyle(this).width : "";
 
           if (val) {
             this.setAttribute("label", val);
             this.removeAttribute("inactive");
+            this._calcMouseTargetRect();
+            MousePosTracker.addListener(this);
           } else {
             this.setAttribute("inactive", "true");
+            MousePosTracker.removeListener(this);
           }
 
           return val;
         ]]></setter>
         <getter>
           return this.hasAttribute("inactive") ? "" : this.getAttribute("label");
         </getter>
       </property>
 
+      <method name="getMouseTargetRect">
+        <body><![CDATA[
+          return this._mouseTargetRect;
+        ]]></body>
+      </method>
+
+      <method name="onMouseEnter">
+        <body>
+          this._mirror();
+        </body>
+      </method>
+
+      <method name="onMouseLeave">
+        <body>
+          this._mirror();
+        </body>
+      </method>
+
       <method name="handleEvent">
         <parameter name="event"/>
         <body><![CDATA[
-          if (event.type == "findbaropen" &&
-              this.label)
-            this.setAttribute("mirror", "true");
+          if (!this.label)
+            return;
+
+          switch (event.type) {
+            case "findbaropen":
+              this.setAttribute("mirror", "true");
+              this._calcMouseTargetRect();
+              break;
+            case "resize":
+              this._calcMouseTargetRect();
+              break;
+          }
+        ]]></body>
+      </method>
+
+      <method name="_calcMouseTargetRect">
+        <body><![CDATA[
+          let alignRight = (window.gFindBarInitialized && !window.gFindBar.hidden);
+
+          if (getComputedStyle(document.documentElement).direction == "rtl")
+            alighRight = !alignRight;
+
+          let rect = this.getBoundingClientRect();
+          this._mouseTargetRect = {
+            top:    rect.top,
+            bottom: rect.bottom,
+            left:   alignRight ? window.innerWidth - rect.width : 0,
+            right:  alignRight ? window.innerWidth : rect.width
+          };
         ]]></body>
       </method>
 
       <method name="_mirror">
         <body>
           if (this.hasAttribute("mirror"))
             this.removeAttribute("mirror");
           else
             this.setAttribute("mirror", "true");
         </body>
       </method>
     </implementation>
-
-    <handlers>
-      <handler event="mouseover" action="this._mirror();"/>
-      <handler event="dragover" action="this._mirror();"/>
-    </handlers>
   </binding>
 
 </bindings>