Bug 501566: It takes too long to highlight a selected link, r=froystig, r=mfinkle
authorBenjamin Stover <ben@vidoop.com>
Tue, 01 Sep 2009 23:39:39 -0400
changeset 811 4b520153dd86581dd7a622c32165c26c84cb10af
parent 810 2e8e83aac0d0d23706585e58305a7a868606f9e7
child 812 873d8e71a69e1b3ba78c8e5ad70bc9769b6f980d
push id697
push usermfinkle@mozilla.com
push dateWed, 02 Sep 2009 03:39:49 +0000
reviewersfroystig, mfinkle
bugs501566
Bug 501566: It takes too long to highlight a selected link, r=froystig, r=mfinkle
chrome/content/InputHandler.js
chrome/content/Util.js
chrome/content/browser.js
--- a/chrome/content/InputHandler.js
+++ b/chrome/content/InputHandler.js
@@ -483,16 +483,19 @@ MouseModule.prototype = {
 
     if (this._dragger && !this._dragger.allowRealtimeDownUp) {
       evInfo.event.stopPropagation();
       evInfo.event.preventDefault();
     }
 
     this._owner.grab(this);
 
+    if (this._clicker)
+      this._clicker.mouseDown(evInfo.event.clientX, evInfo.event.clientY);
+
     if (targetScrollInterface) {
       this._doDragStart(evInfo.event);
     }
 
     this._recordEvent(evInfo);
   },
 
   /**
@@ -520,16 +523,19 @@ MouseModule.prototype = {
 
     this._movedOutOfRadius = this._movedOutOfRadius || dragData.isPointOutsideRadius(sX, sY);
 
     if (dragData.dragging)       // XXX same check as this._dragger but we
       this._doDragStop(sX, sY);  //  are using both, no good reason
 
     this._recordEvent(evInfo);
 
+    if (this._clicker)
+      this._clicker.mouseUp(evInfo.event.clientX, evInfo.event.clientY);
+
     this._doClick(this._movedOutOfRadius);
 
     this._owner.ungrab(this);
   },
 
   /**
    * If we're in a drag, do what we have to do to drag on.
    */
--- a/chrome/content/Util.js
+++ b/chrome/content/Util.js
@@ -58,17 +58,28 @@ let Util = {
       if (instance[key] instanceof Function)
         instance[key] = bind(instance[key], instance);
   },
 
   /** Like dump, but each arg is handled and there's an automatic newline */
   dumpLn: function dumpLn() {
     for (var i = 0; i < arguments.length; i++) { dump(arguments[i] + ' '); }
     dump("\n");
+  },
+
+  /** Executes aFunc after other events have been processed. */
+  executeSoon: function executeSoon(aFunc) {
+    let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
+    tm.mainThread.dispatch({
+      run: function() {
+        aFunc();
+      }
+    }, Ci.nsIThread.DISPATCH_NORMAL);
   }
+
 };
 
 
 // -----------------------------------------------------------
 // Util.Rect is a simple data structure for representation of a rectangle supporting
 // many basic geometric operations.
 //
 
--- a/chrome/content/browser.js
+++ b/chrome/content/browser.js
@@ -339,17 +339,17 @@ var Browser = {
     var self = this;
 
     //dump("begin startup\n");
 
     let container = document.getElementById("tile-container");
     let bv = this._browserView = new BrowserView(container, Browser.getVisibleRect);
 
     /* handles dispatching clicks on tiles into clicks in content or zooms */
-    container.customClicker = this._createContentCustomClicker(bv);
+    container.customClicker = new ContentCustomClicker(bv);
 
     /* vertically scrolling box that contains tiles and the urlbar */
     let contentScrollbox = this.contentScrollbox = document.getElementById("tile-container-container");
     this.contentScrollboxScroller = contentScrollbox.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
     contentScrollbox.customDragger = new Browser.MainDragger(bv);
 
     /* horizontally scrolling box that holds the sidebars as well as the contentScrollbox */
     let controlsScrollbox = this.controlsScrollbox = document.getElementById("scrollbox");
@@ -846,50 +846,16 @@ var Browser = {
             url = url.split("|")[0];
         } catch (e) { /* Fall back on about blank */ }
 
         Browser.selectedBrowser.loadURI(url, null, null, false);
       }
     }
   },
 
-  _createContentCustomClicker: function _createContentCustomClicker(browserView) {
-    // XXX we probably shouldn't generate this dynamically like this, but
-    // actually make it a prototype somewhere and instantiate it and such...
-
-    function dispatchContentClick(browser, x, y) {
-      let cwu = BrowserView.Util.getBrowserDOMWindowUtils(browser);
-      let scrollX = { value: 0 };
-      let scrollY = { value: 0 };
-      cwu.getScrollXY(false, scrollX, scrollY);
-      cwu.sendMouseEvent("mousedown", x - scrollX.value, y - scrollY.value, 0, 1, 0, true);
-      cwu.getScrollXY(false, scrollX, scrollY);
-      cwu.sendMouseEvent("mouseup",   x - scrollX.value, y - scrollY.value, 0, 1, 0, true);
-    }
-
-    return {
-      singleClick: function singleClick(cX, cY) {
-        let browser = browserView.getBrowser();
-        if (browser) {
-          let [x, y] = Browser.transformClientToBrowser(cX, cY);
-          dispatchContentClick(browser, x, y);
-        }
-      },
-
-      doubleClick: function doubleClick(cX1, cY1, cX2, cY2) {
-	if (!Browser.zoomToPoint(cX2, cY2))
-	  Browser.zoomFromPoint(cX2, cY2);
-      },
-
-      toString: function toString() {
-        return "[ContentCustomClicker] { }";
-      }
-    };
-  },
-
   /**
    * Compute the sidebar percentage visibility.
    *
    * @param [optional] dx
    * @param [optional] dy an offset distance at which to perform the visibility
    * computation
    * @return [leftVisibility, rightVisiblity, leftTotalWidth, rightTotalWidth]
    */
@@ -1565,16 +1531,58 @@ const BrowserSearch = {
       if (engine.iconURI)
         button.setAttribute("src", engine.iconURI.spec);
       container.appendChild(button);
       button.engine = engine;
     }
   }
 }
 
+/** Watches for mouse events in chrome and sends them to content. */
+function ContentCustomClicker(browserView) {
+  this._browserView = browserView;
+}
+
+ContentCustomClicker.prototype = {
+    /** Dispatch a mouse event with chrome client coordinates. */
+    _dispatchMouseEvent: function _dispatchMouseEvent(name, cX, cY) {
+      let browser = this._browserView.getBrowser();
+      let [x, y] = Browser.transformClientToBrowser(cX, cY);
+      let cwu = BrowserView.Util.getBrowserDOMWindowUtils(browser);
+      let scrollX = {}, scrollY = {};
+      if (browser) {
+        cwu.getScrollXY(false, scrollX, scrollY);
+        cwu.sendMouseEvent(name, x - scrollX.value, y - scrollY.value, 0, 1, 0, true);
+      }
+    },
+
+    mouseDown: function mouseDown(cX, cY) {
+      this._dispatchMouseEvent("mousedown", cX, cY);
+      // Re-render content after mousedown event has possibly selected something.
+      Util.executeSoon(this._browserView.renderNow);
+    },
+
+    mouseUp: function mouseUp(cX, cY) {
+    },
+
+    singleClick: function singleClick(cX, cY) {
+      // Send mouseup only once we know it is just one click.
+      this._dispatchMouseEvent("mouseup", cX, cY);
+    },
+
+    doubleClick: function doubleClick(cX1, cY1, cX2, cY2) {
+      if (!Browser.zoomToPoint(cX2, cY2))
+        Browser.zoomFromPoint(cX2, cY2);
+    },
+
+    toString: function toString() {
+      return "[ContentCustomClicker] { }";
+    }
+};
+
 /**
  * Utility class to handle manipulations of the identity indicators in the UI
  */
 function IdentityHandler() {
   this._stringBundle = document.getElementById("bundle_browser");
   this._staticStrings = {};
   this._staticStrings[this.IDENTITY_MODE_DOMAIN_VERIFIED] = {
     encryption_label: this._stringBundle.getString("identity.encrypted2")