Bug 584865 - Use IPC context for zooming canvas [r=mark.finkle]
authorMatt Brubeck <mbrubeck>
Thu, 12 Aug 2010 08:43:00 -0700
changeset 66439 a901e2b0753049b846555ce388a38191cad9ac98
parent 66438 c791799b1fefacc4d7d0712202a5ce09836995ed
child 66440 5ba7a3d59319ae7848c55c485c96c9105e6bb829
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmark
bugs584865, 586353
Bug 584865 - Use IPC context for zooming canvas [r=mark.finkle] This fixes the zoom animation, but MozGetIPCCanvas is slow on Android so we need to reuse the same canvas and cache it at startup time (this means a significant startup time regression). See bug 586353 for more.
mobile/chrome/content/AnimatedZoom.js
mobile/chrome/content/InputHandler.js
mobile/chrome/content/browser.js
--- a/mobile/chrome/content/AnimatedZoom.js
+++ b/mobile/chrome/content/AnimatedZoom.js
@@ -56,21 +56,56 @@ function AnimatedZoom(aBrowserView) {
   let viewportRect = new Rect(0, 0, w, h);
   this.zoomFrom = this.bv.getVisibleRect().translateInside(viewportRect);
 
   // try to cover twice the size of the current visible rect
   this.snapshotRect = this.bv.getVisibleRect().inflate(2);
 
   // sanitize the snapshot rectangle to fit inside viewport
   this.snapshotRect.translateInside(viewportRect).restrictTo(viewportRect).expandToIntegers();
-  this.snapshot = this._createCanvas(this.snapshotRect.width, this.snapshotRect.height);
-  let snapshotCtx = this.snapshot.getContext("2d");
+
+  this.snapshot = AnimatedZoom.createCanvas();
+  let snapshotCtx = this.snapshot.MozGetIPCContext("2d")
   snapshotCtx.clearRect(0, 0, this.snapshotRect.width, this.snapshotRect.height);
   this.bv.renderToCanvas(this.snapshot, this.snapshotRect.width, this.snapshotRect.height, this.snapshotRect.clone());
 
+  let remote = !this.bv.getBrowser().contentWindow;
+  if (remote) {
+    this.canvasReady = false;
+    this.snapshot.addEventListener("MozAsyncCanvasRender", this, false);
+  } else {
+    this.canvasReady = true;
+    this.startAnimation();
+  }
+}
+
+AnimatedZoom.prototype.handleEvent = function(aEvent) {
+  if (aEvent.type == "MozAsyncCanvasRender") {
+    this.snapshot.removeEventListener("MozAsyncCanvasRender", this, false);
+    if (aEvent.originalTarget == this.snapshot) {
+      this.canvasReady = true;
+      this.startAnimation();
+    }
+  }
+};
+
+/** Creating a canvas element of width and height. */
+AnimatedZoom.createCanvas = function(aRemote) {
+  if (!this._canvas) {
+    let canvas = document.createElementNS(kXHTMLNamespaceURI, "canvas");
+    canvas.width = Math.max(window.innerWidth, window.innerHeight) * 2;
+    canvas.height = Math.max(window.innerWidth, window.innerHeight) * 2;
+    canvas.mozOpaque = true;
+    this._canvas = canvas;
+  }
+  return this._canvas;
+};
+
+AnimatedZoom.prototype.startAnimation = function()
+{
   // stop live rendering during zooming
   this.bv.pauseRendering();
 
   // hide ui elements to avoid undefined states after zoom
   Browser.hideTitlebar();
   Browser.hideSidebars();
 
   let clientVis = Browser.browserViewToClientRect(this.bv.getCriticalRect());
@@ -88,27 +123,29 @@ function AnimatedZoom(aBrowserView) {
   ctx.mozImageSmoothingEnabled = false;
   ctx.globalCompositeOperation = 'copy';
 
   // set background fill pattern
   let backgroundImage = new Image();
   backgroundImage.src = "chrome://browser/content/checkerboard.png";
   ctx.fillStyle = ctx.createPattern(backgroundImage, 'repeat');
 
-  // fill the canvas with current data
-  this.updateTo(this.zoomFrom);
-}
+  if (this.zoomTo) {
+    this.updateTo(this.zoomFrom);
 
-/** Creating a canvas element of width and height. */
-AnimatedZoom.prototype._createCanvas = function(width, height) {
-  let canvas = document.createElementNS(kXHTMLNamespaceURI, "canvas");
-  canvas.width = width;
-  canvas.height = height;
-  canvas.mozOpaque = true;
-  return canvas;
+    // start animation timer
+    this.counter = 0;
+    this.inc = 1.0 / Services.prefs.getIntPref("browser.ui.zoom.animationDuration");
+    this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    this.interval = 1000 / Services.prefs.getIntPref("browser.ui.zoom.animationFps");
+    this.timer.initWithCallback(Util.bind(this._callback, this), this.interval, this.timer.TYPE_REPEATING_PRECISE);
+
+    // force first update to be according to FPS even though first callback would take longer
+    this.lastTime = 0;
+  }
 };
 
 /** Updates the zoom to new rect. */
 AnimatedZoom.prototype.updateTo = function(nextRect) {
   this.zoomRect = nextRect;
 
   // prepare to draw to the zoom canvas
   let canvasRect = new Rect(0, 0, Elements.viewBuffer.width, Elements.viewBuffer.height);
@@ -125,16 +162,17 @@ AnimatedZoom.prototype.updateTo = functi
   let s = canvasRect.width / nextRect.width;
   let destRect = srcRect.clone().translate(-nextRect.x, -nextRect.y).scale(s, s);
 
   // adjust from viewport coordinates to snapshot canvas coordinates
   srcRect.translate(-this.snapshotRect.left, -this.snapshotRect.top);
 
   // fill background and draw the (possibly scaled) image
   destRect.restrictTo(canvasRect).expandToIntegers();
+
   ctx.drawImage(this.snapshot,
                 Math.floor(srcRect.left), Math.floor(srcRect.top),
                 Math.floor(srcRect.width), Math.floor(srcRect.height),
                 Math.floor(destRect.left), Math.floor(destRect.top),
                 Math.floor(destRect.width), Math.floor(destRect.height));
 
   // clip the over leftover area and fill it with checkerboard
   let unknowns = canvasRect.subtract(destRect);
@@ -145,30 +183,23 @@ AnimatedZoom.prototype.updateTo = functi
     ctx.fill();
   }
 
   ctx.restore();
 };
 
 /** Starts an animated zoom to zoomRect. */
 AnimatedZoom.prototype.animateTo = function(aZoomRect) {
-  if (this.timer)
-    return false;
-
   this.zoomTo = aZoomRect;
 
-  // start animation timer
-  this.counter = 0;
-  this.inc = 1.0 / Services.prefs.getIntPref("browser.ui.zoom.animationDuration");
-  this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-  this.interval = 1000 / Services.prefs.getIntPref("browser.ui.zoom.animationFps");
-  this.timer.initWithCallback(Util.bind(this._callback, this), this.interval, this.timer.TYPE_REPEATING_PRECISE);
+  if (this.timer || !this.canvasReady)
+    return false;
 
-  // force first update to be according to FPS even though first callback would take longer
-  this.lastTime = 0;
+  this.startAnimation();
+
   return true;
 };
 
 /** Callback for the animation. */
 AnimatedZoom.prototype._callback = function() {
   try {
     if (this.counter < 1) {
       // increase animation position according to elapsed time
@@ -183,17 +214,17 @@ AnimatedZoom.prototype._callback = funct
       this.updateTo(rect);
     }
     else {
       // last cycle already rendered final scaled image, now clean up
       this.finish();
     }
   }
   catch(e) {
-    Util.dumpLn("Error while zooming. Please report error at:", e.getSource());
+    Util.dumpLn("Error while zooming. Please report error at:", e);
     this.finish();
     throw e;
   }
 };
 
 /** Stop animation, zoom to point, and clean up. */
 AnimatedZoom.prototype.finish = function() {
   try {
--- a/mobile/chrome/content/InputHandler.js
+++ b/mobile/chrome/content/InputHandler.js
@@ -1262,17 +1262,18 @@ GestureModule.prototype = {
       if (consume) {
         // prevent sending of event to content
         aEvent.stopPropagation();
         aEvent.preventDefault();
       }
     }
     catch (e) {
       Util.dumpLn("Error while handling gesture event", aEvent.type,
-                  "\nPlease report error at:", e.getSource());
+                  "\nPlease report error at:", e);
+      Cu.reportError(e);
     }
   },
 
   cancelPending: function cancelPending() {
     // terminate pinch zoom if ongoing
     if (this._pinchZoom) {
       this._pinchZoom.finish();
       this._pinchZoom = null;
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -89,18 +89,18 @@ function debug() {
   let [x, y] = [x.value, y.value];
   let [w, h] = [w.value, h.value];
   if (bv) {
     dump('----------------------DEBUG!-------------------------\n');
     dump(bv._browserViewportState.toString() + endl);
 
     dump(endl);
 
-    dump('location from Browser: ' + Browser.selectedBrowser.contentWindow.location + endl);
-    dump('location from BV     : ' + bv.getBrowser().contentWindow.location + endl);
+    dump('location from Browser: ' + Browser.selectedBrowser.currentURI.spec + endl);
+    dump('location from BV     : ' + bv.getBrowser().currentURI.spec + endl);
 
     dump(endl + endl);
 
     let cr = bv._tileManager._criticalRect;
     dump('criticalRect from BV: ' + (cr ? cr.toString() : null) + endl);
     dump('visibleRect from BV : ' + bv.getVisibleRect().toString() + endl);
     dump('visibleRect from foo: ' + Browser.getVisibleRect().toString() + endl);
 
@@ -451,16 +451,19 @@ var Browser = {
       for (let i = Browser.tabs.length - 1; i >= 0; i--)
         Browser.tabs[i].updateViewportSize();
 
       bv.commitBatchOperation();
 
       let curEl = document.activeElement;
       if (curEl && curEl.scrollIntoView)
         curEl.scrollIntoView(false);
+
+      // Preload the zoom snapshot canvas, because it's slow on Android (bug 586353)
+      AnimatedZoom.createCanvas().MozGetIPCContext("2d");
     }
     window.addEventListener("resize", resizeHandler, false);
 
     function fullscreenHandler() {
       if (!window.fullScreen)
         document.getElementById("toolbar-main").setAttribute("fullscreen", "true");
       else
         document.getElementById("toolbar-main").removeAttribute("fullscreen");