bug 450930. only redraw when necessary (get rid of 100ms timer redraws.) r=gavin
authorStuart Parmenter <pavlov@pavlov.net>
Thu, 18 Sep 2008 11:07:16 -0700
changeset 64839 6f9224934949541849b7aa103b8ed4478d1dd43d
parent 64838 d861c474523ae364b687ab35498f403db476cc72
child 64840 2f185dfe058567efbde038e02f21fc36518bd638
push idunknown
push userunknown
push dateunknown
reviewersgavin
bugs450930
bug 450930. only redraw when necessary (get rid of 100ms timer redraws.) r=gavin
mobile/chrome/content/browser.js
mobile/chrome/content/deckbrowser.xml
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -350,44 +350,36 @@ function ProgressController(aTabBrowser,
   this.init(aBrowser);
 }
 
 ProgressController.prototype = {
   _browser : null,
 
   init : function(aBrowser) {
     this._browser = aBrowser;
-
-    // FIXME: until we can get proper canvas repainting hooked up, update the canvas every 300ms
-    var tabbrowser = this._tabbrowser;
-    this._refreshInterval = setInterval(function () {
-      tabbrowser.updateCanvasState();
-    }, 400);
   },
 
   onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus) {
     if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
       if (aRequest && aWebProgress.DOMWindow == this._browser.contentWindow) {
         if (aStateFlags & Ci.nsIWebProgressListener.STATE_START) {
           BrowserUI.update(TOOLBARSTATE_LOADING, this._browser);
-          this._tabbrowser.updateCanvasState();
         }
         else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
           BrowserUI.update(TOOLBARSTATE_LOADED, this._browser);
-          this._tabbrowser.updateCanvasState();
         }
       }
     }
 
     if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) {
       if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
         aWebProgress.DOMWindow.focus();
         Browser.translatePhoneNumbers();
         this._tabbrowser.updateBrowser(this._browser, true);
-        this._tabbrowser.updateCanvasState(true);
+        this._tabbrowser.updateCanvasState();
         //aWebProgress.DOMWindow.scrollbars.visible = false;
       }
     }
   },
 
   // This method is called to indicate progress changes for the currently
   // loading page.
   onProgressChange : function(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
@@ -425,24 +417,22 @@ ProgressController.prototype = {
       }
     }
     cBrowser.lastURI = aLocationURI;
 
 
     if (aWebProgress.DOMWindow == this._browser.contentWindow) {
       BrowserUI.setURI();
       this._tabbrowser.updateBrowser(this._browser, false);
-      this._tabbrowser.updateCanvasState();
     }
   },
 
   // This method is called to indicate a status changes for the currently
   // loading page.  The message is already formatted for display.
   onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) {
-    this._tabbrowser.updateCanvasState();
   },
 
  // Properties used to cache security state used to update the UI
   _state: null,
   _host: undefined,
   _hostChanged: false, // onLocationChange will flip this bit
 
   // This method is called when the security state of the browser changes.
--- a/mobile/chrome/content/deckbrowser.xml
+++ b/mobile/chrome/content/deckbrowser.xml
@@ -96,29 +96,22 @@
 
       <field name="tabList">
         null
       </field>
 
       <field name="progressListenerCreator"/>
 
       <method name="updateCanvasState">
-        <parameter name="aNewDoc"/>
         <body><![CDATA[
-          if (aNewDoc)
-            this._updateViewState();
-
-          if (this._updateTimeout)
-            clearTimeout(this._updateTimeout);
-
-          var self = this;
-          this._updateTimeout = setTimeout(function () {
-            if (!self.dragData.dragging && !self.dragData.kineticId)
-              self._browserToCanvas();
-          }, 100);
+          this._updateViewState();
+          // clear the whole canvas and redraw
+          var ctx = this._canvas.getContext("2d");
+          ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
+          this._browserToCanvas();
         ]]></body>
       </method>
 
       <method name="_updateViewState">
         <body><![CDATA[
           // Reset the pan data.
           this.dragData.pageX = 0;
           this.dragData.pageY = 0;
@@ -203,26 +196,51 @@
 
             var canvas = display.firstChild;
             if (canvas.localName == "canvas")
               display.removeChild(canvas);
           }
         ]]></body>
       </method>
 
+
+      <field name="browserRedrawHandler">
+        <![CDATA[
+        ({
+          deckbrowser: this,
+
+          handleEvent: function (aEvent) {
+            let self = this.deckbrowser;
+
+            if (self.dragData.dragging || self.dragData.kineticId)
+              return;
+
+            for (let i = 0; i < aEvent.clientRects.length; i++) {
+              let e = aEvent.clientRects.item(i);
+              //dump(Math.floor(e.left) + ", " + Math.floor(e.top) + ", " + Math.ceil(e.width) + ", " + Math.ceil(e.height) + "\n");
+              self._browserToCanvas2(Math.floor(e.left), Math.floor(e.top), Math.ceil(e.width), Math.ceil(e.height));
+            }
+          }
+        });
+        ]]>
+      </field>
+
       <method name="selectTab">
         <parameter name="tab"/>
         <body><![CDATA[
           var currentTab = this.currentTab;
           this.currentTab = tab;
 
           if (currentTab) {
             var currentDisplay = this.getDisplayForTab(currentTab);
             var currentBrowser = this.getBrowserForDisplay(currentDisplay);
             if (currentBrowser) {
+              // stop monitor paint events for this browser
+              currentBrowser.removeEventListener("MozAfterPaint", this.browserRedrawHandler, false);
+
               currentDisplay.url = currentBrowser.contentWindow.location.toString();
               currentBrowser.setAttribute("type", "content");
               currentTab.updateTab(currentBrowser);
             }
           }
 
           var display = this.getDisplayForTab(tab);
           var browser = this.getBrowserForDisplay(display);
@@ -230,19 +248,23 @@
             browser = this.createBrowser(true, tab, display);
             browser.loadURI(display.url, null, null, false);
           }
           display.lastAccess = Date.now();
 
           browser.setAttribute("type", "content-primary");
           this.displayList.selectedPanel = display;
 
+          // start monitoring paint events for this browser
+          browser.addEventListener("MozAfterPaint", this.browserRedrawHandler, false);
+
           var event = document.createEvent("Events");
           event.initEvent("TabSelect", true, false);
           tab.dispatchEvent(event);
+
         ]]></body>
       </method>
 
       <method name="newTab">
         <parameter name="makeFront"/>
         <body><![CDATA[
           var browser = this.createBrowser(makeFront, null, null);
           if (!browser)
@@ -350,17 +372,17 @@
             tab.markInvalid();
 
           const XHTML_NS = "http://www.w3.org/1999/xhtml";
           var canvas = document.createElementNS(XHTML_NS, "canvas");
           canvas.setAttribute("width", domWin.innerWidth);
           canvas.setAttribute("height", domWin.innerHeight);
 
           var ctx = canvas.getContext("2d");
-          ctx.drawWindow(domWin, 0, 0, domWin.innerWidth, domWin.innerHeight, "rgba(0,0,0,0)");
+          ctx.drawWindow(domWin, 0, 0, domWin.innerWidth, domWin.innerHeight, "white");
           display.insertBefore(canvas, display.firstChild);
 
           display.lastAccess = Date.now();
           display.progressListener = null;
           display.removeChild(browser);
         ]]></body>
       </method>
 
@@ -443,37 +465,105 @@
           }
 
           this.browser.contentWindow.scrollTo(state._scrollX, state._scrollY);
         ]]></body>
       </method>
 
       <method name="_browserToCanvas">
         <body><![CDATA[
+
           // FIXME: canvas needs to know it's actual width/height
+          // we should be able to use _canvas.width/_canvas.height here
+          // and can, most of the time
           var rect = this._canvas.getBoundingClientRect();
           var w = rect.right - rect.left;
           var h = rect.bottom - rect.top;
           this._canvas.width = w;
           this._canvas.height = h;
 
           var ctx = this._canvas.getContext("2d");
-
-          ctx.clearRect(0,0,w,h);
-
           ctx.save();
           ctx.scale(this._zoomLevel, this._zoomLevel);
           ctx.drawWindow(this.browser.contentWindow,
                          this.dragData.pageX, this.dragData.pageY,
                          this._screenToPage(w), this._screenToPage(h),
                          "white");
           ctx.restore();
         ]]></body>
       </method>
 
+      <method name="_browserToCanvas2">
+        <parameter name="x"/>
+        <parameter name="y"/>
+        <parameter name="width"/>
+        <parameter name="height"/>
+        <body><![CDATA[
+          
+          function intersect(r1, r2) {
+            let xmost1 = r1.x + r1.width;
+            let ymost1 = r1.y + r1.height;
+            let xmost2 = r2.x + r2.width;
+            let ymost2 = r2.y + r2.height;
+
+            let x = Math.max(r1.x, r2.x);
+            let y = Math.max(r1.y, r2.y);
+
+            let temp = Math.min(xmost1, xmost2);
+            if (temp <= x)
+              return null;
+
+            let width = temp - x;
+
+            temp = Math.min(ymost1, ymost2);
+            if (temp <= y)
+              return null;
+
+            let height = temp - y;
+
+            return {
+              x: x,
+              y: y,
+              width: width,
+              height: height
+            };
+          }
+
+          let r1 = { x : x, 
+                     y : y, 
+                     width : width, 
+                     height: height };
+
+          // check to see if the input coordinates are inside the visiable destination
+          let r2 = { x : this.dragData.pageX, 
+                     y : this.dragData.pageY, 
+                     width : this._canvas.width / this._zoomLevel, 
+                     height: this._canvas.height / this._zoomLevel };
+
+          let dest = intersect(r1, r2);
+
+          if (!dest)
+            return;
+
+          var ctx = this._canvas.getContext("2d");
+
+          ctx.save();
+          ctx.scale(this._zoomLevel, this._zoomLevel);
+          ctx.translate(dest.x - this.dragData.pageX, dest.y - this.dragData.pageY);
+
+          ctx.drawWindow(this.browser.contentWindow,
+                         dest.x, dest.y,
+                         dest.width, dest.height,
+                         "white");
+
+          ctx.restore();
+
+        ]]></body>
+      </method>
+
       <method name="_updateCanvasPosition">
         <body><![CDATA[
           this._canvas.style.marginLeft = this.dragData.dragX + "px";
           this._canvas.style.marginRight = -this.dragData.dragX + "px";
           this._canvas.style.marginTop = this.dragData.dragY + "px";
           this._canvas.style.marginBottom = -this.dragData.dragY + "px";
 
           // Force a sync redraw
@@ -649,17 +739,17 @@
           // These might not exist yet depending on page load state
           var body = cdoc.body || {};
           var html = cdoc.documentElement || {};
 
           var w = Math.max(body.scrollWidth, html.scrollWidth);
           var h = Math.max(body.scrollHeight, html.scrollHeight);
 
           if (isNaN(w) || isNaN(h))
-            throw "Can't get content width/height";
+            return [this._canvas.width, this._canvas.height];
 
           return [w, h];
         </getter>
       </property>
 
       <property name="_effectiveCanvasDimensions" readonly="true">
         <getter><![CDATA[
           return [this._screenToPage(this._canvas.width),
@@ -895,20 +985,16 @@
             }
             this[aEvent.type](aEvent);
           },
 
           mousedown: function seh_mousedown(aEvent) {
             if (aEvent.button != 0)
               return;
 
-            // cancel any pending canvas updates, since we're going to update again
-            if (this._updateTimeout)
-              clearTimeout(this._updateTimeout);
-
             // stop kinetic scrolling if it's in progress
             // avoid setting _lastMouseDown in that case so that we don't
             // redispatch it in mouseup
             let dragData = this.deckbrowser.dragData;
             if (dragData.kineticId) {
                this.deckbrowser._endKinetic();
             } else {
               // Keep a reference to the event so that we can redispatch it
@@ -1079,21 +1165,22 @@
           <![CDATA[
             let canvas = document.getAnonymousElementByAttribute(this, "anonid", "canvas");
 
             let domWin = browser.contentWindow;
             let width = domWin.innerWidth;
             let height = domWin.innerHeight;
 
             let ctx = canvas.getContext("2d");
+
             ctx.clearRect(0, 0, 80, 60);
             ctx.restore(); // XXXndeakin remove this
             ctx.save();
             ctx.scale(80 / width, 60 / height);
-            ctx.drawWindow(domWin, 0, 0, width, height, "rgba(0,0,0,0)");
+            ctx.drawWindow(domWin, 0, 0, width, height, "white");
             ctx.restore();
           ]]>
         </body>
       </method>
       <method name="markInvalid">
         <parameter name="browser"/>
         <body>
           <![CDATA[