Bug 732564 - Move the display port trimming to Java so that we always have a copy of the display port being used. r=Cwiiis
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 12 Mar 2012 12:03:38 -0400
changeset 89330 43427fc6f584f33491d36b277b794e7e4178ace8
parent 89329 18fa9efab2904c7e09f55e20b5d5ae53f5e1fe0a
child 89331 57bf57e234752aa13199b7c74fe941b6b1268b57
push id22242
push userkgupta@mozilla.com
push dateWed, 14 Mar 2012 15:19:09 +0000
treeherdermozilla-central@936ef50fa498 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersCwiiis
bugs732564
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 732564 - Move the display port trimming to Java so that we always have a copy of the display port being used. r=Cwiiis
mobile/android/base/GeckoEvent.java
mobile/android/base/gfx/GeckoLayerClient.java
mobile/android/base/gfx/ImmutableViewportMetrics.java
mobile/android/base/gfx/RectUtils.java
mobile/android/chrome/content/browser.js
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -376,25 +376,25 @@ public class GeckoEvent {
 
     public static GeckoEvent createBroadcastEvent(String subject, String data) {
         GeckoEvent event = new GeckoEvent(BROADCAST);
         event.mCharacters = subject;
         event.mCharactersExtra = data;
         return event;
     }
 
-    public static GeckoEvent createViewportEvent(ViewportMetrics viewport, Rect displayPortMargins) {
+    public static GeckoEvent createViewportEvent(ViewportMetrics viewport, RectF displayPort) {
         GeckoEvent event = new GeckoEvent(VIEWPORT);
         event.mCharacters = "Viewport:Change";
         PointF origin = viewport.getOrigin();
         StringBuffer sb = new StringBuffer(256);
         sb.append("{ \"x\" : ").append(origin.x)
           .append(", \"y\" : ").append(origin.y)
           .append(", \"zoom\" : ").append(viewport.getZoomFactor())
-          .append(", \"displayPortMargins\" :").append(RectUtils.toJSON(displayPortMargins))
+          .append(", \"displayPort\" :").append(RectUtils.toJSON(displayPort))
           .append('}');
         event.mCharactersExtra = sb.toString();
         return event;
     }
 
     public static GeckoEvent createLoadEvent(String uri) {
         GeckoEvent event = new GeckoEvent(LOAD_URI);
         event.mCharacters = uri;
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -64,17 +64,17 @@ public class GeckoLayerClient implements
 
     private LayerController mLayerController;
     private LayerRenderer mLayerRenderer;
     private boolean mLayerRendererInitialized;
 
     private IntSize mScreenSize;
     private IntSize mWindowSize;
     private IntSize mBufferSize;
-    private Rect mDisplayPortMargins;
+    private RectF mDisplayPort;
 
     private VirtualLayer mRootLayer;
 
     /* The viewport that Gecko is currently displaying. */
     private ViewportMetrics mGeckoViewport;
 
     private boolean mViewportSizeChanged;
 
@@ -82,24 +82,21 @@ public class GeckoLayerClient implements
 
     /* Used by robocop for testing purposes */
     private DrawListener mDrawListener;
 
     /* Used as a temporary ViewTransform by getViewTransform */
     private ViewTransform mCurrentViewTransform;
 
     public GeckoLayerClient(Context context) {
+        // we can fill these in with dummy values because they are always written
+        // to before being read
         mScreenSize = new IntSize(0, 0);
         mBufferSize = new IntSize(0, 0);
-        mDisplayPortMargins = new Rect(DEFAULT_DISPLAY_PORT_MARGIN,
-                                       DEFAULT_DISPLAY_PORT_MARGIN,
-                                       DEFAULT_DISPLAY_PORT_MARGIN,
-                                       DEFAULT_DISPLAY_PORT_MARGIN);
-        // we can fill this in with dummy values because it is always written
-        // to before being read
+        mDisplayPort = new RectF();
         mCurrentViewTransform = new ViewTransform(0, 0, 1);
     }
 
     /** Attaches the root layer to the layer controller so that Gecko appears. */
     void setLayerController(LayerController layerController) {
         mLayerController = layerController;
 
         layerController.setRoot(mRootLayer);
@@ -150,22 +147,17 @@ public class GeckoLayerClient implements
         Log.i(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - endDrawing");
         /* Used by robocop for testing purposes */
         if (mDrawListener != null) {
             mDrawListener.drawFinished();
         }
     }
 
     RectF getDisplayPort() {
-        RectF displayPort = new RectF(mRootLayer.getPosition());
-        displayPort.left -= mDisplayPortMargins.left;
-        displayPort.top -= mDisplayPortMargins.top;
-        displayPort.right += mDisplayPortMargins.right;
-        displayPort.bottom += mDisplayPortMargins.bottom;
-        return displayPort;
+        return mDisplayPort;
     }
 
     /* Informs Gecko that the screen size has changed. */
     private void sendResizeEventIfNecessary(boolean force) {
         DisplayMetrics metrics = new DisplayMetrics();
         GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
 
         IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
@@ -216,23 +208,88 @@ public class GeckoLayerClient implements
     public Bitmap getBitmap() {
         return null;
     }
 
     void viewportSizeChanged() {
         mViewportSizeChanged = true;
     }
 
+    private void updateDisplayPort() {
+        float desiredXMargins = 2 * DEFAULT_DISPLAY_PORT_MARGIN;
+        float desiredYMargins = 2 * DEFAULT_DISPLAY_PORT_MARGIN;
+
+        ImmutableViewportMetrics metrics = mLayerController.getViewportMetrics(); 
+
+        // we need to avoid having a display port that is larger than the page, or we will end up
+        // painting things outside the page bounds (bug 729169). we simultaneously need to make
+        // the display port as large as possible so that we redraw less.
+
+        // figure out how much of the desired buffer amount we can actually use on the horizontal axis
+        float xBufferAmount = Math.min(desiredXMargins, Math.max(0, metrics.pageSizeWidth - metrics.getWidth()));
+        // if we reduced the buffer amount on the horizontal axis, we should take that saved memory and
+        // use it on the vertical axis
+        float savedPixels = (desiredXMargins - xBufferAmount) * (metrics.getHeight() + desiredYMargins);
+        float extraYAmount = (float)Math.floor(savedPixels / (metrics.getWidth() + xBufferAmount));
+        float yBufferAmount = Math.min(desiredYMargins + extraYAmount, Math.max(0, metrics.pageSizeHeight - metrics.getHeight()));
+        // and the reverse - if we shrunk the buffer on the vertical axis we can add it to the horizontal
+        if (xBufferAmount == desiredXMargins && yBufferAmount < desiredYMargins) {
+            savedPixels = (desiredYMargins - yBufferAmount) * (metrics.getWidth() + xBufferAmount);
+            float extraXAmount = (float)Math.floor(savedPixels / (metrics.getHeight() + yBufferAmount));
+            xBufferAmount = Math.min(xBufferAmount + extraXAmount, Math.max(0, metrics.pageSizeWidth - metrics.getWidth()));
+        }
+
+        // and now calculate the display port margins based on how much buffer we've decided to use and
+        // the page bounds, ensuring we use all of the available buffer amounts on one side or the other
+        // on any given axis. (i.e. if we're scrolled to the top of the page, the vertical buffer is
+        // entirely below the visible viewport, but if we're halfway down the page, the vertical buffer
+        // is split).
+        float leftMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, Math.max(0, metrics.viewportRectLeft));
+        float rightMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, Math.max(0, metrics.pageSizeWidth - (metrics.viewportRectLeft + metrics.getWidth())));
+        if (leftMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
+            rightMargin = xBufferAmount - leftMargin;
+        } else if (rightMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
+            leftMargin = xBufferAmount - rightMargin;
+        } else if (!FloatUtils.fuzzyEquals(leftMargin + rightMargin, xBufferAmount)) {
+            float delta = xBufferAmount - leftMargin - rightMargin;
+            leftMargin += delta / 2;
+            rightMargin += delta / 2;
+        }
+
+        float topMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, Math.max(0, metrics.viewportRectTop));
+        float bottomMargin = Math.min(DEFAULT_DISPLAY_PORT_MARGIN, Math.max(0, metrics.pageSizeHeight - (metrics.viewportRectTop + metrics.getHeight())));
+        if (topMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
+            bottomMargin = yBufferAmount - topMargin;
+        } else if (bottomMargin < DEFAULT_DISPLAY_PORT_MARGIN) {
+            topMargin = yBufferAmount - bottomMargin;
+        } else if (!FloatUtils.fuzzyEquals(topMargin + bottomMargin, yBufferAmount)) {
+            float delta = yBufferAmount - topMargin - bottomMargin;
+            topMargin += delta / 2;
+            bottomMargin += delta / 2;
+        }
+
+        // note that unless the viewport size changes, or the page dimensions change (either because of
+        // content changes or zooming), the size of the display port should remain constant. this
+        // is intentional to avoid re-creating textures and all sorts of other reallocations in the
+        // draw and composition code.
+
+        mDisplayPort.left = metrics.viewportRectLeft - leftMargin;
+        mDisplayPort.top = metrics.viewportRectTop - topMargin;
+        mDisplayPort.right = metrics.viewportRectRight + rightMargin;
+        mDisplayPort.bottom = metrics.viewportRectBottom + bottomMargin;
+    }
+
     private void adjustViewport() {
         ViewportMetrics viewportMetrics =
             new ViewportMetrics(mLayerController.getViewportMetrics());
 
         viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
 
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createViewportEvent(viewportMetrics, mDisplayPortMargins));
+        updateDisplayPort();
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createViewportEvent(viewportMetrics, mDisplayPort));
         if (mViewportSizeChanged) {
             mViewportSizeChanged = false;
             GeckoAppShell.viewSizeChanged();
         }
     }
 
     /** Implementation of GeckoEventResponder/GeckoEventListener. */
     public void handleMessage(String event, JSONObject message) {
@@ -249,21 +306,22 @@ public class GeckoLayerClient implements
             } catch (JSONException e) {
                 Log.e(LOGTAG, "Unable to create viewport metrics in " + event + " handler", e);
             }
         }
     }
 
     /** Implementation of GeckoEventResponder. */
     public String getResponse() {
-        // We are responding to the events handled in handleMessage() above with
-        // the display port margins we want. Note that all messages we are currently
-        // handling (Viewport:Update) require this response, so we can just return
-        // this indiscriminately.
-        return RectUtils.toJSON(mDisplayPortMargins);
+        // We are responding to the events handled in handleMessage() above with the
+        // display port we want. Note that all messages we are currently handling
+        // (just Viewport:Update) require this response, so we can just return this
+        // indiscriminately.
+        updateDisplayPort();
+        return RectUtils.toJSON(mDisplayPort);
     }
 
     void geometryChanged() {
         /* Let Gecko know if the screensize has changed */
         sendResizeEventIfNecessary(false);
         if (mLayerController.getRedrawHint())
             adjustViewport();
     }
--- a/mobile/android/base/gfx/ImmutableViewportMetrics.java
+++ b/mobile/android/base/gfx/ImmutableViewportMetrics.java
@@ -36,16 +36,24 @@ public class ImmutableViewportMetrics {
         FloatSize pageSize = m.getPageSize();
         pageSizeWidth = pageSize.width;
         pageSizeHeight = pageSize.height;
 
         zoomFactor = m.getZoomFactor();
         allowZoom = m.getAllowZoom();
     }
 
+    public float getWidth() {
+        return viewportRectRight - viewportRectLeft;
+    }
+
+    public float getHeight() {
+        return viewportRectBottom - viewportRectTop;
+    }
+
     // some helpers to make ImmutableViewportMetrics act more like ViewportMetrics
 
     public PointF getOrigin() {
         return new PointF(viewportRectLeft, viewportRectTop);
     }
 
     public FloatSize getSize() {
         return new FloatSize(viewportRectRight - viewportRectLeft, viewportRectBottom - viewportRectTop);
--- a/mobile/android/base/gfx/RectUtils.java
+++ b/mobile/android/base/gfx/RectUtils.java
@@ -53,17 +53,17 @@ public final class RectUtils {
             int width = json.getInt("width");
             int height = json.getInt("height");
             return new Rect(x, y, x + width, y + height);
         } catch (JSONException e) {
             throw new RuntimeException(e);
         }
     }
 
-    public static String toJSON(Rect rect) {
+    public static String toJSON(RectF rect) {
         StringBuffer sb = new StringBuffer(256);
         sb.append("{ \"left\": ").append(rect.left)
           .append(", \"top\": ").append(rect.top)
           .append(", \"right\": ").append(rect.right)
           .append(", \"bottom\": ").append(rect.bottom)
           .append('}');
         return sb.toString();
     }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1584,104 +1584,45 @@ Tab.prototype = {
       this.browser.focus();
       this.browser.docShellIsActive = true;
     } else {
       this.browser.setAttribute("type", "content-targetable");
       this.browser.docShellIsActive = false;
     }
   },
 
-  refreshDisplayPort: function(aDisplayPortMargins) {
-    if (this._zoom <= 0)
+  setDisplayPort: function(aViewportX, aViewportY, aDisplayPortRect) {
+    let zoom = this._zoom;
+    if (zoom <= 0)
       return;
-    if (!this.browser.contentDocument.documentElement)
+
+    let element = this.browser.contentDocument.documentElement;
+    if (!element)
       return;
 
-    let viewport = this.getViewport();
-
-    // we need to avoid having a display port that is larger than the page, or we will end up
-    // painting things outside the page bounds (bug 729169)
-
-    let requestedXAmount = Math.max(0, aDisplayPortMargins.left + aDisplayPortMargins.right);
-    let requestedYAmount = Math.max(0, aDisplayPortMargins.top + aDisplayPortMargins.bottom);
-
-    // figure out how much of the specified buffer amount we can actually use on the horizontal axis
-    let xBufferAmount = Math.min(requestedXAmount, Math.max(0, viewport.pageWidth - viewport.width));
-    // if we reduced the buffer amount on the horizontal axis, we should take that saved memory and
-    // use it on the vertical axis
-    let savedPixels = (requestedXAmount - xBufferAmount) * (viewport.height + requestedYAmount);
-    let extraYAmount = Math.floor(savedPixels / (viewport.width + xBufferAmount));
-    let yBufferAmount = Math.min(requestedYAmount + extraYAmount, Math.max(0, viewport.pageHeight - viewport.height));
-    // and the reverse - if we shrunk the buffer on the vertical axis we can add it to the horizontal
-    if (xBufferAmount == requestedXAmount && yBufferAmount < requestedYAmount) {
-        savedPixels = (requestedYAmount - yBufferAmount) * (viewport.width + xBufferAmount);
-        let extraXAmount = Math.floor(savedPixels / (viewport.height + yBufferAmount));
-        xBufferAmount = Math.min(xBufferAmount + extraXAmount, Math.max(0, viewport.pageWidth - viewport.width));
-    }
-
-    // and now calculate the display port margins based on how much buffer we've decided to use and
-    // the page bounds, ensuring we use all of the available buffer amounts on one side or the other
-    // on any given axis. (i.e. if we're scrolled to the top of the page, the vertical buffer is
-    // entirely below the visible viewport, but if we're halfway down the page, the vertical buffer
-    // is split as specified in the aDisplayPortMargins parameter).
-    let leftMargin = Math.min(aDisplayPortMargins.left, Math.max(0, viewport.x));
-    let rightMargin = Math.min(aDisplayPortMargins.right, Math.max(0, viewport.pageWidth - (viewport.x + viewport.width)));
-    if (leftMargin < aDisplayPortMargins.left) {
-      rightMargin = xBufferAmount - leftMargin;
-    } else if (rightMargin < aDisplayPortMargins.right) {
-      leftMargin = xBufferAmount - rightMargin;
-    } else if (Math.abs(leftMargin + rightMargin - xBufferAmount) >= 1e-6) {
-      let delta = xBufferAmount - leftMargin - rightMargin;
-      leftMargin += delta / 2;
-      rightMargin += delta / 2;
-    }
-
-    let topMargin = Math.min(aDisplayPortMargins.top, Math.max(0, viewport.y));
-    let bottomMargin = Math.min(aDisplayPortMargins.bottom, Math.max(0, viewport.pageHeight - (viewport.y + viewport.height)));
-    if (topMargin < aDisplayPortMargins.top) {
-      bottomMargin = yBufferAmount - topMargin;
-    } else if (bottomMargin < aDisplayPortMargins.bottom) {
-      topMargin = yBufferAmount - bottomMargin;
-    } else if (Math.abs(topMargin + bottomMargin - yBufferAmount) >= 1e-6) {
-      let delta = yBufferAmount - topMargin - bottomMargin;
-      topMargin += delta / 2;
-      bottomMargin += delta / 2;
-    }
-
-    dump("### displayport margins=(" + leftMargin + ", " + topMargin + ", " + rightMargin + ", " + bottomMargin + ") at zoom=" + viewport.zoom
-        + " and buffer amounts=(" + xBufferAmount + ", " + yBufferAmount + ")");
-
-    // note that unless the viewport size changes, or the page dimensions change (either because of
-    // content changes or zooming), the size of the display port should remain constant. this
-    // is intentional to avoid re-creating textures and all sorts of other reallocations in the
-    // draw and composition code.
-
-    let cwu = window.top.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDOMWindowUtils);
-    cwu.setDisplayPortForElement(-leftMargin / viewport.zoom,
-                                 -topMargin / viewport.zoom,
-                                 (leftMargin + viewport.width + rightMargin) / viewport.zoom,
-                                 (topMargin + viewport.height + bottomMargin) / viewport.zoom,
-                                 this.browser.contentDocument.documentElement);
+    let cwu = window.top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+    cwu.setDisplayPortForElement((aDisplayPortRect.left - aViewportX) / zoom,
+                                 (aDisplayPortRect.top - aViewportY) / zoom,
+                                 (aDisplayPortRect.right - aDisplayPortRect.left) / zoom,
+                                 (aDisplayPortRect.bottom - aDisplayPortRect.top) / zoom,
+                                 element);
   },
 
   setViewport: function(aViewport) {
     // Transform coordinates based on zoom
-    aViewport.x /= aViewport.zoom;
-    aViewport.y /= aViewport.zoom;
+    let x = aViewport.x / aViewport.zoom;
+    let y = aViewport.y / aViewport.zoom;
 
     // Set scroll position
     let win = this.browser.contentWindow;
-    win.scrollTo(aViewport.x, aViewport.y);
+    win.scrollTo(x, y);
     this.userScrollPos.x = win.scrollX;
     this.userScrollPos.y = win.scrollY;
     this.setResolution(aViewport.zoom, false);
-
-    // always refresh display port when we scroll so that we can clip it to page bounds
-    this.refreshDisplayPort(aViewport.displayPortMargins);
+    this.setDisplayPort(aViewport.x, aViewport.y, aViewport.displayPort);
   },
 
   setResolution: function(aZoom, aForce) {
     // Set zoom level
     if (aForce || Math.abs(aZoom - this._zoom) >= 1e-6) {
       this._zoom = aZoom;
       if (BrowserApp.selectedTab == this) {
         let cwu = window.top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
@@ -1747,19 +1688,19 @@ Tab.prototype = {
 
   sendViewportUpdate: function() {
     if (BrowserApp.selectedTab != this)
       return;
     if (!BrowserApp.isBrowserContentDocumentDisplayed())
       return;
     let message = this.getViewport();
     message.type = "Viewport:Update";
-    let displayPortMargins = sendMessageToJava({ gecko: message });
-    if (displayPortMargins != null)
-      this.refreshDisplayPort(JSON.parse(displayPortMargins));
+    let displayPort = sendMessageToJava({ gecko: message });
+    if (displayPort != null)
+      this.setDisplayPort(message.x, message.y, JSON.parse(displayPort));
   },
 
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
       case "DOMContentLoaded": {
         let target = aEvent.originalTarget;
 
         // ignore on frames
@@ -2012,17 +1953,17 @@ Tab.prototype = {
     if ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) == 0) {
       // The document element must have a display port on it at all times. At this
       // point Java doesn't know about this document yet, so we cannot query it for
       // a display port. We default to a display port with zero margins, as this is
       // safe and Java will overwrite it once it becomes aware of this document.
       // XXX This code assumes that this is the earliest hook we have at which
       // browser.contentDocument is changed to the new document we're loading
       this.contentDocumentIsDisplayed = false;
-      this.refreshDisplayPort({left: 0, top: 0, right: 0, bottom: 0 });
+      this.setDisplayPort(0, 0, {left: 0, top: 0, right: gScreenWidth, bottom: gScreenHeight });
     } else {
       this.sendViewportUpdate();
     }
   },
 
   onSecurityChange: function(aWebProgress, aRequest, aState) {
     let mode = "unknown";
     if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)