Bug 748384 - Back out 330e9c52f9ac, 9ba1078559fe, 2ed39c12d792, f53f05ecacd5 for build bustage. r=bustage
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 23 May 2012 11:08:19 -0400
changeset 94731 d36479e22b4a4cd773f8aaa11c7a74fa22699f8f
parent 94730 85e40de1d54c4f536994c51019fdab6add5cebd2
child 94732 32eb5ee5d5e48a93db8dc4d0fa5b23c0efe56096
push id797
push userrcampbell@mozilla.com
push dateSat, 26 May 2012 17:15:03 +0000
treeherderfx-team@ebc06677c620 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbustage
bugs748384
milestone15.0a1
Bug 748384 - Back out 330e9c52f9ac, 9ba1078559fe, 2ed39c12d792, f53f05ecacd5 for build bustage. r=bustage
dom/base/nsDOMWindowUtils.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
gfx/layers/Layers.h
gfx/layers/TiledLayerBuffer.h
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
gfx/layers/ipc/ShadowLayerUtils.h
gfx/layers/opengl/ReusableTileStoreOGL.cpp
ipc/glue/IPCMessageUtils.h
layout/base/nsDisplayList.cpp
layout/ipc/RenderFrameParent.cpp
mobile/android/base/GeckoAppShell.java
mobile/android/base/gfx/DisplayPortCalculator.java
mobile/android/base/gfx/GeckoLayerClient.java
mobile/android/base/gfx/ImmutableViewportMetrics.java
mobile/android/base/gfx/Layer.java
mobile/android/base/gfx/LayerController.java
mobile/android/base/gfx/LayerRenderer.java
mobile/android/base/gfx/NinePatchTileLayer.java
mobile/android/base/gfx/ScrollbarLayer.java
mobile/android/base/gfx/SingleTileLayer.java
mobile/android/base/gfx/ViewportMetrics.java
mobile/android/base/ui/Axis.java
mobile/android/base/ui/PanZoomController.java
mobile/android/chrome/content/browser.js
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
widget/android/nsIAndroidBridge.idl
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -44,17 +44,16 @@
 #include "nsStyleAnimation.h"
 #include "nsCSSProps.h"
 #include "nsDOMFile.h"
 #include "BasicLayers.h"
 #include "nsTArrayHelpers.h"
 #include "nsIDocShell.h"
 #include "nsIContentViewer.h"
 #include "nsIMarkupDocumentViewer.h"
-#include "nsClientRect.h"
 
 #if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK2)
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #endif
 
 #include "Layers.h"
 #include "nsIIOService.h"
@@ -1257,49 +1256,16 @@ nsDOMWindowUtils::GetScrollXY(bool aFlus
 
   *aScrollX = nsPresContext::AppUnitsToIntCSSPixels(scrollPos.x);
   *aScrollY = nsPresContext::AppUnitsToIntCSSPixels(scrollPos.y);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::GetRootBounds(nsIDOMClientRect** aResult)
-{
-  // Weak ref, since we addref it below
-  nsClientRect* rect = new nsClientRect();
-  NS_ADDREF(*aResult = rect);
-
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
-  NS_ENSURE_STATE(window);
-
-  nsCOMPtr<nsIDocument> doc(do_QueryInterface(window->GetExtantDocument()));
-  NS_ENSURE_STATE(doc);
-
-  nsRect bounds(0, 0, 0, 0);
-  nsIPresShell* presShell = doc->GetShell();
-  if (presShell) {
-    nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable();
-    if (sf) {
-      bounds = sf->GetScrollRange();
-      bounds.width += sf->GetScrollPortRect().width;
-      bounds.height += sf->GetScrollPortRect().height;
-    } else if (presShell->GetRootFrame()) {
-      bounds = presShell->GetRootFrame()->GetRect();
-    }
-  }
-
-  rect->SetRect(nsPresContext::AppUnitsToFloatCSSPixels(bounds.x),
-                nsPresContext::AppUnitsToFloatCSSPixels(bounds.y),
-                nsPresContext::AppUnitsToFloatCSSPixels(bounds.width),
-                nsPresContext::AppUnitsToFloatCSSPixels(bounds.height));
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsDOMWindowUtils::GetIMEIsOpen(bool *aState)
 {
   NS_ENSURE_ARG_POINTER(aState);
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return NS_ERROR_FAILURE;
 
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -32,19 +32,18 @@ interface nsIDOMHTMLCanvasElement;
 interface nsIDOMEvent;
 interface nsITransferable;
 interface nsIQueryContentEventResult;
 interface nsIDOMWindow;
 interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
-interface nsIDOMClientRect;
 
-[scriptable, uuid(2b3ac53c-2a88-421f-af09-f10665c88acf)]
+[scriptable, uuid(2e5a1f37-786b-4a52-b0e3-f711ee2268a8)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -591,23 +590,16 @@ interface nsIDOMWindowUtils : nsISupport
    * Returns the scroll position of the window's currently loaded document.
    *
    * @param aFlushLayout flushes layout if true. Otherwise, no flush occurs.
    * @see nsIDOMWindow::scrollX/Y
    */
   void getScrollXY(in boolean aFlushLayout, out long aScrollX, out long aScrollY);
 
   /**
-   * Returns the bounds of the window's currently loaded document. This will
-   * generally be (0, 0, pageWidth, pageHeight) but in some cases (e.g. RTL
-   * documents) may have a negative left value.
-   */
-  nsIDOMClientRect getRootBounds();
-
-  /**
    * Get IME open state. TRUE means 'Open', otherwise, 'Close'.
    * This property works only when IMEEnabled is IME_STATUS_ENABLED.
    */
   readonly attribute boolean IMEIsOpen;
 
   /**
    * WARNING: These values must be same as nsIWidget's values.
    */
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -72,20 +72,20 @@ public:
   typedef PRUint64 ViewID;
   static const ViewID NULL_SCROLL_ID;   // This container layer does not scroll.
   static const ViewID ROOT_SCROLL_ID;   // This is the root scroll frame.
   static const ViewID START_SCROLL_ID;  // This is the ID that scrolling subframes
                                         // will begin at.
 
   FrameMetrics()
     : mViewport(0, 0, 0, 0)
-    , mContentRect(0, 0, 0, 0)
+    , mContentSize(0, 0)
     , mViewportScrollOffset(0, 0)
     , mScrollId(NULL_SCROLL_ID)
-    , mCSSContentRect(0, 0, 0, 0)
+    , mCSSContentSize(0, 0)
     , mResolution(1, 1)
   {}
 
   // Default copy ctor and operator= are fine
 
   bool operator==(const FrameMetrics& aOther) const
   {
     return (mViewport.IsEqualEdges(aOther.mViewport) &&
@@ -110,24 +110,24 @@ public:
 
   bool IsScrollable() const
   {
     return mScrollId != NULL_SCROLL_ID;
   }
 
   // These are all in layer coordinate space.
   nsIntRect mViewport;
-  nsIntRect mContentRect;
+  nsIntSize mContentSize;
   nsIntPoint mViewportScrollOffset;
   nsIntRect mDisplayPort;
   ViewID mScrollId;
 
-  // Consumers often want to know the origin/size before scaling to pixels
-  // so we record this as well.
-  gfx::Rect mCSSContentRect;
+  // Consumers often want to know the size before scaling to pixels
+  // so we record this size as well.
+  gfx::Size mCSSContentSize;
 
   // This represents the resolution at which the associated layer
   // will been rendered.
   gfxSize mResolution;
 };
 
 #define MOZ_LAYER_DECL_NAME(n, e)                           \
   virtual const char* Name() const { return n; }            \
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -23,19 +23,16 @@
 namespace mozilla {
 namespace layers {
 
 // An abstract implementation of a tile buffer. This code covers the logic of
 // moving and reusing tiles and leaves the validation up to the implementor. To
 // avoid the overhead of virtual dispatch, we employ the curiously recurring
 // template pattern.
 //
-// Tiles are aligned to a grid with one of the grid points at (0,0) and other
-// grid points spaced evenly in the x- and y-directions by GetTileLength().
-//
 // This tile buffer stores a valid region, which defines the areas that have
 // up-to-date content. The contents of tiles within this region will be reused
 // from paint to paint. It also stores the region that was modified in the last
 // paint operation; this is useful when one tiled layer buffer shadows another
 // (as in an off-main-thread-compositing scenario), so that the shadow tiled
 // layer buffer can correctly reflect the updates of the master layer buffer.
 //
 // The associated Tile may be of any type as long as the derived class can
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -350,25 +350,31 @@ CompositorParent::TransformShadowTree()
   const FrameMetrics* metrics = &container->GetFrameMetrics();
   const gfx3DMatrix& rootTransform = mLayerManager->GetRoot()->GetTransform();
   const gfx3DMatrix& currentTransform = layer->GetTransform();
 
   float rootScaleX = rootTransform.GetXScale();
   float rootScaleY = rootTransform.GetYScale();
 
   if (mIsFirstPaint && metrics) {
-    mContentRect = metrics->mContentRect;
-    SetFirstPaintViewport(metrics->mViewportScrollOffset,
+    nsIntPoint scrollOffset = metrics->mViewportScrollOffset;
+    mContentSize = metrics->mContentSize;
+    SetFirstPaintViewport(scrollOffset.x, scrollOffset.y,
                           1/rootScaleX,
-                          mContentRect,
-                          metrics->mCSSContentRect);
+                          mContentSize.width,
+                          mContentSize.height,
+                          metrics->mCSSContentSize.width,
+                          metrics->mCSSContentSize.height);
     mIsFirstPaint = false;
-  } else if (metrics && !metrics->mContentRect.IsEqualEdges(mContentRect)) {
-    mContentRect = metrics->mContentRect;
-    SetPageRect(1/rootScaleX, mContentRect, metrics->mCSSContentRect);
+  } else if (metrics && (metrics->mContentSize != mContentSize)) {
+    mContentSize = metrics->mContentSize;
+    SetPageSize(1/rootScaleX, mContentSize.width,
+                mContentSize.height,
+                metrics->mCSSContentSize.width,
+                metrics->mCSSContentSize.height);
   }
 
   // We synchronise the viewport information with Java after sending the above
   // notifications, so that Java can take these into account in its response.
   if (metrics) {
     // Calculate the absolute display port to send to Java
     nsIntRect displayPort = metrics->mDisplayPort;
     nsIntPoint scrollOffset = metrics->mViewportScrollOffset;
@@ -410,29 +416,34 @@ CompositorParent::TransformShadowTree()
     TranslateFixedLayers(layer, reverseViewTranslation);
   } else {
     ViewTransform treeTransform(nsIntPoint(0,0), mXScale, mYScale);
     shadow->SetShadowTransform(gfx3DMatrix(treeTransform) * currentTransform);
   }
 }
 
 void
-CompositorParent::SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom,
-                                        const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect)
+CompositorParent::SetFirstPaintViewport(float aOffsetX, float aOffsetY, float aZoom,
+                                        float aPageWidth, float aPageHeight,
+                                        float aCssPageWidth, float aCssPageHeight)
 {
 #ifdef MOZ_WIDGET_ANDROID
-  mozilla::AndroidBridge::Bridge()->SetFirstPaintViewport(aOffset, aZoom, aPageRect, aCssPageRect);
+  mozilla::AndroidBridge::Bridge()->SetFirstPaintViewport(aOffsetX, aOffsetY,
+                                                          aZoom, aPageWidth, aPageHeight,
+                                                          aCssPageWidth, aCssPageHeight);
 #endif
 }
 
 void
-CompositorParent::SetPageRect(float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect)
+CompositorParent::SetPageSize(float aZoom, float aPageWidth, float aPageHeight,
+                              float aCssPageWidth, float aCssPageHeight)
 {
 #ifdef MOZ_WIDGET_ANDROID
-  mozilla::AndroidBridge::Bridge()->SetPageRect(aZoom, aPageRect, aCssPageRect);
+  mozilla::AndroidBridge::Bridge()->SetPageSize(aZoom, aPageWidth, aPageHeight,
+                                                aCssPageWidth, aCssPageHeight);
 #endif
 }
 
 void
 CompositorParent::SyncViewportInfo(const nsIntRect& aDisplayPort,
                                    float aDisplayResolution, bool aLayersUpdated,
                                    nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY)
 {
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -78,18 +78,19 @@ public:
   void ScheduleResumeOnCompositorThread(int width, int height);
 
 protected:
   virtual PLayersParent* AllocPLayers(const LayersBackend& aBackendType, int* aMaxTextureSize);
   virtual bool DeallocPLayers(PLayersParent* aLayers);
   virtual void ScheduleTask(CancelableTask*, int);
   virtual void Composite();
   virtual void ScheduleComposition();
-  virtual void SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect);
-  virtual void SetPageRect(float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect);
+  virtual void SetFirstPaintViewport(float aOffsetX, float aOffsetY, float aZoom, float aPageWidth, float aPageHeight,
+                                     float aCssPageWidth, float aCssPageHeight);
+  virtual void SetPageSize(float aZoom, float aPageWidth, float aPageHeight, float aCssPageWidth, float aCssPageHeight);
   virtual void SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated,
                                 nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY);
   void SetEGLSurfaceSize(int width, int height);
 
 private:
   void PauseComposition();
   void ResumeComposition();
   void ResumeCompositionAndResize(int width, int height);
@@ -119,17 +120,17 @@ private:
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   TimeStamp mExpectedComposeTime;
 #endif
 
   bool mPaused;
   float mXScale;
   float mYScale;
   nsIntPoint mScrollOffset;
-  nsIntRect mContentRect;
+  nsIntSize mContentSize;
   nsIntSize mWidgetSize;
 
   // When this flag is set, the next composition will be the first for a
   // particular document (i.e. the document displayed on the screen will change).
   // This happens when loading a new page or switching tabs. We notify the
   // front-end (e.g. Java on Android) about this so that it take the new page
   // size and zoom into account when providing us with the next view transform.
   bool mIsFirstPaint;
--- a/gfx/layers/ipc/ShadowLayerUtils.h
+++ b/gfx/layers/ipc/ShadowLayerUtils.h
@@ -29,30 +29,30 @@ namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::layers::FrameMetrics>
 {
   typedef mozilla::layers::FrameMetrics paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
-    WriteParam(aMsg, aParam.mCSSContentRect);
+    WriteParam(aMsg, aParam.mCSSContentSize);
     WriteParam(aMsg, aParam.mViewport);
-    WriteParam(aMsg, aParam.mContentRect);
+    WriteParam(aMsg, aParam.mContentSize);
     WriteParam(aMsg, aParam.mViewportScrollOffset);
     WriteParam(aMsg, aParam.mDisplayPort);
     WriteParam(aMsg, aParam.mScrollId);
     WriteParam(aMsg, aParam.mResolution);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
-    return (ReadParam(aMsg, aIter, &aResult->mCSSContentRect) &&
+    return (ReadParam(aMsg, aIter, &aResult->mCSSContentSize) &&
             ReadParam(aMsg, aIter, &aResult->mViewport) &&
-            ReadParam(aMsg, aIter, &aResult->mContentRect) &&
+            ReadParam(aMsg, aIter, &aResult->mContentSize) &&
             ReadParam(aMsg, aIter, &aResult->mViewportScrollOffset) &&
             ReadParam(aMsg, aIter, &aResult->mDisplayPort) &&
             ReadParam(aMsg, aIter, &aResult->mScrollId) &&
             ReadParam(aMsg, aIter, &aResult->mResolution));
   }
 };
 
 #if !defined(MOZ_HAVE_SURFACEDESCRIPTORX11)
--- a/gfx/layers/opengl/ReusableTileStoreOGL.cpp
+++ b/gfx/layers/opengl/ReusableTileStoreOGL.cpp
@@ -214,19 +214,19 @@ ReusableTileStoreOGL::DrawTiles(TiledThe
   for (ContainerLayer* parent = aLayer->GetParent(); parent; parent = parent->GetParent()) {
       const FrameMetrics& parentMetrics = parent->GetFrameMetrics();
       if (parentMetrics.IsScrollable())
         scrollableLayer = parent;
       if (!parentMetrics.mDisplayPort.IsEmpty() && scrollableLayer) {
           displayPort = parent->GetEffectiveTransform().
             TransformBounds(gfxRect(parentMetrics.mDisplayPort));
           const FrameMetrics& metrics = scrollableLayer->GetFrameMetrics();
-          const nsIntSize& contentSize = metrics.mContentRect.Size();
-          const nsIntPoint& contentOrigin = metrics.mContentRect.TopLeft() - metrics.mViewportScrollOffset;
-          gfxRect contentRect = gfxRect(contentOrigin.x, contentOrigin.y,
+          const nsIntSize& contentSize = metrics.mContentSize;
+          const nsIntPoint& contentOrigin = metrics.mViewportScrollOffset;
+          gfxRect contentRect = gfxRect(-contentOrigin.x, -contentOrigin.y,
                                         contentSize.width, contentSize.height);
           contentBounds = scrollableLayer->GetEffectiveTransform().TransformBounds(contentRect);
           break;
       }
   }
 
   // Render old tiles to fill in gaps we haven't had the time to render yet.
   for (PRUint32 i = 0; i < mTiles.Length(); i++) {
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -755,37 +755,16 @@ struct ParamTraits<mozilla::gfx::Size>
 
   static bool Read(const Message* msg, void** iter, paramType* result)
   {
     return (ReadParam(msg, iter, &result->width) &&
             ReadParam(msg, iter, &result->height));
   }
 };
 
-template<>
-struct ParamTraits<mozilla::gfx::Rect>
-{
-  typedef mozilla::gfx::Rect paramType;
-
-  static void Write(Message* msg, const paramType& param)
-  {
-    WriteParam(msg, param.x);
-    WriteParam(msg, param.y);
-    WriteParam(msg, param.width);
-    WriteParam(msg, param.height);
-  }
-
-  static bool Read(const Message* msg, void** iter, paramType* result)
-  {
-    return (ReadParam(msg, iter, &result->x) &&
-            ReadParam(msg, iter, &result->y) &&
-            ReadParam(msg, iter, &result->width) &&
-            ReadParam(msg, iter, &result->height));
-  }
-};
 
 template<>
 struct ParamTraits<nsRect>
 {
   typedef nsRect paramType;
   
   static void Write(Message* msg, const paramType& param)
   {
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -194,35 +194,31 @@ static void RecordFrameMetrics(nsIFrame*
       aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
   }
 
   nsIScrollableFrame* scrollableFrame = nsnull;
   if (aScrollFrame)
     scrollableFrame = aScrollFrame->GetScrollTargetFrame();
 
   if (scrollableFrame) {
-    nsRect contentBounds = scrollableFrame->GetScrollRange();
-    contentBounds.width += scrollableFrame->GetScrollPortRect().width;
-    contentBounds.height += scrollableFrame->GetScrollPortRect().height;
-    metrics.mCSSContentRect = gfx::Rect(nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.x),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.y),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.width),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.height));
-    metrics.mContentRect = contentBounds.ScaleToNearestPixels(
+    nsSize contentSize =
+      scrollableFrame->GetScrollRange().Size() +
+      scrollableFrame->GetScrollPortRect().Size();
+    metrics.mCSSContentSize = gfx::Size(nsPresContext::AppUnitsToFloatCSSPixels(contentSize.width),
+                                        nsPresContext::AppUnitsToFloatCSSPixels(contentSize.height));
+    metrics.mContentSize = contentSize.ScaleToNearestPixels(
       aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
     metrics.mViewportScrollOffset = scrollableFrame->GetScrollPosition().ScaleToNearestPixels(
       aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
   }
   else {
-    nsRect contentBounds = aForFrame->GetRect();
-    metrics.mCSSContentRect = gfx::Rect(nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.x),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.y),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.width),
-                                        nsPresContext::AppUnitsToFloatCSSPixels(contentBounds.height));
-    metrics.mContentRect = contentBounds.ScaleToNearestPixels(
+    nsSize contentSize = aForFrame->GetSize();
+    metrics.mCSSContentSize = gfx::Size(nsPresContext::AppUnitsToFloatCSSPixels(contentSize.width),
+                                        nsPresContext::AppUnitsToFloatCSSPixels(contentSize.height));
+    metrics.mContentSize = contentSize.ScaleToNearestPixels(
       aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
   }
 
   metrics.mScrollId = aScrollId;
 
   nsIPresShell* presShell = presContext->GetPresShell();
   metrics.mResolution = gfxSize(presShell->GetXResolution(), presShell->GetYResolution());
 
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -379,18 +379,18 @@ BuildViewMap(ViewMap& oldContentViews, V
       view->mParentScaleX = aAccConfigXScale;
       view->mParentScaleY = aAccConfigYScale;
     }
 
     view->mViewportSize = nsSize(
       NSIntPixelsToAppUnits(metrics.mViewport.width, auPerDevPixel) * aXScale,
       NSIntPixelsToAppUnits(metrics.mViewport.height, auPerDevPixel) * aYScale);
     view->mContentSize = nsSize(
-      nsPresContext::CSSPixelsToAppUnits(metrics.mCSSContentRect.width) * aXScale,
-      nsPresContext::CSSPixelsToAppUnits(metrics.mCSSContentRect.height) * aYScale);
+      nsPresContext::CSSPixelsToAppUnits(metrics.mCSSContentSize.width) * aXScale,
+      nsPresContext::CSSPixelsToAppUnits(metrics.mCSSContentSize.height) * aYScale);
 
     newContentViews[scrollId] = view;
   }
 
   for (Layer* child = aLayer->GetFirstChild();
        child; child = child->GetNextSibling()) {
     BuildViewMap(oldContentViews, newContentViews, aFrameLoader, child,
                  aXScale, aYScale, aAccConfigXScale, aAccConfigYScale);
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -2165,17 +2165,27 @@ public class GeckoAppShell
                 mDirtyTop = Float.POSITIVE_INFINITY;
                 mDirtyLeft = Float.POSITIVE_INFINITY;
                 mDirtyBottom = Float.NEGATIVE_INFINITY;
                 mDirtyRight = Float.NEGATIVE_INFINITY;
                 mIsRepaintRunnablePosted = false;
             }
 
             Tab tab = Tabs.getInstance().getSelectedTab();
-            GeckoAppShell.screenshotWholePage(tab);
+            ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
+            /*
+            if (FloatUtils.fuzzyEquals(sCheckerboardPageWidth, viewport.pageSizeWidth) &&
+                FloatUtils.fuzzyEquals(sCheckerboardPageHeight, viewport.pageSizeHeight)) {
+                float width = right - left;
+                float height = bottom - top;
+                GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), (int)top, (int)left, (int)width, (int)height, 0, 0, (int)(sLastCheckerboardWidthRatio * width), (int)(sLastCheckerboardHeightRatio * height), GeckoAppShell.SCREENSHOT_UPDATE));
+            } else {
+            */
+                GeckoAppShell.screenshotWholePage(tab);
+            //}
         }
 
         void addRectToRepaint(float top, float left, float bottom, float right) {
             synchronized(this) {
                 mDirtyTop = Math.min(top, mDirtyTop);
                 mDirtyLeft = Math.min(left, mDirtyLeft);
                 mDirtyBottom = Math.max(bottom, mDirtyBottom);
                 mDirtyRight = Math.max(right, mDirtyRight);
@@ -2213,18 +2223,18 @@ public class GeckoAppShell
             GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
             sMaxTextureSize = maxTextureSize[0];
             if (sMaxTextureSize == 0)
                 return;
         }
         ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
         Log.i(LOGTAG, "Taking whole-screen screenshot, viewport: " + viewport);
         // source width and height to screenshot
-        float sw = viewport.getPageWidth() / viewport.zoomFactor;
-        float sh = viewport.getPageHeight() / viewport.zoomFactor;
+        float sw = viewport.pageSizeWidth / viewport.zoomFactor;
+        float sh = viewport.pageSizeHeight / viewport.zoomFactor;
         int maxPixels = Math.min(ScreenshotLayer.getMaxNumPixels(), sMaxTextureSize * sMaxTextureSize);
         // 2Mb of 16bit image data
         // may be bumped by up to 4x for power of 2 alignment
         float idealZoomFactor = (float)Math.sqrt((sw * sh) / (maxPixels / 4));
 
         // calc destination width and hight
         int idealDstWidth = IntSize.nextPowerOfTwo(sw / idealZoomFactor);
         // min texture size such that the other dimention doesn't excede the max
@@ -2232,14 +2242,11 @@ public class GeckoAppShell
         int dw = clamp(minTextureSize, idealDstWidth, sMaxTextureSize);
         int dh = maxPixels / dw;
 
         sLastCheckerboardWidthRatio = dw / sw;
         sLastCheckerboardHeightRatio = dh / sh;
         sCheckerboardPageWidth = sw;
         sCheckerboardPageHeight = sh;
 
-        GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(),
-                (int)FloatMath.ceil(viewport.pageRectLeft), (int)FloatMath.ceil(viewport.pageRectTop),
-                (int)FloatMath.floor(viewport.getPageWidth()), (int)FloatMath.floor(viewport.getPageHeight()),
-                0, 0,  dw, dh, GeckoAppShell.SCREENSHOT_WHOLE_PAGE));
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenshotEvent(tab.getId(), 0, 0, (int)sw, (int)sh, 0, 0,  dw, dh, GeckoAppShell.SCREENSHOT_WHOLE_PAGE));
     }
 }
--- a/mobile/android/base/gfx/DisplayPortCalculator.java
+++ b/mobile/android/base/gfx/DisplayPortCalculator.java
@@ -124,75 +124,79 @@ final class DisplayPortCalculator {
     /**
      * Return the dimensions for a rect that has area (width*height) that does not exceed the page size in the
      * given metrics object. The area in the returned FloatSize may be less than width*height if the page is
      * small, but it will never be larger than width*height.
      * Note that this process may change the relative aspect ratio of the given dimensions.
      */
     private static FloatSize reshapeForPage(float width, float height, ImmutableViewportMetrics metrics) {
         // figure out how much of the desired buffer amount we can actually use on the horizontal axis
-        float usableWidth = Math.min(width, metrics.getPageWidth());
+        float usableWidth = Math.min(width, metrics.pageSizeWidth);
         // if we reduced the buffer amount on the horizontal axis, we should take that saved memory and
         // use it on the vertical axis
         float extraUsableHeight = (float)Math.floor(((width - usableWidth) * height) / usableWidth);
-        float usableHeight = Math.min(height + extraUsableHeight, metrics.getPageHeight());
+        float usableHeight = Math.min(height + extraUsableHeight, metrics.pageSizeHeight);
         if (usableHeight < height && usableWidth == width) {
             // and the reverse - if we shrunk the buffer on the vertical axis we can add it to the horizontal
             float extraUsableWidth = (float)Math.floor(((height - usableHeight) * width) / usableHeight);
-            usableWidth = Math.min(width + extraUsableWidth, metrics.getPageWidth());
+            usableWidth = Math.min(width + extraUsableWidth, metrics.pageSizeWidth);
         }
         return new FloatSize(usableWidth, usableHeight);
     }
 
     /**
      * Expand the given rect in all directions by a "danger zone". The size of the danger zone on an axis
      * is the size of the view on that axis multiplied by the given multiplier. The expanded rect is then
      * clamped to page bounds and returned.
      */
     private static RectF expandByDangerZone(RectF rect, float dangerZoneXMultiplier, float dangerZoneYMultiplier, ImmutableViewportMetrics metrics) {
         // calculate the danger zone amounts in pixels
         float dangerZoneX = metrics.getWidth() * dangerZoneXMultiplier;
         float dangerZoneY = metrics.getHeight() * dangerZoneYMultiplier;
         rect = RectUtils.expand(rect, dangerZoneX, dangerZoneY);
         // clamp to page bounds
-        return clampToPageBounds(rect, metrics);
+        if (rect.top < 0) rect.top = 0;
+        if (rect.left < 0) rect.left = 0;
+        if (rect.right > metrics.pageSizeWidth) rect.right = metrics.pageSizeWidth;
+        if (rect.bottom > metrics.pageSizeHeight) rect.bottom = metrics.pageSizeHeight;
+        return rect;
     }
 
     /**
      * Expand the given margins such that when they are applied on the viewport, the resulting rect
      * does not have any partial tiles, except when it is clipped by the page bounds. This assumes
      * the tiles are TILE_SIZE by TILE_SIZE and start at the origin, such that there will always be
      * a tile at (0,0)-(TILE_SIZE,TILE_SIZE)).
      */
     private static DisplayPortMetrics getTileAlignedDisplayPortMetrics(RectF margins, float zoom, ImmutableViewportMetrics metrics) {
         float left = metrics.viewportRectLeft - margins.left;
         float top = metrics.viewportRectTop - margins.top;
         float right = metrics.viewportRectRight + margins.right;
         float bottom = metrics.viewportRectBottom + margins.bottom;
-        left = Math.max(metrics.pageRectLeft, TILE_SIZE * FloatMath.floor(left / TILE_SIZE));
-        top = Math.max(metrics.pageRectTop, TILE_SIZE * FloatMath.floor(top / TILE_SIZE));
-        right = Math.min(metrics.pageRectRight, TILE_SIZE * FloatMath.ceil(right / TILE_SIZE));
-        bottom = Math.min(metrics.pageRectBottom, TILE_SIZE * FloatMath.ceil(bottom / TILE_SIZE));
+        left = Math.max(0.0f, TILE_SIZE * FloatMath.floor(left / TILE_SIZE));
+        top = Math.max(0.0f, TILE_SIZE * FloatMath.floor(top / TILE_SIZE));
+        right = Math.min(metrics.pageSizeWidth, TILE_SIZE * FloatMath.ceil(right / TILE_SIZE));
+        bottom = Math.min(metrics.pageSizeHeight, TILE_SIZE * FloatMath.ceil(bottom / TILE_SIZE));
         return new DisplayPortMetrics(left, top, right, bottom, zoom);
     }
 
     /**
      * Adjust the given margins so if they are applied on the viewport in the metrics, the resulting rect
      * does not exceed the page bounds. This code will maintain the total margin amount for a given axis;
      * it assumes that margins.left + metrics.getWidth() + margins.right is less than or equal to
-     * metrics.getPageWidth(); and the same for the y axis.
+     * metrics.pageSizeWidth; and the same for the y axis.
      */
     private static RectF shiftMarginsForPageBounds(RectF margins, ImmutableViewportMetrics metrics) {
         // check how much we're overflowing in each direction. note that at most one of leftOverflow
         // and rightOverflow can be greater than zero, and at most one of topOverflow and bottomOverflow
         // can be greater than zero, because of the assumption described in the method javadoc.
-        float leftOverflow = metrics.pageRectLeft - (metrics.viewportRectLeft - margins.left);
-        float rightOverflow = (metrics.viewportRectRight + margins.right) - metrics.pageRectRight;
-        float topOverflow = metrics.pageRectTop - (metrics.viewportRectTop - margins.top);
-        float bottomOverflow = (metrics.viewportRectBottom + margins.bottom) - metrics.pageRectBottom;
+        float leftOverflow = margins.left - metrics.viewportRectLeft;
+        float rightOverflow = margins.right - (metrics.pageSizeWidth - metrics.viewportRectRight);
+        float topOverflow = margins.top - metrics.viewportRectTop;
+        float bottomOverflow = margins.bottom - (metrics.pageSizeHeight - metrics.viewportRectBottom);
 
         // if the margins overflow the page bounds, shift them to other side on the same axis
         if (leftOverflow > 0) {
             margins.left -= leftOverflow;
             margins.right += leftOverflow;
         } else if (rightOverflow > 0) {
             margins.right -= rightOverflow;
             margins.left += rightOverflow;
@@ -206,20 +210,20 @@ final class DisplayPortCalculator {
         }
         return margins;
     }
 
     /**
      * Clamp the given rect to the page bounds and return it.
      */
     private static RectF clampToPageBounds(RectF rect, ImmutableViewportMetrics metrics) {
-        if (rect.top < metrics.pageRectTop) rect.top = metrics.pageRectTop;
-        if (rect.left < metrics.pageRectLeft) rect.left = metrics.pageRectLeft;
-        if (rect.right > metrics.pageRectRight) rect.right = metrics.pageRectRight;
-        if (rect.bottom > metrics.pageRectBottom) rect.bottom = metrics.pageRectBottom;
+        rect.left = Math.max(rect.left, 0);
+        rect.top = Math.max(rect.top, 0);
+        rect.right = Math.min(rect.right, metrics.pageSizeWidth);
+        rect.bottom = Math.min(rect.bottom, metrics.pageSizeHeight);
         return rect;
     }
 
     /**
      * This class implements the variation where we basically don't bother with a a display port.
      */
     private static class NoMarginStrategy extends DisplayPortStrategy {
         NoMarginStrategy(Map<String, Integer> prefs) {
@@ -386,18 +390,18 @@ final class DisplayPortCalculator {
             if (Math.abs(velocity.x) > VELOCITY_THRESHOLD && FloatUtils.fuzzyEquals(velocity.y, 0)) {
                 displayPortHeight = metrics.getHeight();
             } else if (Math.abs(velocity.y) > VELOCITY_THRESHOLD && FloatUtils.fuzzyEquals(velocity.x, 0)) {
                 displayPortWidth = metrics.getWidth();
             }
 
             // 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).
-            displayPortWidth = Math.min(displayPortWidth, metrics.getPageWidth());
-            displayPortHeight = Math.min(displayPortHeight, metrics.getPageHeight());
+            displayPortWidth = Math.min(displayPortWidth, metrics.pageSizeWidth);
+            displayPortHeight = Math.min(displayPortHeight, metrics.pageSizeHeight);
             float horizontalBuffer = displayPortWidth - metrics.getWidth();
             float verticalBuffer = displayPortHeight - metrics.getHeight();
 
             // split the buffer amounts into margins based on velocity, and shift it to
             // take into account the page bounds
             RectF margins = velocityBiasedMargins(horizontalBuffer, verticalBuffer, velocity);
             margins = shiftMarginsForPageBounds(margins, metrics);
 
@@ -405,18 +409,18 @@ final class DisplayPortCalculator {
         }
 
         public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
             // calculate the danger zone amounts based on the prefs
             float dangerZoneX = metrics.getWidth() * (DANGER_ZONE_BASE_X_MULTIPLIER + (velocity.x * DANGER_ZONE_INCR_X_MULTIPLIER));
             float dangerZoneY = metrics.getHeight() * (DANGER_ZONE_BASE_Y_MULTIPLIER + (velocity.y * DANGER_ZONE_INCR_Y_MULTIPLIER));
             // clamp it such that when added to the viewport, they don't exceed page size.
             // this is a prerequisite to calling shiftMarginsForPageBounds as we do below.
-            dangerZoneX = Math.min(dangerZoneX, metrics.getPageWidth() - metrics.getWidth());
-            dangerZoneY = Math.min(dangerZoneY, metrics.getPageHeight() - metrics.getHeight());
+            dangerZoneX = Math.min(dangerZoneX, metrics.pageSizeWidth - metrics.getWidth());
+            dangerZoneY = Math.min(dangerZoneY, metrics.pageSizeHeight - metrics.getHeight());
 
             // split the danger zone into margins based on velocity, and ensure it doesn't exceed
             // page bounds.
             RectF dangerMargins = velocityBiasedMargins(dangerZoneX, dangerZoneY, velocity);
             dangerMargins = shiftMarginsForPageBounds(dangerMargins, metrics);
 
             // we're about to checkerboard if the current viewport area + the danger zone margins
             // fall out of the current displayport anywhere.
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -199,17 +199,17 @@ public class GeckoLayerClient implements
                 mLayerController.abortPanZoomAnimation();
                 break;
             case PAGE_SIZE:
                 // adjust the page dimensions to account for differences in zoom
                 // between the rendered content (which is what Gecko tells us)
                 // and our zoom level (which may have diverged).
                 float scaleFactor = oldMetrics.zoomFactor / messageMetrics.getZoomFactor();
                 newMetrics = new ViewportMetrics(oldMetrics);
-                newMetrics.setPageRect(RectUtils.scale(messageMetrics.getPageRect(), scaleFactor), messageMetrics.getCssPageRect());
+                newMetrics.setPageSize(messageMetrics.getPageSize().scale(scaleFactor), messageMetrics.getCssPageSize());
                 break;
             }
 
             mLayerController.post(new Runnable() {
                 public void run() {
                     mGeckoViewport = newMetrics;
                 }
             });
@@ -293,28 +293,25 @@ public class GeckoLayerClient implements
     public ViewportMetrics getGeckoViewportMetrics() {
         return mGeckoViewport;
     }
 
     /** This function is invoked by Gecko via JNI; be careful when modifying signature.
       * The compositor invokes this function just before compositing a frame where the document
       * is different from the document composited on the last frame. In these cases, the viewport
       * information we have in Java is no longer valid and needs to be replaced with the new
-      * viewport information provided. setPageRect will never be invoked on the same frame that
+      * viewport information provided. setPageSize will never be invoked on the same frame that
       * this function is invoked on; and this function will always be called prior to syncViewportInfo.
       */
-    public void setFirstPaintViewport(float offsetX, float offsetY, float zoom,
-            float pageLeft, float pageTop, float pageRight, float pageBottom,
-            float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) {
+    public void setFirstPaintViewport(float offsetX, float offsetY, float zoom, float pageWidth, float pageHeight, float cssPageWidth, float cssPageHeight) {
         synchronized (mLayerController) {
             final ViewportMetrics currentMetrics = new ViewportMetrics(mLayerController.getViewportMetrics());
             currentMetrics.setOrigin(new PointF(offsetX, offsetY));
             currentMetrics.setZoomFactor(zoom);
-            currentMetrics.setPageRect(new RectF(pageLeft, pageTop, pageRight, pageBottom),
-                                       new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom));
+            currentMetrics.setPageSize(new FloatSize(pageWidth, pageHeight), new FloatSize(cssPageWidth, cssPageHeight));
             // Since we have switched to displaying a different document, we need to update any
             // viewport-related state we have lying around. This includes mGeckoViewport and the
             // viewport in mLayerController. Usually this information is updated via handleViewportMessage
             // while we remain on the same document.
             mLayerController.post(new Runnable() {
                 public void run() {
                     mGeckoViewport = currentMetrics;
                 }
@@ -338,31 +335,30 @@ public class GeckoLayerClient implements
         }
         DisplayPortCalculator.resetPageState();
         mDrawTimingQueue.reset();
         mLayerController.getView().getRenderer().resetCheckerboard();
         GeckoAppShell.screenshotWholePage(Tabs.getInstance().getSelectedTab());
     }
 
     /** This function is invoked by Gecko via JNI; be careful when modifying signature.
-      * The compositor invokes this function whenever it determines that the page rect
+      * The compositor invokes this function whenever it determines that the page size
       * has changed (based on the information it gets from layout). If setFirstPaintViewport
       * is invoked on a frame, then this function will not be. For any given frame, this
       * function will be invoked before syncViewportInfo.
       */
-    public void setPageRect(float zoom, float pageLeft, float pageTop, float pageRight, float pageBottom,
-            float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) {
+    public void setPageSize(float zoom, float pageWidth, float pageHeight, float cssPageWidth, float cssPageHeight) {
         synchronized (mLayerController) {
             // adjust the page dimensions to account for differences in zoom
             // between the rendered content (which is what the compositor tells us)
             // and our zoom level (which may have diverged).
-            RectF pageRect = new RectF(pageLeft, pageTop, pageRight, pageBottom);
-            RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
             float ourZoom = mLayerController.getZoomFactor();
-            mLayerController.setPageRect(RectUtils.scale(pageRect, ourZoom / zoom), cssPageRect);
+            pageWidth = pageWidth * ourZoom / zoom;
+            pageHeight = pageHeight * ourZoom /zoom;
+            mLayerController.setPageSize(new FloatSize(pageWidth, pageHeight), new FloatSize(cssPageWidth, cssPageHeight));
             // Here the page size of the document has changed, but the document being displayed
             // is still the same. Therefore, we don't need to send anything to browser.js; any
             // changes we need to make to the display port will get sent the next time we call
             // adjustViewport().
         }
     }
 
     /** This function is invoked by Gecko via JNI; be careful when modifying signature.
--- a/mobile/android/base/gfx/ImmutableViewportMetrics.java
+++ b/mobile/android/base/gfx/ImmutableViewportMetrics.java
@@ -12,48 +12,40 @@ import android.graphics.RectF;
  * ImmutableViewportMetrics are used to store the viewport metrics
  * in way that we can access a version of them from multiple threads
  * without having to take a lock
  */
 public class ImmutableViewportMetrics {
 
     // We need to flatten the RectF and FloatSize structures
     // because Java doesn't have the concept of const classes
-    public final float pageRectLeft;
-    public final float pageRectTop;
-    public final float pageRectRight;
-    public final float pageRectBottom;
-    public final float cssPageRectLeft;
-    public final float cssPageRectTop;
-    public final float cssPageRectRight;
-    public final float cssPageRectBottom;
+    public final float pageSizeWidth;
+    public final float pageSizeHeight;
+    public final float cssPageSizeWidth;
+    public final float cssPageSizeHeight;
+    public final float viewportRectBottom;
     public final float viewportRectLeft;
+    public final float viewportRectRight;
     public final float viewportRectTop;
-    public final float viewportRectRight;
-    public final float viewportRectBottom;
     public final float zoomFactor;
 
     public ImmutableViewportMetrics(ViewportMetrics m) {
         RectF viewportRect = m.getViewport();
+        viewportRectBottom = viewportRect.bottom;
         viewportRectLeft = viewportRect.left;
+        viewportRectRight = viewportRect.right;
         viewportRectTop = viewportRect.top;
-        viewportRectRight = viewportRect.right;
-        viewportRectBottom = viewportRect.bottom;
 
-        RectF pageRect = m.getPageRect();
-        pageRectLeft = pageRect.left;
-        pageRectTop = pageRect.top;
-        pageRectRight = pageRect.right;
-        pageRectBottom = pageRect.bottom;
+        FloatSize pageSize = m.getPageSize();
+        pageSizeWidth = pageSize.width;
+        pageSizeHeight = pageSize.height;
 
-        RectF cssPageRect = m.getCssPageRect();
-        cssPageRectLeft = cssPageRect.left;
-        cssPageRectTop = cssPageRect.top;
-        cssPageRectRight = cssPageRect.right;
-        cssPageRectBottom = cssPageRect.bottom;
+        FloatSize cssPageSize = m.getCssPageSize();
+        cssPageSizeWidth = cssPageSize.width;
+        cssPageSizeHeight = cssPageSize.height;
 
         zoomFactor = m.getZoomFactor();
     }
 
     public float getWidth() {
         return viewportRectRight - viewportRectLeft;
     }
 
@@ -77,33 +69,23 @@ public class ImmutableViewportMetrics {
                          viewportRectRight,
                          viewportRectBottom);
     }
 
     public RectF getCssViewport() {
         return RectUtils.scale(getViewport(), 1/zoomFactor);
     }
 
-    public RectF getPageRect() {
-        return new RectF(pageRectLeft, pageRectTop, pageRectRight, pageRectBottom);
+    public FloatSize getPageSize() {
+        return new FloatSize(pageSizeWidth, pageSizeHeight);
     }
 
-    public float getPageWidth() {
-        return pageRectRight - pageRectLeft;
-    }
-
-    public float getPageHeight() {
-        return pageRectBottom - pageRectTop;
-    }
-
-    public RectF getCssPageRect() {
-        return new RectF(cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom);
+    public FloatSize getCssPageSize() {
+        return new FloatSize(cssPageSizeWidth, cssPageSizeHeight);
     }
 
     @Override
     public String toString() {
         return "ImmutableViewportMetrics v=(" + viewportRectLeft + "," + viewportRectTop + ","
-                + viewportRectRight + "," + viewportRectBottom + ") p=(" + pageRectLeft + ","
-                + pageRectTop + "," + pageRectRight + "," + pageRectBottom + ") c=("
-                + cssPageRectLeft + "," + cssPageRectTop + "," + cssPageRectRight + ","
-                + cssPageRectBottom + ") z=" + zoomFactor;
+                + viewportRectRight + "," + viewportRectBottom + ") p=(" + pageSizeWidth + ","
+                + pageSizeHeight + ") z=" + zoomFactor;
     }
 }
--- a/mobile/android/base/gfx/Layer.java
+++ b/mobile/android/base/gfx/Layer.java
@@ -150,37 +150,37 @@ public abstract class Layer {
         if (mNewResolution != 0.0f) {
             mResolution = mNewResolution;
             mNewResolution = 0.0f;
         }
     }
 
     public static class RenderContext {
         public final RectF viewport;
-        public final RectF pageRect;
+        public final FloatSize pageSize;
         public final IntSize screenSize;
         public final float zoomFactor;
         public final int positionHandle;
         public final int textureHandle;
         public final FloatBuffer coordBuffer;
 
-        public RenderContext(RectF aViewport, RectF aPageRect, IntSize aScreenSize, float aZoomFactor,
+        public RenderContext(RectF aViewport, FloatSize aPageSize, IntSize aScreenSize, float aZoomFactor,
                              int aPositionHandle, int aTextureHandle, FloatBuffer aCoordBuffer) {
             viewport = aViewport;
-            pageRect = aPageRect;
+            pageSize = aPageSize;
             screenSize = aScreenSize;
             zoomFactor = aZoomFactor;
             positionHandle = aPositionHandle;
             textureHandle = aTextureHandle;
             coordBuffer = aCoordBuffer;
         }
 
         public boolean fuzzyEquals(RenderContext other) {
             if (other == null) {
                 return false;
             }
             return RectUtils.fuzzyEquals(viewport, other.viewport)
-                && RectUtils.fuzzyEquals(pageRect, other.pageRect)
+                && pageSize.fuzzyEquals(other.pageSize)
                 && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor);
         }
     }
 }
 
--- a/mobile/android/base/gfx/LayerController.java
+++ b/mobile/android/base/gfx/LayerController.java
@@ -98,22 +98,22 @@ public class LayerController {
     public RectF getCssViewport() {
         return mViewportMetrics.getCssViewport();
     }
 
     public FloatSize getViewportSize() {
         return mViewportMetrics.getSize();
     }
 
-    public RectF getPageRect() {
-        return mViewportMetrics.getPageRect();
+    public FloatSize getPageSize() {
+        return mViewportMetrics.getPageSize();
     }
 
-    public RectF getCssPageRect() {
-        return mViewportMetrics.getCssPageRect();
+    public FloatSize getCssPageSize() {
+        return mViewportMetrics.getCssPageSize();
     }
 
     public PointF getOrigin() {
         return mViewportMetrics.getOrigin();
     }
 
     public float getZoomFactor() {
         return mViewportMetrics.zoomFactor;
@@ -162,34 +162,31 @@ public class LayerController {
         origin.offset(point.x, point.y);
         viewportMetrics.setOrigin(origin);
         mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
 
         notifyLayerClientOfGeometryChange();
         mView.requestRender();
     }
 
-    /** Sets the current page rect. You must hold the monitor while calling this. */
-    public void setPageRect(RectF rect, RectF cssRect) {
-        // Since the "rect" is always just a multiple of "cssRect" we don't need to
-        // check both; this function assumes that both "rect" and "cssRect" are relative
-        // the zoom factor in mViewportMetrics.
-        if (mViewportMetrics.getCssPageRect().equals(cssRect))
+    /** Sets the current page size. You must hold the monitor while calling this. */
+    public void setPageSize(FloatSize size, FloatSize cssSize) {
+        if (mViewportMetrics.getCssPageSize().equals(cssSize))
             return;
 
         ViewportMetrics viewportMetrics = new ViewportMetrics(mViewportMetrics);
-        viewportMetrics.setPageRect(rect, cssRect);
+        viewportMetrics.setPageSize(size, cssSize);
         mViewportMetrics = new ImmutableViewportMetrics(viewportMetrics);
 
         // Page size is owned by the layer client, so no need to notify it of
         // this change.
 
         mView.post(new Runnable() {
             public void run() {
-                mPanZoomController.pageRectUpdated();
+                mPanZoomController.pageSizeUpdated();
                 mView.requestRender();
             }
         });
     }
 
     /**
      * Sets the entire viewport metrics at once. This function does not notify the layer client or
      * the pan/zoom controller, so you will need to call notifyLayerClientOfGeometryChange() or
--- a/mobile/android/base/gfx/LayerRenderer.java
+++ b/mobile/android/base/gfx/LayerRenderer.java
@@ -306,29 +306,29 @@ public class LayerRenderer implements GL
             }
             mPixelBuffer = null;
         }
         return pixelBuffer;
     }
 
     private RenderContext createScreenContext(ImmutableViewportMetrics metrics) {
         RectF viewport = new RectF(0.0f, 0.0f, metrics.getWidth(), metrics.getHeight());
-        RectF pageRect = new RectF(metrics.getPageRect());
-        return createContext(viewport, pageRect, 1.0f);
+        FloatSize pageSize = new FloatSize(metrics.getPageSize());
+        return createContext(viewport, pageSize, 1.0f);
     }
 
     private RenderContext createPageContext(ImmutableViewportMetrics metrics) {
         Rect viewport = RectUtils.round(metrics.getViewport());
-        RectF pageRect = metrics.getPageRect();
+        FloatSize pageSize = metrics.getPageSize();
         float zoomFactor = metrics.zoomFactor;
-        return createContext(new RectF(viewport), pageRect, zoomFactor);
+        return createContext(new RectF(viewport), pageSize, zoomFactor);
     }
 
-    private RenderContext createContext(RectF viewport, RectF pageRect, float zoomFactor) {
-        return new RenderContext(viewport, pageRect, new IntSize(mSurfaceWidth, mSurfaceHeight), zoomFactor, mPositionHandle, mTextureHandle,
+    private RenderContext createContext(RectF viewport, FloatSize pageSize, float zoomFactor) {
+        return new RenderContext(viewport, pageSize, new IntSize(mSurfaceWidth, mSurfaceHeight), zoomFactor, mPositionHandle, mTextureHandle,
                                  mCoordBuffer);
     }
 
     public void onSurfaceChanged(GL10 gl, final int width, final int height) {
         mSurfaceWidth = width;
         mSurfaceHeight = height;
 
         GLES20.glViewport(0, 0, width, height);
@@ -485,19 +485,22 @@ public class LayerRenderer implements GL
             int bottom = Math.min(screenSize.height, rect.bottom);
 
             return new Rect(left, screenSize.height - bottom, right,
                             (screenSize.height - bottom) + (bottom - top));
         }
 
         private Rect getPageRect() {
             Point origin = PointUtils.round(mFrameMetrics.getOrigin());
-            Rect pageRect = RectUtils.round(mFrameMetrics.getPageRect());
-            pageRect.offset(-origin.x, -origin.y);
-            return pageRect;
+            IntSize pageSize = new IntSize(mFrameMetrics.getPageSize());
+
+            origin.negate();
+
+            return new Rect(origin.x, origin.y,
+                            origin.x + pageSize.width, origin.y + pageSize.height);
         }
 
         /** This function is invoked via JNI; be careful when modifying signature. */
         public void beginDrawing() {
             mFrameStartTime = SystemClock.uptimeMillis();
 
             TextureReaper.get().reap();
             TextureGenerator.get().fill();
--- a/mobile/android/base/gfx/NinePatchTileLayer.java
+++ b/mobile/android/base/gfx/NinePatchTileLayer.java
@@ -47,43 +47,43 @@ public class NinePatchTileLayer extends 
          *    | 0 | 1 | 2 |
          *    +---+---+---+
          *    | 3 |   | 4 |
          *    +---+---+---+
          *    | 5 | 6 | 7 |
          *    +---+---+---+
          */
 
-        // page is the rect of the "missing" center spot in the picture above
-        RectF page = context.pageRect;
+        FloatSize size = context.pageSize;
+        float width = size.width, height = size.height;
 
         drawPatch(context, 0, PATCH_SIZE * 3,                                              /* 0 */
-                  page.left - PATCH_SIZE, page.top - PATCH_SIZE, PATCH_SIZE, PATCH_SIZE);
-        drawPatch(context, PATCH_SIZE, PATCH_SIZE * 3,                                     /* 1 */
-                  page.left, page.top - PATCH_SIZE, page.width(), PATCH_SIZE);
-        drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE * 3,                                 /* 2 */
-                  page.right, page.top - PATCH_SIZE, PATCH_SIZE, PATCH_SIZE);
+                  0.0f, 0.0f, PATCH_SIZE, PATCH_SIZE);
+        drawPatch(context, PATCH_SIZE, PATCH_SIZE*3,                                       /* 1 */
+                  PATCH_SIZE, 0.0f, width, PATCH_SIZE);
+        drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE*3,                                   /* 2 */
+                  PATCH_SIZE + width, 0.0f, PATCH_SIZE, PATCH_SIZE);
         drawPatch(context, 0, PATCH_SIZE * 2,                                              /* 3 */
-                  page.left - PATCH_SIZE, page.top, PATCH_SIZE, page.height());
+                  0.0f, PATCH_SIZE, PATCH_SIZE, height);
         drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE * 2,                                 /* 4 */
-                  page.right, page.top, PATCH_SIZE, page.height());
+                  PATCH_SIZE + width, PATCH_SIZE, PATCH_SIZE, height);
         drawPatch(context, 0, PATCH_SIZE,                                                  /* 5 */
-                  page.left - PATCH_SIZE, page.bottom, PATCH_SIZE, PATCH_SIZE);
+                  0.0f, PATCH_SIZE + height, PATCH_SIZE, PATCH_SIZE);
         drawPatch(context, PATCH_SIZE, PATCH_SIZE,                                         /* 6 */
-                  page.left, page.bottom, page.width(), PATCH_SIZE);
+                  PATCH_SIZE, PATCH_SIZE + height, width, PATCH_SIZE);
         drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE,                                     /* 7 */
-                  page.right, page.bottom, PATCH_SIZE, PATCH_SIZE);
+                  PATCH_SIZE + width, PATCH_SIZE + height, PATCH_SIZE, PATCH_SIZE);
     }
 
     private void drawPatch(RenderContext context, int textureX, int textureY,
                            float tileX, float tileY, float tileWidth, float tileHeight) {
         RectF viewport = context.viewport;
         float viewportHeight = viewport.height();
-        float drawX = tileX - viewport.left;
-        float drawY = viewportHeight - (tileY + tileHeight - viewport.top);
+        float drawX = tileX - viewport.left - PATCH_SIZE;
+        float drawY = viewportHeight - (tileY + tileHeight - viewport.top - PATCH_SIZE);
 
         float[] coords = {
             //x, y, z, texture_x, texture_y
             drawX/viewport.width(), drawY/viewport.height(), 0,
             textureX/(float)TEXTURE_SIZE, textureY/(float)TEXTURE_SIZE,
 
             drawX/viewport.width(), (drawY+tileHeight)/viewport.height(), 0,
             textureX/(float)TEXTURE_SIZE, (textureY+PATCH_SIZE)/(float)TEXTURE_SIZE,
--- a/mobile/android/base/gfx/ScrollbarLayer.java
+++ b/mobile/android/base/gfx/ScrollbarLayer.java
@@ -416,32 +416,32 @@ public class ScrollbarLayer extends Tile
 
         // Enable the default shader program again
         deactivateProgram();
         mRenderer.activateDefaultProgram();
     }
 
     private RectF getVerticalRect(RenderContext context) {
         RectF viewport = context.viewport;
-        RectF pageRect = context.pageRect;
-        float barStart = ((viewport.top - pageRect.top) * (viewport.height() / pageRect.height())) + CAP_RADIUS;
-        float barEnd = ((viewport.bottom - pageRect.top) * (viewport.height() / pageRect.height())) - CAP_RADIUS;
+        FloatSize pageSize = context.pageSize;
+        float barStart = (viewport.height() * viewport.top / pageSize.height) + CAP_RADIUS;
+        float barEnd = (viewport.height() * viewport.bottom / pageSize.height) - CAP_RADIUS;
         if (barStart > barEnd) {
             float middle = (barStart + barEnd) / 2.0f;
             barStart = barEnd = middle;
         }
         float right = viewport.width() - PADDING;
         return new RectF(right - BAR_SIZE, barStart, right, barEnd);
     }
 
     private RectF getHorizontalRect(RenderContext context) {
         RectF viewport = context.viewport;
-        RectF pageRect = context.pageRect;
-        float barStart = ((viewport.left - pageRect.left) * (viewport.width() / pageRect.width())) + CAP_RADIUS;
-        float barEnd = ((viewport.right - pageRect.left) * (viewport.width() / pageRect.width())) - CAP_RADIUS;
+        FloatSize pageSize = context.pageSize;
+        float barStart = (viewport.width() * viewport.left / pageSize.width) + CAP_RADIUS;
+        float barEnd = (viewport.width() * viewport.right / pageSize.width) - CAP_RADIUS;
         if (barStart > barEnd) {
             float middle = (barStart + barEnd) / 2.0f;
             barStart = barEnd = middle;
         }
         float bottom = viewport.height() - PADDING;
         return new RectF(barStart, bottom - BAR_SIZE, barEnd, bottom);
     }
 }
--- a/mobile/android/base/gfx/SingleTileLayer.java
+++ b/mobile/android/base/gfx/SingleTileLayer.java
@@ -62,17 +62,17 @@ public class SingleTileLayer extends Til
             // the texture repeats the correct number of times when drawn at
             // the size of the viewport.
             bounds = getBounds(context);
             textureBounds = new RectF(0.0f, 0.0f, bounds.width(), bounds.height());
             bounds = new RectF(0.0f, 0.0f, viewport.width(), viewport.height());
         } else if (stretches()) {
             // If we're stretching, we just want the bounds and texture bounds
             // to fit to the page.
-            bounds = new RectF(context.pageRect);
+            bounds = new RectF(0.0f, 0.0f, context.pageSize.width, context.pageSize.height);
             textureBounds = bounds;
         } else {
             bounds = getBounds(context);
             textureBounds = bounds;
         }
 
         Rect intBounds = new Rect();
         bounds.roundOut(intBounds);
--- a/mobile/android/base/gfx/ViewportMetrics.java
+++ b/mobile/android/base/gfx/ViewportMetrics.java
@@ -8,85 +8,76 @@ package org.mozilla.gecko.gfx;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.util.DisplayMetrics;
 import org.mozilla.gecko.FloatUtils;
 import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.gfx.FloatSize;
+import org.mozilla.gecko.gfx.IntSize;
 import org.mozilla.gecko.gfx.LayerController;
 import org.mozilla.gecko.gfx.RectUtils;
 import org.json.JSONException;
 import org.json.JSONObject;
 import android.util.Log;
 
 /**
  * ViewportMetrics manages state and contains some utility functions related to
  * the page viewport for the Gecko layer client to use.
  */
 public class ViewportMetrics {
     private static final String LOGTAG = "GeckoViewportMetrics";
 
-    private RectF mPageRect;
-    private RectF mCssPageRect;
+    private FloatSize mPageSize;
+    private FloatSize mCssPageSize;
     private RectF mViewportRect;
     private float mZoomFactor;
 
     public ViewportMetrics() {
         DisplayMetrics metrics = new DisplayMetrics();
         GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
 
-        mPageRect = new RectF(0, 0, metrics.widthPixels, metrics.heightPixels);
-        mCssPageRect = new RectF(0, 0, metrics.widthPixels, metrics.heightPixels);
+        mPageSize = new FloatSize(metrics.widthPixels, metrics.heightPixels);
+        mCssPageSize = new FloatSize(metrics.widthPixels, metrics.heightPixels);
         mViewportRect = new RectF(0, 0, metrics.widthPixels, metrics.heightPixels);
         mZoomFactor = 1.0f;
     }
 
     public ViewportMetrics(ViewportMetrics viewport) {
-        mPageRect = new RectF(viewport.getPageRect());
-        mCssPageRect = new RectF(viewport.getCssPageRect());
+        mPageSize = new FloatSize(viewport.getPageSize());
+        mCssPageSize = new FloatSize(viewport.getCssPageSize());
         mViewportRect = new RectF(viewport.getViewport());
         mZoomFactor = viewport.getZoomFactor();
     }
 
     public ViewportMetrics(ImmutableViewportMetrics viewport) {
-        mPageRect = new RectF(viewport.pageRectLeft,
-                viewport.pageRectTop,
-                viewport.pageRectRight,
-                viewport.pageRectBottom);
-        mCssPageRect = new RectF(viewport.cssPageRectLeft,
-                viewport.cssPageRectTop,
-                viewport.cssPageRectRight,
-                viewport.cssPageRectBottom);
+        mPageSize = new FloatSize(viewport.pageSizeWidth, viewport.pageSizeHeight);
+        mCssPageSize = new FloatSize(viewport.cssPageSizeWidth, viewport.cssPageSizeHeight);
         mViewportRect = new RectF(viewport.viewportRectLeft,
-                viewport.viewportRectTop,
-                viewport.viewportRectRight,
-                viewport.viewportRectBottom);
+                                  viewport.viewportRectTop,
+                                  viewport.viewportRectRight,
+                                  viewport.viewportRectBottom);
         mZoomFactor = viewport.zoomFactor;
     }
 
 
     public ViewportMetrics(JSONObject json) throws JSONException {
         float x = (float)json.getDouble("x");
         float y = (float)json.getDouble("y");
         float width = (float)json.getDouble("width");
         float height = (float)json.getDouble("height");
-        float pageLeft = (float)json.getDouble("pageLeft");
-        float pageTop = (float)json.getDouble("pageTop");
-        float pageRight = (float)json.getDouble("pageRight");
-        float pageBottom = (float)json.getDouble("pageBottom");
-        float cssPageLeft = (float)json.getDouble("cssPageLeft");
-        float cssPageTop = (float)json.getDouble("cssPageTop");
-        float cssPageRight = (float)json.getDouble("cssPageRight");
-        float cssPageBottom = (float)json.getDouble("cssPageBottom");
+        float pageWidth = (float)json.getDouble("pageWidth");
+        float pageHeight = (float)json.getDouble("pageHeight");
+        float cssPageWidth = (float)json.getDouble("cssPageWidth");
+        float cssPageHeight = (float)json.getDouble("cssPageHeight");
         float zoom = (float)json.getDouble("zoom");
 
-        mPageRect = new RectF(pageLeft, pageTop, pageRight, pageBottom);
-        mCssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
+        mPageSize = new FloatSize(pageWidth, pageHeight);
+        mCssPageSize = new FloatSize(cssPageWidth, cssPageHeight);
         mViewportRect = new RectF(x, y, x + width, y + height);
         mZoomFactor = zoom;
     }
 
     public PointF getOrigin() {
         return new PointF(mViewportRect.left, mViewportRect.top);
     }
 
@@ -101,45 +92,48 @@ public class ViewportMetrics {
     public RectF getCssViewport() {
         return RectUtils.scale(mViewportRect, 1/mZoomFactor);
     }
 
     /** Returns the viewport rectangle, clamped within the page-size. */
     public RectF getClampedViewport() {
         RectF clampedViewport = new RectF(mViewportRect);
 
-        // The viewport bounds ought to never exceed the page bounds.
-        if (clampedViewport.right > mPageRect.right)
-            clampedViewport.offset(mPageRect.right - clampedViewport.right, 0);
-        if (clampedViewport.left < mPageRect.left)
-            clampedViewport.offset(mPageRect.left - clampedViewport.left, 0);
+        // While the viewport size ought to never exceed the page size, we
+        // do the clamping in this order to make sure that the origin is
+        // never negative.
+        if (clampedViewport.right > mPageSize.width)
+            clampedViewport.offset(mPageSize.width - clampedViewport.right, 0);
+        if (clampedViewport.left < 0)
+            clampedViewport.offset(-clampedViewport.left, 0);
 
-        if (clampedViewport.bottom > mPageRect.bottom)
-            clampedViewport.offset(0, mPageRect.bottom - clampedViewport.bottom);
-        if (clampedViewport.top < mPageRect.top)
-            clampedViewport.offset(0, mPageRect.top - clampedViewport.top);
+        if (clampedViewport.bottom > mPageSize.height)
+            clampedViewport.offset(0, mPageSize.height - clampedViewport.bottom);
+        if (clampedViewport.top < 0)
+            clampedViewport.offset(0, -clampedViewport.top);
 
         return clampedViewport;
     }
 
-    public RectF getPageRect() {
-        return mPageRect;
+    public FloatSize getPageSize() {
+        return mPageSize;
     }
 
-    public RectF getCssPageRect() {
-        return mCssPageRect;
+    public FloatSize getCssPageSize() {
+        return mCssPageSize;
     }
 
+
     public float getZoomFactor() {
         return mZoomFactor;
     }
 
-    public void setPageRect(RectF pageRect, RectF cssPageRect) {
-        mPageRect = pageRect;
-        mCssPageRect = cssPageRect;
+    public void setPageSize(FloatSize pageSize, FloatSize cssPageSize) {
+        mPageSize = pageSize;
+        mCssPageSize = cssPageSize;
     }
 
     public void setViewport(RectF viewport) {
         mViewportRect = viewport;
     }
 
     public void setOrigin(PointF origin) {
         mViewportRect.set(origin.x, origin.y,
@@ -156,19 +150,19 @@ public class ViewportMetrics {
         mZoomFactor = zoomFactor;
     }
 
     /* This will set the zoom factor and re-scale page-size and viewport offset
      * accordingly. The given focus will remain at the same point on the screen
      * after scaling.
      */
     public void scaleTo(float newZoomFactor, PointF focus) {
-        // mCssPageRect is invariant, since we're setting the scale factor
-        // here. The page rect is based on the CSS page rect.
-        mPageRect = RectUtils.scale(mCssPageRect, newZoomFactor);
+        // mCssPageSize is invariant, since we're setting the scale factor
+        // here. The page size is based on the CSS page size.
+        mPageSize = mCssPageSize.scale(newZoomFactor);
 
         float scaleFactor = newZoomFactor / mZoomFactor;
         PointF origin = getOrigin();
 
         origin.offset(focus.x, focus.y);
         origin = PointUtils.scale(origin, scaleFactor);
         origin.offset(-focus.x, -focus.y);
 
@@ -179,57 +173,53 @@ public class ViewportMetrics {
 
     /*
      * Returns the viewport metrics that represent a linear transition between `from` and `to` at
      * time `t`, which is on the scale [0, 1). This function interpolates the viewport rect, the
      * page size, the offset, and the zoom factor.
      */
     public ViewportMetrics interpolate(ViewportMetrics to, float t) {
         ViewportMetrics result = new ViewportMetrics();
-        result.mPageRect = RectUtils.interpolate(mPageRect, to.mPageRect, t);
-        result.mCssPageRect = RectUtils.interpolate(mCssPageRect, to.mCssPageRect, t);
+        result.mPageSize = mPageSize.interpolate(to.mPageSize, t);
+        result.mCssPageSize = mCssPageSize.interpolate(to.mCssPageSize, t);
         result.mZoomFactor = FloatUtils.interpolate(mZoomFactor, to.mZoomFactor, t);
         result.mViewportRect = RectUtils.interpolate(mViewportRect, to.mViewportRect, t);
         return result;
     }
 
     public boolean fuzzyEquals(ViewportMetrics other) {
-        return RectUtils.fuzzyEquals(mPageRect, other.mPageRect)
-            && RectUtils.fuzzyEquals(mCssPageRect, other.mCssPageRect)
+        return mPageSize.fuzzyEquals(other.mPageSize)
+            && mCssPageSize.fuzzyEquals(other.mCssPageSize)
             && RectUtils.fuzzyEquals(mViewportRect, other.mViewportRect)
             && FloatUtils.fuzzyEquals(mZoomFactor, other.mZoomFactor);
     }
 
     public String toJSON() {
         // Round off height and width. Since the height and width are the size of the screen, it
         // makes no sense to send non-integer coordinates to Gecko.
         int height = Math.round(mViewportRect.height());
         int width = Math.round(mViewportRect.width());
 
-        StringBuffer sb = new StringBuffer(512);
+        StringBuffer sb = new StringBuffer(256);
         sb.append("{ \"x\" : ").append(mViewportRect.left)
           .append(", \"y\" : ").append(mViewportRect.top)
           .append(", \"width\" : ").append(width)
           .append(", \"height\" : ").append(height)
-          .append(", \"pageLeft\" : ").append(mPageRect.left)
-          .append(", \"pageTop\" : ").append(mPageRect.top)
-          .append(", \"pageRight\" : ").append(mPageRect.right)
-          .append(", \"pageBottom\" : ").append(mPageRect.bottom)
-          .append(", \"cssPageLeft\" : ").append(mCssPageRect.left)
-          .append(", \"cssPageTop\" : ").append(mCssPageRect.top)
-          .append(", \"cssPageRight\" : ").append(mCssPageRect.right)
-          .append(", \"cssPageBottom\" : ").append(mCssPageRect.bottom)
+          .append(", \"pageWidth\" : ").append(mPageSize.width)
+          .append(", \"pageHeight\" : ").append(mPageSize.height)
+          .append(", \"cssPageWidth\" : ").append(mCssPageSize.width)
+          .append(", \"cssPageHeight\" : ").append(mCssPageSize.height)
           .append(", \"zoom\" : ").append(mZoomFactor)
           .append(" }");
         return sb.toString();
     }
 
     @Override
     public String toString() {
-        StringBuffer buff = new StringBuffer(256);
+        StringBuffer buff = new StringBuffer(128);
         buff.append("v=").append(mViewportRect.toString())
-            .append(" p=").append(mPageRect.toString())
-            .append(" c=").append(mCssPageRect.toString())
+            .append(" p=").append(mPageSize.toString())
+            .append(" c=").append(mCssPageSize.toString())
             .append(" z=").append(mZoomFactor);
         return buff.toString();
     }
 }
 
--- a/mobile/android/base/ui/Axis.java
+++ b/mobile/android/base/ui/Axis.java
@@ -109,31 +109,26 @@ abstract class Axis {
     private boolean mScrollingDisabled;     /* Whether movement on this axis is locked. */
     private boolean mDisableSnap;           /* Whether overscroll snapping is disabled. */
     private float mDisplacement;
 
     private FlingStates mFlingState;        /* The fling state we're in on this axis. */
 
     protected abstract float getOrigin();
     protected abstract float getViewportLength();
-    protected abstract float getPageStart();
     protected abstract float getPageLength();
 
     Axis(SubdocumentScrollHelper subscroller) {
         mSubscroller = subscroller;
     }
 
     private float getViewportEnd() {
         return getOrigin() + getViewportLength();
     }
 
-    private float getPageEnd() {
-        return getPageStart() + getPageLength();
-    }
-
     void startTouch(float pos) {
         mVelocity = 0.0f;
         mScrollingDisabled = false;
         mFirstTouchPos = mTouchPos = mLastTouchPos = pos;
     }
 
     float panDistance(float currentPos) {
         return currentPos - mFirstTouchPos;
@@ -165,36 +160,36 @@ abstract class Axis {
         mTouchPos = pos;
     }
 
     boolean overscrolled() {
         return getOverscroll() != Overscroll.NONE;
     }
 
     private Overscroll getOverscroll() {
-        boolean minus = (getOrigin() < getPageStart());
-        boolean plus = (getViewportEnd() > getPageEnd());
+        boolean minus = (getOrigin() < 0.0f);
+        boolean plus = (getViewportEnd() > getPageLength());
         if (minus && plus) {
             return Overscroll.BOTH;
         } else if (minus) {
             return Overscroll.MINUS;
         } else if (plus) {
             return Overscroll.PLUS;
         } else {
             return Overscroll.NONE;
         }
     }
 
     // Returns the amount that the page has been overscrolled. If the page hasn't been
     // overscrolled on this axis, returns 0.
     private float getExcess() {
         switch (getOverscroll()) {
-        case MINUS:     return getPageStart() - getOrigin();
-        case PLUS:      return getViewportEnd() - getPageEnd();
-        case BOTH:      return (getViewportEnd() - getPageEnd()) + (getPageStart() - getOrigin());
+        case MINUS:     return -getOrigin();
+        case PLUS:      return getViewportEnd() - getPageLength();
+        case BOTH:      return getViewportEnd() - getPageLength() - getOrigin();
         default:        return 0.0f;
         }
     }
 
     /*
      * Returns true if the page is zoomed in to some degree along this axis such that scrolling is
      * possible and this axis has not been scroll locked while panning. Otherwise, returns false.
      */
--- a/mobile/android/base/ui/PanZoomController.java
+++ b/mobile/android/base/ui/PanZoomController.java
@@ -160,26 +160,26 @@ public class PanZoomController
                                      x + (float)message.getDouble("w"),
                                      y + (float)message.getDouble("h"));
                 mController.post(new Runnable() {
                     public void run() {
                         animatedZoomTo(zoomRect);
                     }
                 });
             } else if (MESSAGE_ZOOM_PAGE.equals(event)) {
-                RectF cssPageRect = mController.getCssPageRect();
+                FloatSize pageSize = mController.getCssPageSize();
 
                 RectF viewableRect = mController.getCssViewport();
                 float y = viewableRect.top;
                 // attempt to keep zoom keep focused on the center of the viewport
-                float newHeight = viewableRect.height() * cssPageRect.width() / viewableRect.width();
+                float newHeight = viewableRect.height() * pageSize.width / viewableRect.width();
                 float dh = viewableRect.height() - newHeight; // increase in the height
                 final RectF r = new RectF(0.0f,
                                     y + dh/2,
-                                    cssPageRect.width(),
+                                    pageSize.width,
                                     y + dh/2 + newHeight);
                 mController.post(new Runnable() {
                     public void run() {
                         animatedZoomTo(r);
                     }
                 });
             } else if (MESSAGE_PREFS_DATA.equals(event)) {
                 JSONArray jsonPrefs = message.getJSONArray("preferences");
@@ -289,17 +289,17 @@ public class PanZoomController
             // were prevented by touch listeners. Now there are no touch points left, so
             // we need to reset our state and re-bounce because we might be in overscroll
             mState = PanZoomState.NOTHING;
             bounce();
         }
     }
 
     /** This must be called on the UI thread. */
-    public void pageRectUpdated() {
+    public void pageSizeUpdated() {
         if (mState == PanZoomState.NOTHING) {
             synchronized (mController) {
                 ViewportMetrics validated = getValidViewportMetrics();
                 if (! (new ViewportMetrics(mController.getViewportMetrics())).fuzzyEquals(validated)) {
                     // page size changed such that we are now in overscroll. snap to the
                     // the nearest valid viewport
                     mController.setViewportMetrics(validated);
                     mController.notifyLayerClientOfGeometryChange();
@@ -770,17 +770,17 @@ public class PanZoomController
         return getValidViewportMetrics(new ViewportMetrics(mController.getViewportMetrics()));
     }
 
     private ViewportMetrics getValidViewportMetrics(ViewportMetrics viewportMetrics) {
         Log.d(LOGTAG, "generating valid viewport using " + viewportMetrics);
 
         /* First, we adjust the zoom factor so that we can make no overscrolled area visible. */
         float zoomFactor = viewportMetrics.getZoomFactor();
-        RectF pageRect = viewportMetrics.getPageRect();
+        FloatSize pageSize = viewportMetrics.getPageSize();
         RectF viewport = viewportMetrics.getViewport();
 
         float focusX = viewport.width() / 2.0f;
         float focusY = viewport.height() / 2.0f;
 
         float minZoomFactor = 0.0f;
         float maxZoomFactor = MAX_ZOOM;
 
@@ -790,26 +790,26 @@ public class PanZoomController
             maxZoomFactor = mController.getMaxZoom();
 
         if (!mController.getAllowZoom()) {
             // If allowZoom is false, clamp to the default zoom level.
             maxZoomFactor = minZoomFactor = mController.getDefaultZoom();
         }
 
         // Ensure minZoomFactor keeps the page at least as big as the viewport.
-        if (pageRect.width() > 0) {
-            float scaleFactor = viewport.width() / pageRect.width();
+        if (pageSize.width > 0) {
+            float scaleFactor = viewport.width() / pageSize.width;
             minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor);
-            if (viewport.width() > pageRect.width())
+            if (viewport.width() > pageSize.width)
                 focusX = 0.0f;
         }
-        if (pageRect.height() > 0) {
-            float scaleFactor = viewport.height() / pageRect.height();
+        if (pageSize.height > 0) {
+            float scaleFactor = viewport.height() / pageSize.height;
             minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor);
-            if (viewport.height() > pageRect.height())
+            if (viewport.height() > pageSize.height)
                 focusY = 0.0f;
         }
 
         maxZoomFactor = Math.max(maxZoomFactor, minZoomFactor);
 
         if (zoomFactor < minZoomFactor) {
             // if one (or both) of the page dimensions is smaller than the viewport,
             // zoom using the top/left as the focus on that axis. this prevents the
@@ -832,31 +832,27 @@ public class PanZoomController
 
     private class AxisX extends Axis {
         AxisX(SubdocumentScrollHelper subscroller) { super(subscroller); }
         @Override
         public float getOrigin() { return mController.getOrigin().x; }
         @Override
         protected float getViewportLength() { return mController.getViewportSize().width; }
         @Override
-        protected float getPageStart() { return mController.getPageRect().left; }
-        @Override
-        protected float getPageLength() { return mController.getPageRect().width(); }
+        protected float getPageLength() { return mController.getPageSize().width; }
     }
 
     private class AxisY extends Axis {
         AxisY(SubdocumentScrollHelper subscroller) { super(subscroller); }
         @Override
         public float getOrigin() { return mController.getOrigin().y; }
         @Override
         protected float getViewportLength() { return mController.getViewportSize().height; }
         @Override
-        protected float getPageStart() { return mController.getPageRect().top; }
-        @Override
-        protected float getPageLength() { return mController.getPageRect().height(); }
+        protected float getPageLength() { return mController.getPageSize().height; }
     }
 
     /*
      * Zooming
      */
     @Override
     public boolean onScaleBegin(SimpleScaleGestureDetector detector) {
         Log.d(LOGTAG, "onScaleBegin in " + mState);
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -114,30 +114,40 @@ var Strings = {};
   ["charset",    "chrome://global/locale/charsetTitles.properties"]
 ].forEach(function (aStringBundle) {
   let [name, bundle] = aStringBundle;
   XPCOMUtils.defineLazyGetter(Strings, name, function() {
     return Services.strings.createBundle(bundle);
   });
 });
 
+var MetadataProvider = {
+  getDrawMetadata: function getDrawMetadata() {
+    let viewport = BrowserApp.selectedTab.getViewport();
+    viewport.zoom = BrowserApp.selectedTab._drawZoom;
+    return JSON.stringify(viewport);
+  },
+};
+
 var BrowserApp = {
   _tabs: [],
   _selectedTab: null,
 
   deck: null,
 
   startup: function startup() {
     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess();
     dump("zerdatime " + Date.now() + " - browser chrome startup finished.");
 
     this.deck = document.getElementById("browsers");
     BrowserEventHandler.init();
     ViewportHandler.init();
 
+    getBridge().setDrawMetadataProvider(MetadataProvider);
+
     getBridge().browserApp = this;
 
     Services.obs.addObserver(this, "Tab:Add", false);
     Services.obs.addObserver(this, "Tab:Load", false);
     Services.obs.addObserver(this, "Tab:Selected", false);
     Services.obs.addObserver(this, "Tab:Closed", false);
     Services.obs.addObserver(this, "Session:Back", false);
     Services.obs.addObserver(this, "Session:Forward", false);
@@ -1866,63 +1876,58 @@ Tab.prototype = {
   },
 
   getViewport: function() {
     let viewport = {
       width: gScreenWidth,
       height: gScreenHeight,
       cssWidth: gScreenWidth / this._zoom,
       cssHeight: gScreenHeight / this._zoom,
-      pageLeft: 0,
-      pageTop: 0,
-      pageRight: gScreenWidth,
-      pageBottom: gScreenHeight,
+      pageWidth: gScreenWidth,
+      pageHeight: gScreenHeight,
       // We make up matching css page dimensions
-      cssPageLeft: 0,
-      cssPageTop: 0,
-      cssPageRight: gScreenWidth / this._zoom,
-      cssPageBottom: gScreenHeight / this._zoom,
+      cssPageWidth: gScreenWidth / this._zoom,
+      cssPageHeight: gScreenHeight / this._zoom,
       zoom: this._zoom,
     };
 
     // Set the viewport offset to current scroll offset
     viewport.cssX = this.browser.contentWindow.scrollX || 0;
     viewport.cssY = this.browser.contentWindow.scrollY || 0;
 
     // Transform coordinates based on zoom
     viewport.x = Math.round(viewport.cssX * viewport.zoom);
     viewport.y = Math.round(viewport.cssY * viewport.zoom);
 
     let doc = this.browser.contentDocument;
     if (doc != null) {
-      let cwu = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
-      let cssPageRect = cwu.getRootBounds();
+      let [pageWidth, pageHeight] = this.getPageSize(doc, viewport.cssWidth, viewport.cssHeight);
+
+      let cssPageWidth = pageWidth;
+      let cssPageHeight = pageHeight;
+
+      /* Transform the page width and height based on the zoom factor. */
+      pageWidth *= viewport.zoom;
+      pageHeight *= viewport.zoom;
 
       /*
        * Avoid sending page sizes of less than screen size before we hit DOMContentLoaded, because
        * this causes the page size to jump around wildly during page load. After the page is loaded,
        * send updates regardless of page size; we'll zoom to fit the content as needed.
        *
-       * In the check below, we floor the viewport size because there might be slight rounding errors
-       * introduced in the CSS page size due to the conversion to and from app units in Gecko. The
-       * error should be no more than one app unit so doing the floor is overkill, but safe in the
-       * sense that the extra page size updates that get sent as a result will be mostly harmless.
+       * Also, we need to compare the page size returned from getPageSize (in CSS pixels) to the floored
+       * screen size in CSS pixels because the page size returned from getPageSize may also be floored.
        */
-      let pageLargerThanScreen = (cssPageRect.width >= Math.floor(viewport.cssWidth))
-                              && (cssPageRect.height >= Math.floor(viewport.cssHeight));
+      let pageLargerThanScreen = (cssPageWidth >= Math.floor(viewport.cssWidth))
+                              && (cssPageHeight >= Math.floor(viewport.cssHeight));
       if (doc.readyState === 'complete' || pageLargerThanScreen) {
-        viewport.cssPageLeft = cssPageRect.left;
-        viewport.cssPageTop = cssPageRect.top;
-        viewport.cssPageRight = cssPageRect.right;
-        viewport.cssPageBottom = cssPageRect.bottom;
-        /* Transform the page width and height based on the zoom factor. */
-        viewport.pageLeft = (viewport.cssPageLeft * viewport.zoom);
-        viewport.pageTop = (viewport.cssPageTop * viewport.zoom);
-        viewport.pageRight = (viewport.cssPageRight * viewport.zoom);
-        viewport.pageBottom = (viewport.cssPageBottom * viewport.zoom);
+        viewport.cssPageWidth = cssPageWidth;
+        viewport.cssPageHeight = cssPageHeight;
+        viewport.pageWidth = pageWidth;
+        viewport.pageHeight = pageHeight;
       }
     }
 
     return viewport;
   },
 
   sendViewportUpdate: function(aPageSizeUpdate) {
     let message;
@@ -2695,23 +2700,23 @@ var BrowserEventHandler = {
     } else {
       const margin = 15;
       const minDifference = -20;
       const maxDifference = 20;
       let rect = ElementTouchHelper.getBoundingContentRect(element);
 
       let viewport = BrowserApp.selectedTab.getViewport();
       let vRect = new Rect(viewport.cssX, viewport.cssY, viewport.cssWidth, viewport.cssHeight);
-      let bRect = new Rect(Math.max(viewport.cssPageLeft, rect.x - margin),
+      let bRect = new Rect(Math.max(0,rect.x - margin),
                            rect.y,
                            rect.w + 2*margin,
                            rect.h);
 
-      // constrict the rect to the screen's right edge
-      bRect.width = Math.min(bRect.width, viewport.cssPageRight - bRect.x);
+      // constrict the rect to the screen width
+      bRect.width = Math.min(bRect.width, viewport.cssPageWidth - bRect.x);
 
       let overlap = vRect.intersect(bRect);
       let overlapArea = overlap.width*overlap.height;
       // we want to know if the area of the element showing is near the max we can show
       // on the screen at any time and if its already stretching the width of the screen
       let availHeight = Math.min(bRect.width*vRect.height/vRect.width, bRect.height);
       let showing = overlapArea/(bRect.width*availHeight);
       let dw = (bRect.width - vRect.width);
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -1626,16 +1626,24 @@ AndroidBridge::HandleGeckoMessage(const 
     if (jniFrame.CheckForException())
         return;
 
     nsJNIString jniStr(returnMessage, env);
     aRet.Assign(jniStr);
     ALOG_BRIDGE("leaving %s", __PRETTY_FUNCTION__);
 }
 
+static nsCOMPtr<nsIAndroidDrawMetadataProvider> gDrawMetadataProvider = NULL;
+
+nsCOMPtr<nsIAndroidDrawMetadataProvider>
+AndroidBridge::GetDrawMetadataProvider()
+{
+    return gDrawMetadataProvider;
+}
+
 void
 AndroidBridge::CheckURIVisited(const nsAString& aURI)
 {
     JNIEnv *env = GetJNIEnv();
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env);
@@ -2027,33 +2035,34 @@ AndroidBridge::IsTablet()
     bool ret = env->CallStaticBooleanMethod(mGeckoAppShellClass, jIsTablet);
     if (jniFrame.CheckForException())
         return false;
 
     return ret;
 }
 
 void
-AndroidBridge::SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect)
+AndroidBridge::SetFirstPaintViewport(float aOffsetX, float aOffsetY, float aZoom, float aPageWidth, float aPageHeight,
+                                     float aCssPageWidth, float aCssPageHeight)
 {
     AndroidGeckoLayerClient *client = mLayerClient;
     if (!client)
         return;
 
-    client->SetFirstPaintViewport(aOffset, aZoom, aPageRect, aCssPageRect);
+    client->SetFirstPaintViewport(aOffsetX, aOffsetY, aZoom, aPageWidth, aPageHeight, aCssPageWidth, aCssPageHeight);
 }
 
 void
-AndroidBridge::SetPageRect(float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect)
+AndroidBridge::SetPageSize(float aZoom, float aPageWidth, float aPageHeight, float aCssPageWidth, float aCssPageHeight)
 {
     AndroidGeckoLayerClient *client = mLayerClient;
     if (!client)
         return;
 
-    client->SetPageRect(aZoom, aPageRect, aCssPageRect);
+    client->SetPageSize(aZoom, aPageWidth, aPageHeight, aCssPageWidth, aCssPageHeight);
 }
 
 void
 AndroidBridge::SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated,
                                 nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY)
 {
     AndroidGeckoLayerClient *client = mLayerClient;
     if (!client)
@@ -2084,16 +2093,23 @@ nsAndroidBridge::~nsAndroidBridge()
 
 /* void handleGeckoEvent (in AString message); */
 NS_IMETHODIMP nsAndroidBridge::HandleGeckoMessage(const nsAString & message, nsAString &aRet NS_OUTPARAM)
 {
     AndroidBridge::Bridge()->HandleGeckoMessage(message, aRet);
     return NS_OK;
 }
 
+/* void SetDrawMetadataProvider (in nsIAndroidDrawMetadataProvider message); */
+NS_IMETHODIMP nsAndroidBridge::SetDrawMetadataProvider(nsIAndroidDrawMetadataProvider *aProvider)
+{
+    gDrawMetadataProvider = aProvider;
+    return NS_OK;
+}
+
 void
 AndroidBridge::NotifyDefaultPrevented(bool aDefaultPrevented)
 {
     JNIEnv *env = GetJNIEnv();
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env, 0);
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -299,16 +299,18 @@ public:
     void ReleaseNativeWindow(void *window);
     bool SetNativeWindowFormat(void *window, int width, int height, int format);
 
     bool LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride);
     bool UnlockWindow(void *window);
     
     void HandleGeckoMessage(const nsAString& message, nsAString &aRet);
 
+    nsCOMPtr<nsIAndroidDrawMetadataProvider> GetDrawMetadataProvider();
+
     void CheckURIVisited(const nsAString& uri);
     void MarkURIVisited(const nsAString& uri);
 
     bool InitCamera(const nsCString& contentType, PRUint32 camera, PRUint32 *width, PRUint32 *height, PRUint32 *fps);
 
     void CloseCamera();
 
     void EnableBatteryNotifications();
@@ -325,18 +327,19 @@ public:
     void ClearMessageList(PRInt32 aListId);
 
     bool IsTablet();
 
     void GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInfo);
     void EnableNetworkNotifications();
     void DisableNetworkNotifications();
 
-    void SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect);
-    void SetPageRect(float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect);
+    void SetFirstPaintViewport(float aOffsetX, float aOffsetY, float aZoom, float aPageWidth, float aPageHeight,
+                               float aCssPageWidth, float aCssPageHeight);
+    void SetPageSize(float aZoom, float aPageWidth, float aPageHeight, float aCssPageWidth, float aCssPageHeight);
     void SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated,
                           nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY);
 
     jobject CreateSurface();
     void DestroySurface(jobject surface);
     void ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted, bool aBlend);
     void HideSurface(jobject surface);
 
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -59,17 +59,17 @@ jmethodID AndroidLocation::jGetLongitude
 jmethodID AndroidLocation::jGetAltitudeMethod = 0;
 jmethodID AndroidLocation::jGetAccuracyMethod = 0;
 jmethodID AndroidLocation::jGetBearingMethod = 0;
 jmethodID AndroidLocation::jGetSpeedMethod = 0;
 jmethodID AndroidLocation::jGetTimeMethod = 0;
 
 jclass AndroidGeckoLayerClient::jGeckoLayerClientClass = 0;
 jmethodID AndroidGeckoLayerClient::jSetFirstPaintViewport = 0;
-jmethodID AndroidGeckoLayerClient::jSetPageRect = 0;
+jmethodID AndroidGeckoLayerClient::jSetPageSize = 0;
 jmethodID AndroidGeckoLayerClient::jSyncViewportInfoMethod = 0;
 jmethodID AndroidGeckoLayerClient::jCreateFrameMethod = 0;
 jmethodID AndroidGeckoLayerClient::jActivateProgramMethod = 0;
 jmethodID AndroidGeckoLayerClient::jDeactivateProgramMethod = 0;
 
 jclass AndroidLayerRendererFrame::jLayerRendererFrameClass = 0;
 jmethodID AndroidLayerRendererFrame::jBeginDrawingMethod = 0;
 jmethodID AndroidLayerRendererFrame::jDrawBackgroundMethod = 0;
@@ -243,18 +243,18 @@ AndroidRect::InitRectClass(JNIEnv *jEnv)
 void
 AndroidGeckoLayerClient::InitGeckoLayerClientClass(JNIEnv *jEnv)
 {
 #ifdef MOZ_JAVA_COMPOSITOR
     initInit();
 
     jGeckoLayerClientClass = getClassGlobalRef("org/mozilla/gecko/gfx/GeckoLayerClient");
 
-    jSetFirstPaintViewport = getMethod("setFirstPaintViewport", "(FFFFFFFFFFF)V");
-    jSetPageRect = getMethod("setPageRect", "(FFFFFFFFF)V");
+    jSetFirstPaintViewport = getMethod("setFirstPaintViewport", "(FFFFFFF)V");
+    jSetPageSize = getMethod("setPageSize", "(FFFFF)V");
     jSyncViewportInfoMethod = getMethod("syncViewportInfo",
                                         "(IIIIFZ)Lorg/mozilla/gecko/gfx/ViewTransform;");
     jCreateFrameMethod = getMethod("createFrame", "()Lorg/mozilla/gecko/gfx/LayerRenderer$Frame;");
     jActivateProgramMethod = getMethod("activateProgram", "()V");
     jDeactivateProgramMethod = getMethod("deactivateProgram", "()V");
 #endif
 }
 
@@ -646,41 +646,39 @@ AndroidGeckoSurfaceView::Draw2D(jobject 
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env, 0);
     env->CallVoidMethod(wrapped_obj, jDraw2DBufferMethod, buffer, stride);
 }
 
 void
-AndroidGeckoLayerClient::SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect)
+AndroidGeckoLayerClient::SetFirstPaintViewport(float aOffsetX, float aOffsetY, float aZoom, float aPageWidth, float aPageHeight,
+                                               float aCssPageWidth, float aCssPageHeight)
 {
     NS_ASSERTION(!isNull(), "SetFirstPaintViewport called on null layer client!");
     JNIEnv *env = GetJNIForThread();    // this is called on the compositor thread
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env, 0);
-    return env->CallVoidMethod(wrapped_obj, jSetFirstPaintViewport, (float)aOffset.x, (float)aOffset.y, aZoom,
-                               (float)aPageRect.x, (float)aPageRect.y, (float)aPageRect.XMost(), (float)aPageRect.YMost(),
-                               aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost());
+    return env->CallVoidMethod(wrapped_obj, jSetFirstPaintViewport, aOffsetX, aOffsetY, aZoom, aPageWidth, aPageHeight,
+                               aCssPageWidth, aCssPageHeight);
 }
 
 void
-AndroidGeckoLayerClient::SetPageRect(float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect)
+AndroidGeckoLayerClient::SetPageSize(float aZoom, float aPageWidth, float aPageHeight, float aCssPageWidth, float aCssPageHeight)
 {
-    NS_ASSERTION(!isNull(), "SetPageRect called on null layer client!");
+    NS_ASSERTION(!isNull(), "SetPageSize called on null layer client!");
     JNIEnv *env = GetJNIForThread();    // this is called on the compositor thread
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env, 0);
-    return env->CallVoidMethod(wrapped_obj, jSetPageRect, aZoom,
-                               (float)aPageRect.x, (float)aPageRect.y, (float)aPageRect.XMost(), (float)aPageRect.YMost(),
-                               aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost());
+    return env->CallVoidMethod(wrapped_obj, jSetPageSize, aZoom, aPageWidth, aPageHeight, aCssPageWidth, aCssPageHeight);
 }
 
 void
 AndroidGeckoLayerClient::SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated,
                                           nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY)
 {
     NS_ASSERTION(!isNull(), "SyncViewportInfo called on null layer client!");
     JNIEnv *env = GetJNIForThread();    // this is called on the compositor thread
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -8,17 +8,16 @@
 
 #include <jni.h>
 #include <android/log.h>
 
 #include "nsGeoPosition.h"
 #include "nsPoint.h"
 #include "nsRect.h"
 #include "nsString.h"
-#include "mozilla/gfx/Rect.h"
 
 //#define FORCE_ALOG 1
 
 #ifndef ALOG
 #if defined(DEBUG) || defined(FORCE_ALOG)
 #define ALOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
 #else
 #define ALOG(args...)
@@ -167,28 +166,29 @@ class AndroidGeckoLayerClient : public W
 public:
     static void InitGeckoLayerClientClass(JNIEnv *jEnv);
 
     void Init(jobject jobj);
 
     AndroidGeckoLayerClient() {}
     AndroidGeckoLayerClient(jobject jobj) { Init(jobj); }
 
-    void SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect);
-    void SetPageRect(float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect);
+    void SetFirstPaintViewport(float aOffsetX, float aOffsetY, float aZoom, float aPageWidth, float aPageHeight,
+                               float aCssPageWidth, float aCssPageHeight);
+    void SetPageSize(float aZoom, float aPageWidth, float aPageHeight, float aCssPageWidth, float aCssPageHeight);
     void SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated,
                           nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY);
     bool CreateFrame(AutoLocalJNIFrame *jniFrame, AndroidLayerRendererFrame& aFrame);
     bool ActivateProgram(AutoLocalJNIFrame *jniFrame);
     bool DeactivateProgram(AutoLocalJNIFrame *jniFrame);
 
 protected:
     static jclass jGeckoLayerClientClass;
     static jmethodID jSetFirstPaintViewport;
-    static jmethodID jSetPageRect;
+    static jmethodID jSetPageSize;
     static jmethodID jSyncViewportInfoMethod;
     static jmethodID jCreateFrameMethod;
     static jmethodID jActivateProgramMethod;
     static jmethodID jDeactivateProgramMethod;
 };
 
 class AndroidGeckoSurfaceView : public WrappedJavaObject
 {
--- a/widget/android/nsIAndroidBridge.idl
+++ b/widget/android/nsIAndroidBridge.idl
@@ -1,24 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIDOMWindow.idl"
 
+[scriptable, uuid(56fd8e18-a5cf-4e7a-92ba-4f68b4ad50ac)]
+interface nsIAndroidDrawMetadataProvider : nsISupports {
+  AString getDrawMetadata();
+};
+
 [scriptable, uuid(0843f3c1-043e-4c64-9d8c-091370548c05)]
 interface nsIBrowserTab : nsISupports {
   readonly attribute nsIDOMWindow window;
   readonly attribute float scale;
 };
 
 [scriptable, uuid(d10377b4-1c90-493a-a532-63cb3f16ee2b)]
 interface nsIAndroidBrowserApp : nsISupports {
   nsIBrowserTab getBrowserTab(in PRInt32 tabId);
 };
 
-[scriptable, uuid(bbb8e0d7-5cca-4ad0-88be-538ce6d04f63)]
+[scriptable, uuid(7dd8441a-4f38-49b2-bd90-da69d02a96cf)]
 interface nsIAndroidBridge : nsISupports
 {
   AString handleGeckoMessage(in AString message);
+  void setDrawMetadataProvider(in nsIAndroidDrawMetadataProvider provider);
   attribute nsIAndroidBrowserApp browserApp;
 };