Bug 804985 - [browser] don't try to zoom out wider than the page width r=cjones a=lsblakk
authorBenjamin Chen <bechen@mozilla.com>
Wed, 28 Nov 2012 17:18:12 +0800
changeset 118572 269946c84e11fa6425c799365e5fd687dda226e7
parent 118571 0c97e6c40f6c42e8320bc3d753318383e2925d42
child 118573 317c31ddf1a5e3c9b0284763984c295e1c13561f
push idunknown
push userunknown
push dateunknown
reviewerscjones, lsblakk
bugs804985
milestone19.0a2
Bug 804985 - [browser] don't try to zoom out wider than the page width r=cjones a=lsblakk
dom/browser-element/BrowserElementScrolling.js
dom/ipc/TabChild.cpp
gfx/layers/ipc/AsyncPanZoomController.cpp
--- a/dom/browser-element/BrowserElementScrolling.js
+++ b/dom/browser-element/BrowserElementScrolling.js
@@ -21,17 +21,17 @@ const ContentPanning = {
         this.onTouchMove(evt);
         break;
       case 'mouseup':
         this.onTouchEnd(evt);
         break;
       case 'click':
         evt.stopPropagation();
         evt.preventDefault();
-        
+
         let target = evt.target;
         let view = target.ownerDocument ? target.ownerDocument.defaultView
                                         : target;
         view.removeEventListener('click', this, true, true);
         break;
     }
   },
 
@@ -236,16 +236,19 @@ const ContentPanning = {
     return docShell.asyncPanZoomEnabled;
   },
 
   _recvViewportChange: function(data) {
     let metrics = data.json;
     this._viewport = new Rect(metrics.x, metrics.y,
                               metrics.viewport.width,
                               metrics.viewport.height);
+    this._cssCompositedRect = new Rect(metrics.x, metrics.y,
+                                       metrics.cssCompositedRect.width,
+                                       metrics.cssCompositedRect.height);
     this._cssPageRect = new Rect(metrics.cssPageRect.x,
                                  metrics.cssPageRect.y,
                                  metrics.cssPageRect.width,
                                  metrics.cssPageRect.height);
   },
 
   _recvDoubleTap: function(data) {
     let data = data.json;
@@ -278,25 +281,25 @@ const ContentPanning = {
                            rect.y,
                            rect.w + 2 * margin,
                            rect.h);
       // constrict the rect to the screen's right edge
       bRect.width = Math.min(bRect.width, cssPageRect.right - bRect.x);
 
       // if the rect is already taking up most of the visible area and is stretching the
       // width of the page, then we want to zoom out instead.
-      if (this._isRectZoomedIn(bRect, viewport)) {
+      if (this._isRectZoomedIn(bRect, this._cssCompositedRect)) {
         this._zoomOut();
         return;
       }
 
       rect.x = Math.round(bRect.x);
       rect.y = Math.round(bRect.y);
       rect.w = Math.round(bRect.width);
-      rect.h = Math.round(Math.min(bRect.width * viewport.height / viewport.height, bRect.height));
+      rect.h = Math.round(bRect.height);
 
       // if the block we're zooming to is really tall, and the user double-tapped
       // more than a screenful of height from the top of it, then adjust the y-coordinate
       // so that we center the actual point the user double-tapped upon. this prevents
       // flying to the top of a page when double-tapping to zoom in (bug 761721).
       // the 1.2 multiplier is just a little fuzz to compensate for bRect including horizontal
       // margins but not vertical ones.
       let cssTapY = viewport.y + data.y;
@@ -323,35 +326,27 @@ const ContentPanning = {
   _zoomOut: function() {
     let rect = new Rect(0, 0, 0, 0);
     var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
     os.notifyObservers(docShell, 'browser-zoom-to-rect', JSON.stringify(rect));
   },
 
   _isRectZoomedIn: function(aRect, aViewport) {
     // This function checks to see if the area of the rect visible in the
-    // viewport (i.e. the "overlapArea" variable below) is approximately
-    // the max area of the rect we can show. It also checks that the rect
-    // is actually on-screen by testing the left and right edges of the rect.
-    // In effect, this tells us whether or not zooming in to this rect
-    // will significantly change what the user is seeing.
-    const minDifference = -20;
-    const maxDifference = 20;
-
+    // viewport (i.e. the "overlapArea" variable below) is approximately 
+    // the max area of the rect we can show.
     let vRect = new Rect(aViewport.x, aViewport.y, aViewport.width, aViewport.height);
     let overlap = vRect.intersect(aRect);
     let overlapArea = overlap.width * overlap.height;
     let availHeight = Math.min(aRect.width * vRect.height / vRect.width, aRect.height);
     let showing = overlapArea / (aRect.width * availHeight);
-    let dw = (aRect.width - vRect.width);
-    let dx = (aRect.x - vRect.x);
+    let ratioW = (aRect.width / vRect.width);
+    let ratioH = (aRect.height / vRect.height);
 
-    return (showing > 0.9 &&
-            dx > minDifference && dx < maxDifference &&
-            dw > minDifference && dw < maxDifference);
+    return (showing > 0.9 && (ratioW > 0.9 || ratioH > 0.9)); 
   }
 };
 
 ContentPanning.init();
 
 // Min/max velocity of kinetic panning. This is in pixels/millisecond.
 const kMinVelocity = 0.4;
 const kMaxVelocity = 6;
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1168,16 +1168,18 @@ ScrollWindowTo(nsIDOMWindow* aWindow, co
 
 bool
 TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
 {
     if (!mCx || !mTabChildGlobal) {
         return true;
     }
 
+    gfx::Rect cssCompositedRect =
+      AsyncPanZoomController::CalculateCompositedRectInCssPixels(aFrameMetrics);
     // The BrowserElementScrolling helper must know about these updated metrics
     // for other functions it performs, such as double tap handling.
     nsCString data;
     data += nsPrintfCString("{ \"x\" : %d", NS_lround(aFrameMetrics.mScrollOffset.x));
     data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mScrollOffset.y));
     data += nsPrintfCString(", \"viewport\" : ");
         data += nsPrintfCString("{ \"width\" : %f", aFrameMetrics.mViewport.width);
         data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mViewport.height);
@@ -1195,25 +1197,27 @@ TabChild::RecvUpdateFrame(const FrameMet
         data += nsPrintfCString(", \"height\" : %d", aFrameMetrics.mCompositionBounds.height);
         data += nsPrintfCString(" }");
     data += nsPrintfCString(", \"cssPageRect\" : ");
         data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mScrollableRect.x);
         data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mScrollableRect.y);
         data += nsPrintfCString(", \"width\" : %f", aFrameMetrics.mScrollableRect.width);
         data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mScrollableRect.height);
         data += nsPrintfCString(" }");
+    data += nsPrintfCString(", \"cssCompositedRect\" : ");
+            data += nsPrintfCString("{ \"width\" : %f", cssCompositedRect.width);
+            data += nsPrintfCString(", \"height\" : %f", cssCompositedRect.height);
+            data += nsPrintfCString(" }");
     data += nsPrintfCString(" }");
 
     DispatchMessageManagerMessage(NS_LITERAL_STRING("Viewport:Change"), data);
 
     nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
     nsCOMPtr<nsIDOMWindow> window = do_GetInterface(mWebNav);
 
-    gfx::Rect cssCompositedRect =
-      AsyncPanZoomController::CalculateCompositedRectInCssPixels(aFrameMetrics);
     utils->SetScrollPositionClampingScrollPortSize(
       cssCompositedRect.width, cssCompositedRect.height);
     ScrollWindowTo(window, aFrameMetrics.mScrollOffset);
     gfxSize resolution = AsyncPanZoomController::CalculateResolution(
       aFrameMetrics);
     utils->SetResolution(resolution.width, resolution.height);
 
     nsCOMPtr<nsIDOMDocument> domDoc;
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -1202,56 +1202,59 @@ void AsyncPanZoomController::ZoomToRect(
       float newHeight =
         cssCompositionBounds.height * cssPageRect.width / cssCompositionBounds.width;
       float dh = cssCompositionBounds.height - newHeight;
 
       zoomToRect = gfx::Rect(0.0f,
                              y + dh/2,
                              cssPageRect.width,
                              y + dh/2 + newHeight);
-    } else {
-      float targetRatio = float(compositionBounds.width) / float(compositionBounds.height);
-      float rectRatio = zoomToRect.width / zoomToRect.height;
-
-      if (fabsf(targetRatio - rectRatio) < EPSILON) {
-        // All good, do nothing.
-      } else if (targetRatio < rectRatio) {
-        // Need to increase zoomToRect height.
-        float newHeight = zoomToRect.height / targetRatio;
-        zoomToRect.y -= (newHeight - zoomToRect.height) / 2;
-        zoomToRect.height = newHeight;
-      } else { // (targetRatio > rectRatio) {
-        // Need to increase zoomToRect width.
-        float newWidth = targetRatio * zoomToRect.width;
-        zoomToRect.x -= (newWidth - zoomToRect.width) / 2;
-        zoomToRect.width = newWidth;
-      }
-
-      zoomToRect = zoomToRect.Intersect(cssPageRect);
     }
 
     gfxFloat targetResolution =
       NS_MIN(compositionBounds.width / zoomToRect.width,
              compositionBounds.height / zoomToRect.height);
-    gfxFloat targetZoom = clamped(float(targetResolution / resolution.width),
-                                  mMinZoom, mMaxZoom);
-    mEndZoomToMetrics.mZoom = gfxSize(targetZoom, targetZoom);
 
     // Recalculate the zoom to rect using the new dimensions.
     zoomToRect.width = compositionBounds.width / targetResolution;
     zoomToRect.height = compositionBounds.height / targetResolution;
 
     // Clamp the zoom to rect to the CSS rect to make sure it fits.
     zoomToRect = zoomToRect.Intersect(cssPageRect);
 
     // Do one final recalculation to get the resolution.
     targetResolution = NS_MAX(compositionBounds.width / zoomToRect.width,
                               compositionBounds.height / zoomToRect.height);
-    targetZoom = targetResolution / resolution.width;
-    mEndZoomToMetrics.mZoom = gfxSize(targetZoom, targetZoom);
+    float targetZoom = float(targetResolution / resolution.width) * mFrameMetrics.mZoom.width;
+
+    // If current zoom is equal to mMaxZoom,
+    // user still double-tapping it, just zoom-out to the full page size
+    if (mFrameMetrics.mZoom.width == mMaxZoom && targetZoom >= mMaxZoom) {
+      nsIntRect cssCompositionBounds = compositionBounds;
+      cssCompositionBounds.ScaleInverseRoundIn(resolution.width,
+                                               resolution.height);
+      cssCompositionBounds.MoveBy(scrollOffset.x, scrollOffset.y);
+
+      float y = mFrameMetrics.mScrollOffset.y;
+      float newHeight =
+        cssCompositionBounds.height * cssPageRect.width / cssCompositionBounds.width;
+      float dh = cssCompositionBounds.height - newHeight;
+
+      zoomToRect = gfx::Rect(0.0f,
+                             y + dh/2,
+                             cssPageRect.width,
+                             y + dh/2 + newHeight);
+
+      zoomToRect = zoomToRect.Intersect(cssPageRect);
+      // assign 1 to targetZoom is a shortcut
+      targetZoom = 1;
+    }
+
+    gfxFloat targetFinalZoom = clamped(targetZoom, mMinZoom, mMaxZoom);
+    mEndZoomToMetrics.mZoom = gfxSize(targetFinalZoom, targetFinalZoom);
 
     mStartZoomToMetrics = mFrameMetrics;
     mEndZoomToMetrics.mScrollOffset =
       gfx::Point(zoomToRect.x, zoomToRect.y);
 
     mAnimationStartTime = TimeStamp::Now();
 
     ScheduleComposite();