Backed out 7 changesets (bug 883646) for reftest-ipc failures.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 20 Jun 2013 17:36:39 -0400
changeset 147403 3a96c5cf458c8a72e93753710e52a7418301af6e
parent 147402 b029e52490da258dfb1ec50f018b822af7b5502f
child 147404 ac48416672e7b9d484af08dec178284e29560a36
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs883646
milestone24.0a1
backs out2272804a8e711e9047b2f17252f6f7155f8bd421
e39d3bdf847a1c69e2ea2b6b4b0f7be8878c04a8
bf46fc332efec683eedab80be3043edee7172344
6bb558c3136fd8c2a352afd50bcff78f105b1e02
d7d5d16e27e07356d610ac7cacc8a8c930184916
14c73096a132dbc3ea172bfa878072e99bb5318a
89f6185a271b9cce8186295f5d458cf60ed5c3a2
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 7 changesets (bug 883646) for reftest-ipc failures. Backed out changeset 2272804a8e71 (bug 883646) Backed out changeset e39d3bdf847a (bug 883646) Backed out changeset bf46fc332efe (bug 883646) Backed out changeset 6bb558c3136f (bug 883646) Backed out changeset d7d5d16e27e0 (bug 883646) Backed out changeset 14c73096a132 (bug 883646) Backed out changeset 89f6185a271b (bug 883646)
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
gfx/2d/ScaleFactor.h
gfx/layers/FrameMetrics.h
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/ClientTiledThebesLayer.cpp
gfx/layers/composite/AsyncCompositionManager.cpp
gfx/layers/composite/AsyncCompositionManager.h
gfx/layers/composite/ThebesLayerComposite.cpp
gfx/layers/ipc/AsyncPanZoomController.cpp
gfx/layers/ipc/AsyncPanZoomController.h
gfx/layers/ipc/Axis.cpp
ipc/glue/IPCMessageUtils.h
layout/base/Units.h
layout/base/nsDisplayList.cpp
layout/ipc/RenderFrameParent.cpp
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -341,29 +341,25 @@ TabChild::Observe(nsISupports *aSubject,
         // Reset CSS viewport and zoom to default on new page, then
         // calculate them properly using the actual metadata from the
         // page.
         SetCSSViewport(kDefaultViewportSize);
 
         // Calculate a really simple resolution that we probably won't
         // be keeping, as well as putting the scroll offset back to
         // the top-left of the page.
-        mLastMetrics.mZoom = ScreenToScreenScale(1.0);
+        mLastMetrics.mZoom = gfxSize(1.0, 1.0);
         mLastMetrics.mViewport = CSSRect(CSSPoint(), kDefaultViewportSize);
         mLastMetrics.mCompositionBounds = ScreenIntRect(ScreenIntPoint(), mInnerSize);
         CSSToScreenScale resolution =
           AsyncPanZoomController::CalculateResolution(mLastMetrics);
-        // We use ScreenToLayerScale(1) below in order to ask gecko to render
-        // what's currently visible on the screen. This is effectively turning
-        // the async zoom amount into the gecko zoom amount.
-        mLastMetrics.mResolution =
-          resolution / mLastMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
+        mLastMetrics.mResolution = gfxSize(resolution.scale, resolution.scale);
         mLastMetrics.mScrollOffset = CSSPoint(0, 0);
-        utils->SetResolution(mLastMetrics.mResolution.scale,
-                             mLastMetrics.mResolution.scale);
+        utils->SetResolution(mLastMetrics.mResolution.width,
+                             mLastMetrics.mResolution.height);
 
         HandlePossibleViewportChange();
       }
     }
   } else if (!strcmp(aTopic, DETECT_SCROLLABLE_SUBFRAME)) {
     nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aSubject));
     nsCOMPtr<nsITabChild> tabChild(GetTabChildFrom(docShell));
     if (tabChild == this) {
@@ -596,41 +592,47 @@ TabChild::HandlePossibleViewportChange()
   metrics.mCompositionBounds = ScreenIntRect(ScreenIntPoint(), mInnerSize);
 
   // Changing the zoom when we're not doing a first paint will get ignored
   // by AsyncPanZoomController and causes a blurry flash.
   bool isFirstPaint;
   nsresult rv = utils->GetIsFirstPaint(&isFirstPaint);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   if (NS_FAILED(rv) || isFirstPaint) {
-    CSSToScreenScale intrinsicScale =
+    gfxSize intrinsicScale =
         AsyncPanZoomController::CalculateIntrinsicScale(metrics);
     // FIXME/bug 799585(?): GetViewportInfo() returns a defaultZoom of
     // 0.0 to mean "did not calculate a zoom".  In that case, we default
     // it to the intrinsic scale.
     if (viewportInfo.GetDefaultZoom() < 0.01f) {
-      viewportInfo.SetDefaultZoom(intrinsicScale.scale);
+      viewportInfo.SetDefaultZoom(intrinsicScale.width);
     }
 
     double defaultZoom = viewportInfo.GetDefaultZoom();
     MOZ_ASSERT(viewportInfo.GetMinZoom() <= defaultZoom &&
                defaultZoom <= viewportInfo.GetMaxZoom());
     // GetViewportInfo() returns a resolution-dependent scale factor.
     // Convert that to a resolution-indepedent zoom.
-    metrics.mZoom = ScreenToScreenScale(defaultZoom / intrinsicScale.scale);
+    metrics.mZoom = gfxSize(defaultZoom / intrinsicScale.width,
+                            defaultZoom / intrinsicScale.height);
   }
 
   metrics.mDisplayPort = AsyncPanZoomController::CalculatePendingDisplayPort(
     // The page must have been refreshed in some way such as a new document or
     // new CSS viewport, so we know that there's no velocity, acceleration, and
     // we have no idea how long painting will take.
     metrics, gfx::Point(0.0f, 0.0f), gfx::Point(0.0f, 0.0f), 0.0);
   CSSToScreenScale resolution = AsyncPanZoomController::CalculateResolution(metrics);
-  metrics.mResolution = resolution / metrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
-  utils->SetResolution(metrics.mResolution.scale, metrics.mResolution.scale);
+  // XXX is this actually hysteresis?  This calculation is not well
+  // understood.  It's taken from the previous JS implementation.
+  gfxFloat hysteresis/*?*/ =
+    gfxFloat(oldBrowserWidth) / gfxFloat(oldScreenWidth);
+  metrics.mResolution = gfxSize(resolution.scale * hysteresis,
+                                resolution.scale * hysteresis);
+  utils->SetResolution(metrics.mResolution.width, metrics.mResolution.height);
 
   // Force a repaint with these metrics. This, among other things, sets the
   // displayport, so we start with async painting.
   ProcessUpdateFrame(metrics);
 }
 
 nsresult
 TabChild::Init()
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -285,17 +285,17 @@ public:
     JSContext* GetJSContext() { return mCx; }
 
     nsIPrincipal* GetPrincipal() { return mPrincipal; }
 
     /** Return the DPI of the widget this TabChild draws to. */
     void GetDPI(float* aDPI);
     void GetDefaultScale(double *aScale);
 
-    ScreenToScreenScale GetZoom() { return mLastMetrics.mZoom; }
+    gfxSize GetZoom() { return mLastMetrics.mZoom; }
 
     ScreenOrientation GetOrientation() { return mOrientation; }
 
     void SetBackgroundColor(const nscolor& aColor);
 
     void NotifyPainted();
 
     bool IsAsyncPanZoomEnabled();
--- a/gfx/2d/ScaleFactor.h
+++ b/gfx/2d/ScaleFactor.h
@@ -51,27 +51,17 @@ struct ScaleFactor {
   }
 
   template<class other>
   ScaleFactor<other, dst> operator/(const ScaleFactor<src, other>& aOther) const {
     return ScaleFactor<other, dst>(scale / aOther.scale);
   }
 
   template<class other>
-  ScaleFactor<src, other> operator/(const ScaleFactor<other, dst>& aOther) const {
-    return ScaleFactor<src, other>(scale / aOther.scale);
-  }
-
-  template<class other>
   ScaleFactor<src, other> operator*(const ScaleFactor<dst, other>& aOther) const {
     return ScaleFactor<src, other>(scale * aOther.scale);
   }
-
-  template<class other>
-  ScaleFactor<other, dst> operator*(const ScaleFactor<other, src>& aOther) const {
-    return ScaleFactor<other, dst>(scale * aOther.scale);
-  }
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_SCALEFACTOR_H_ */
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -33,18 +33,18 @@ public:
   FrameMetrics()
     : mCompositionBounds(0, 0, 0, 0)
     , mDisplayPort(0, 0, 0, 0)
     , mCriticalDisplayPort(0, 0, 0, 0)
     , mViewport(0, 0, 0, 0)
     , mScrollOffset(0, 0)
     , mScrollId(NULL_SCROLL_ID)
     , mScrollableRect(0, 0, 0, 0)
-    , mResolution(1)
-    , mZoom(1)
+    , mResolution(1, 1)
+    , mZoom(1, 1)
     , mDevPixelsPerCSSPixel(1)
     , mMayHaveTouchListeners(false)
     , mPresShellId(-1)
   {}
 
   // Default copy ctor and operator= are fine
 
   bool operator==(const FrameMetrics& aOther) const
@@ -79,24 +79,28 @@ public:
     return mScrollId == ROOT_SCROLL_ID;
   }
 
   bool IsScrollable() const
   {
     return mScrollId != NULL_SCROLL_ID;
   }
 
-  CSSToLayerScale LayersPixelsPerCSSPixel() const
+  gfxSize LayersPixelsPerCSSPixel() const
   {
     return mResolution * mDevPixelsPerCSSPixel;
   }
 
-  LayerPoint GetScrollOffsetInLayerPixels() const
+  gfx::Point GetScrollOffsetInLayerPixels() const
   {
-    return mScrollOffset * LayersPixelsPerCSSPixel();
+    return gfx::Point(
+      static_cast<gfx::Float>(
+        mScrollOffset.x * LayersPixelsPerCSSPixel().width),
+      static_cast<gfx::Float>(
+        mScrollOffset.y * LayersPixelsPerCSSPixel().height));
   }
 
   // ---------------------------------------------------------------------------
   // The following metrics are all in widget space/device pixels.
   //
 
   // This is the area within the widget that we're compositing to, which means
   // that it is the visible region of this frame. It is not relative to
@@ -163,18 +167,18 @@ public:
   // (or the document relative to the viewport, if that helps understand it).
   //
   // Thus it is relative to the document. It is in the same coordinate space as
   // |mScrollableRect|, but a different coordinate space than |mViewport| and
   // |mDisplayPort|.
   //
   // It is required that the rect:
   // { x = mScrollOffset.x, y = mScrollOffset.y,
-  //   width = mCompositionBounds.x / mResolution.scale,
-  //   height = mCompositionBounds.y / mResolution.scale }
+  //   width = mCompositionBounds.x / mResolution.width,
+  //   height = mCompositionBounds.y / mResolution.height }
   // Be within |mScrollableRect|.
   //
   // This is valid for any layer, but is always relative to this frame and
   // not any parents, regardless of parent transforms.
   CSSPoint mScrollOffset;
 
   // A unique ID assigned to each scrollable frame (unless this is
   // ROOT_SCROLL_ID, in which case it is not unique).
@@ -200,38 +204,38 @@ public:
   // The resolution, along both axes, that the current frame has been painted
   // at.
   //
   // Every time this frame is composited and the compositor samples its
   // transform, this metric is used to create a transform which is
   // post-multiplied into the parent's transform. Since this only happens when
   // we walk the layer tree, the resulting transform isn't stored here. Thus the
   // resolution of parent layers is opaque to this metric.
-  LayoutDeviceToLayerScale mResolution;
+  gfxSize mResolution;
 
   // The resolution-independent "user zoom".  For example, if a page
   // configures the viewport to a zoom value of 2x, then this member
   // will always be 2.0 no matter what the viewport or composition
   // bounds.
   //
   // In the steady state (no animations), and ignoring DPI, then the
   // following is usually true
   //
   //  intrinsicScale = (mCompositionBounds / mViewport)
   //  mResolution = mZoom * intrinsicScale
   //
   // When this is not true, we're probably asynchronously sampling a
   // zoom animation for content.
-  ScreenToScreenScale mZoom;
+  gfxSize mZoom;
 
   // The conversion factor between CSS pixels and device pixels for this frame.
   // This can vary based on a variety of things, such as reflowing-zoom. The
   // conversion factor for device pixels to layers pixels is just the
   // resolution.
-  CSSToLayoutDeviceScale mDevPixelsPerCSSPixel;
+  float mDevPixelsPerCSSPixel;
 
   // Whether or not this frame may have touch listeners.
   bool mMayHaveTouchListeners;
 
   uint32_t mPresShellId;
 };
 
 }
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -403,18 +403,18 @@ ClientLayerManager::ProgressiveUpdateCal
 #ifdef MOZ_WIDGET_ANDROID
   Layer* primaryScrollable = GetPrimaryScrollableLayer();
   if (primaryScrollable) {
     const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
 
     // This is derived from the code in
     // gfx/layers/ipc/CompositorParent.cpp::TransformShadowTree.
     const gfx3DMatrix& rootTransform = GetRoot()->GetTransform();
-    CSSToLayerScale paintScale = metrics.mDevPixelsPerCSSPixel
-        / LayerToLayoutDeviceScale(rootTransform.GetXScale(), rootTransform.GetYScale());
+    CSSToLayerScale paintScale = LayerToCSSScale(rootTransform.GetXScale(),
+                                                 rootTransform.GetYScale()).Inverse();
     const CSSRect& metricsDisplayPort =
       (aDrawingCritical && !metrics.mCriticalDisplayPort.IsEmpty()) ?
         metrics.mCriticalDisplayPort : metrics.mDisplayPort;
     LayerRect displayPort = (metricsDisplayPort + metrics.mScrollOffset) * paintScale;
 
     return AndroidBridge::Bridge()->ProgressiveUpdateCallback(
       aHasPendingNewThebesContent, displayPort, paintScale.scale, aDrawingCritical,
       aViewport, aScaleX, aScaleY);
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -52,37 +52,36 @@ ClientTiledThebesLayer::BeginPaint()
     if (parent->UseIntermediateSurface()) {
       mPaintData.mTransformScreenToLayer.PreMultiply(parent->GetEffectiveTransform());
     }
   }
   mPaintData.mTransformScreenToLayer.Invert();
 
   // Compute the critical display port in layer space.
   mPaintData.mLayerCriticalDisplayPort.SetEmpty();
-  const FrameMetrics& metrics = GetParent()->GetFrameMetrics();
   const gfx::Rect& criticalDisplayPort =
-    (metrics.mCriticalDisplayPort * metrics.mDevPixelsPerCSSPixel).ToUnknownRect();
+    GetParent()->GetFrameMetrics().mCriticalDisplayPort.ToUnknownRect();
   if (!criticalDisplayPort.IsEmpty()) {
     gfxRect transformedCriticalDisplayPort =
       mPaintData.mTransformScreenToLayer.TransformBounds(
         gfxRect(criticalDisplayPort.x, criticalDisplayPort.y,
                 criticalDisplayPort.width, criticalDisplayPort.height));
     transformedCriticalDisplayPort.RoundOut();
     mPaintData.mLayerCriticalDisplayPort = nsIntRect(transformedCriticalDisplayPort.x,
                                              transformedCriticalDisplayPort.y,
                                              transformedCriticalDisplayPort.width,
                                              transformedCriticalDisplayPort.height);
   }
 
   // Calculate the frame resolution.
   mPaintData.mResolution.SizeTo(1, 1);
   for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
     const FrameMetrics& metrics = parent->GetFrameMetrics();
-    mPaintData.mResolution.width *= metrics.mResolution.scale;
-    mPaintData.mResolution.height *= metrics.mResolution.scale;
+    mPaintData.mResolution.width *= metrics.mResolution.width;
+    mPaintData.mResolution.height *= metrics.mResolution.height;
   }
 
   // Calculate the scroll offset since the last transaction, and the
   // composition bounds.
   mPaintData.mCompositionBounds.SetEmpty();
   mPaintData.mScrollOffset.MoveTo(0, 0);
   Layer* primaryScrollable = ClientManager()->GetPrimaryScrollableLayer();
   if (primaryScrollable) {
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -346,24 +346,22 @@ AsyncCompositionManager::ApplyAsyncConte
     *aWantNextFrame |=
       controller->SampleContentTransformForFrame(aCurrentFrame,
                                                  container,
                                                  &treeTransform,
                                                  scrollOffset);
 
     const gfx3DMatrix& rootTransform = mLayerManager->GetRoot()->GetTransform();
     const FrameMetrics& metrics = container->GetFrameMetrics();
-    CSSToLayerScale paintScale = metrics.mDevPixelsPerCSSPixel
-      / LayerToLayoutDeviceScale(rootTransform.GetXScale(), rootTransform.GetYScale());
     CSSRect displayPort(metrics.mCriticalDisplayPort.IsEmpty() ?
                         metrics.mDisplayPort : metrics.mCriticalDisplayPort);
     gfx::Margin fixedLayerMargins(0, 0, 0, 0);
     ScreenPoint offset(0, 0);
     SyncFrameMetrics(scrollOffset, treeTransform.mScale.scale, metrics.mScrollableRect,
-                     mLayersUpdated, displayPort, paintScale,
+                     mLayersUpdated, displayPort, 1 / rootTransform.GetXScale(),
                      mIsFirstPaint, fixedLayerMargins, offset);
 
     mIsFirstPaint = false;
     mLayersUpdated = false;
 
     // Apply the render offset
     mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
 
@@ -401,25 +399,25 @@ AsyncCompositionManager::TransformScroll
 
   const FrameMetrics& metrics = container->GetFrameMetrics();
   // We must apply the resolution scale before a pan/zoom transform, so we call
   // GetTransform here.
   const gfx3DMatrix& currentTransform = aLayer->GetTransform();
 
   gfx3DMatrix treeTransform;
 
-  CSSToLayerScale geckoZoom = metrics.mDevPixelsPerCSSPixel /
-    LayerToLayoutDeviceScale(aRootTransform.GetXScale(), aRootTransform.GetYScale());
+  CSSToLayerScale geckoZoom = LayerToCSSScale(aRootTransform.GetXScale(),
+                                              aRootTransform.GetYScale()).Inverse();
 
   LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.mScrollOffset * geckoZoom);
 
   if (mIsFirstPaint) {
     mContentRect = metrics.mScrollableRect;
     SetFirstPaintViewport(scrollOffsetLayerPixels,
-                          geckoZoom,
+                          geckoZoom.scale,
                           mContentRect);
     mIsFirstPaint = false;
   } else if (!metrics.mScrollableRect.IsEqualEdges(mContentRect)) {
     mContentRect = metrics.mScrollableRect;
     SetPageRect(mContentRect);
   }
 
   // We synchronise the viewport information with Java after sending the above
@@ -431,18 +429,18 @@ AsyncCompositionManager::TransformScroll
       : metrics.mCriticalDisplayPort
     ) * geckoZoom);
   displayPort += scrollOffsetLayerPixels;
 
   gfx::Margin fixedLayerMargins(0, 0, 0, 0);
   ScreenPoint offset(0, 0);
   ScreenPoint userScroll(0, 0);
   CSSToScreenScale userZoom;
-  SyncViewportInfo(displayPort, geckoZoom, mLayersUpdated,
-                   userScroll, userZoom, fixedLayerMargins,
+  SyncViewportInfo(displayPort, geckoZoom.scale, mLayersUpdated,
+                   userScroll, userZoom.scale, userZoom.scale, fixedLayerMargins,
                    offset);
   mLayersUpdated = false;
 
   // Apply the render offset
   mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
 
   // Handle transformations for asynchronous panning and zooming. We determine the
   // zoom used by Gecko from the transformation set on the root layer, and we
@@ -453,17 +451,17 @@ AsyncCompositionManager::TransformScroll
   LayerToScreenScale zoomAdjust = userZoom / geckoZoom;
 
   LayerIntPoint geckoScroll(0, 0);
   if (metrics.IsScrollable()) {
     geckoScroll = scrollOffsetLayerPixels;
   }
 
   LayerPoint translation = (userScroll / zoomAdjust) - geckoScroll;
-  treeTransform = gfx3DMatrix(ViewTransform(-translation, userZoom / metrics.mDevPixelsPerCSSPixel));
+  treeTransform = gfx3DMatrix(ViewTransform(-translation, userZoom));
 
   // Translate fixed position layers so that they stay in the correct position
   // when userScroll and geckoScroll differ.
   gfxPoint fixedOffset;
   gfxSize scaleDiff;
 
   LayerRect content = mContentRect * geckoZoom;
   // If the contents can fit entirely within the widget area on a particular
@@ -540,17 +538,17 @@ AsyncCompositionManager::TransformShadow
     }
   }
 
   return wantNextFrame;
 }
 
 void
 AsyncCompositionManager::SetFirstPaintViewport(const LayerIntPoint& aOffset,
-                                               const CSSToLayerScale& aZoom,
+                                               float aZoom,
                                                const CSSRect& aCssPageRect)
 {
 #ifdef MOZ_WIDGET_ANDROID
   AndroidBridge::Bridge()->SetFirstPaintViewport(aOffset, aZoom, aCssPageRect);
 #endif
 }
 
 void
@@ -558,41 +556,41 @@ AsyncCompositionManager::SetPageRect(con
 {
 #ifdef MOZ_WIDGET_ANDROID
   AndroidBridge::Bridge()->SetPageRect(aCssPageRect);
 #endif
 }
 
 void
 AsyncCompositionManager::SyncViewportInfo(const LayerIntRect& aDisplayPort,
-                                          const CSSToLayerScale& aDisplayResolution,
+                                          float aDisplayResolution,
                                           bool aLayersUpdated,
                                           ScreenPoint& aScrollOffset,
-                                          CSSToScreenScale& aScale,
+                                          float& aScaleX, float& aScaleY,
                                           gfx::Margin& aFixedLayerMargins,
                                           ScreenPoint& aOffset)
 {
 #ifdef MOZ_WIDGET_ANDROID
   AndroidBridge::Bridge()->SyncViewportInfo(aDisplayPort,
                                             aDisplayResolution,
                                             aLayersUpdated,
                                             aScrollOffset,
-                                            aScale,
+                                            aScaleX, aScaleY,
                                             aFixedLayerMargins,
                                             aOffset);
 #endif
 }
 
 void
 AsyncCompositionManager::SyncFrameMetrics(const ScreenPoint& aScrollOffset,
                                           float aZoom,
                                           const CSSRect& aCssPageRect,
                                           bool aLayersUpdated,
                                           const CSSRect& aDisplayPort,
-                                          const CSSToLayerScale& aDisplayResolution,
+                                          float aDisplayResolution,
                                           bool aIsFirstPaint,
                                           gfx::Margin& aFixedLayerMargins,
                                           ScreenPoint& aOffset)
 {
 #ifdef MOZ_WIDGET_ANDROID
   AndroidBridge::Bridge()->SyncFrameMetrics(aScrollOffset, aZoom, aCssPageRect,
                                             aLayersUpdated, aDisplayPort,
                                             aDisplayResolution, aIsFirstPaint,
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -23,30 +23,30 @@ namespace layers {
 class AsyncPanZoomController;
 class Layer;
 class LayerManagerComposite;
 class AutoResolveRefLayers;
 
 // Represents (affine) transforms that are calculated from a content view.
 struct ViewTransform {
   ViewTransform(LayerPoint aTranslation = LayerPoint(),
-                LayoutDeviceToScreenScale aScale = LayoutDeviceToScreenScale())
+                CSSToScreenScale aScale = CSSToScreenScale())
     : mTranslation(aTranslation)
     , mScale(aScale)
   {}
 
   operator gfx3DMatrix() const
   {
     return
       gfx3DMatrix::Translation(mTranslation.x, mTranslation.y, 0) *
       gfx3DMatrix::ScalingMatrix(mScale.scale, mScale.scale, 1);
   }
 
   LayerPoint mTranslation;
-  LayoutDeviceToScreenScale mScale;
+  CSSToScreenScale mScale;
 };
 
 /**
  * Manage async composition effects. This class is only used with OMTC and only
  * lives on the compositor thread. It is a layer on top of the layer manager
  * (LayerManagerComposite) which deals with elements of composition which are
  * usually dealt with by dom or layout when main thread rendering, but which can
  * short circuit that stuff to directly affect layers as they are composited,
@@ -110,32 +110,32 @@ private:
   void TransformScrollableLayer(Layer* aLayer, const gfx3DMatrix& aRootTransform);
   // Return true if an AsyncPanZoomController content transform was
   // applied for |aLayer|.  *aWantNextFrame is set to true if the
   // controller wants another animation frame.
   bool ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame, Layer* aLayer,
                                         bool* aWantNextFrame);
 
   void SetFirstPaintViewport(const LayerIntPoint& aOffset,
-                             const CSSToLayerScale& aZoom,
+                             float aZoom,
                              const CSSRect& aCssPageRect);
   void SetPageRect(const CSSRect& aCssPageRect);
   void SyncViewportInfo(const LayerIntRect& aDisplayPort,
-                        const CSSToLayerScale& aDisplayResolution,
+                        float aDisplayResolution,
                         bool aLayersUpdated,
                         ScreenPoint& aScrollOffset,
-                        CSSToScreenScale& aScale,
+                        float& aScaleX, float& aScaleY,
                         gfx::Margin& aFixedLayerMargins,
                         ScreenPoint& aOffset);
   void SyncFrameMetrics(const ScreenPoint& aScrollOffset,
                         float aZoom,
                         const CSSRect& aCssPageRect,
                         bool aLayersUpdated,
                         const CSSRect& aDisplayPort,
-                        const CSSToLayerScale& aDisplayResolution,
+                        float aDisplayResolution,
                         bool aIsFirstPaint,
                         gfx::Margin& aFixedLayerMargins,
                         ScreenPoint& aOffset);
 
   /**
    * Recursively applies the given translation to all top-level fixed position
    * layers that are descendants of the given layer.
    * aScaleDiff is considered to be the scale transformation applied when
--- a/gfx/layers/composite/ThebesLayerComposite.cpp
+++ b/gfx/layers/composite/ThebesLayerComposite.cpp
@@ -160,18 +160,18 @@ ThebesLayerComposite::GetEffectiveResolu
   // Work out render resolution by multiplying the resolution of our ancestors.
   // Only container layers can have frame metrics, so we start off with a
   // resolution of 1, 1.
   // XXX For large layer trees, it would be faster to do this once from the
   //     root node upwards and store the value on each layer.
   gfxSize resolution(1, 1);
   for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
     const FrameMetrics& metrics = parent->GetFrameMetrics();
-    resolution.width *= metrics.mResolution.scale;
-    resolution.height *= metrics.mResolution.scale;
+    resolution.width *= metrics.mResolution.width;
+    resolution.height *= metrics.mResolution.height;
   }
 
   return resolution;
 }
 
 gfxRect
 ThebesLayerComposite::GetDisplayPort()
 {
@@ -194,18 +194,18 @@ ThebesLayerComposite::GetDisplayPort()
           // it'd be quite hard to retain invalid tiles correctly in this
           // situation anyway.
           displayPort = gfxRect(metrics.mDisplayPort.x,
                                 metrics.mDisplayPort.y,
                                 metrics.mDisplayPort.width,
                                 metrics.mDisplayPort.height);
           displayPort.ScaleRoundOut(parentResolution.width, parentResolution.height);
       }
-      parentResolution.width /= metrics.mResolution.scale;
-      parentResolution.height /= metrics.mResolution.scale;
+      parentResolution.width /= metrics.mResolution.width;
+      parentResolution.height /= metrics.mResolution.height;
     }
     if (parent->UseIntermediateSurface()) {
       transform.PreMultiply(parent->GetTransform());
     }
   }
 
   // If no display port was found, use the widget size from the layer manager.
   if (displayPort.IsEmpty()) {
@@ -247,20 +247,19 @@ ThebesLayerComposite::GetCompositionBoun
       Layer* rootLayer = Manager()->GetRoot();
       const gfx3DMatrix& rootTransform = rootLayer->GetTransform();
       LayerToCSSScale scale(rootTransform.GetXScale(),
                             rootTransform.GetYScale());
 
       // Get the content document bounds, in screen-space.
       const FrameMetrics& metrics = scrollableLayer->GetFrameMetrics();
       const LayerIntRect content = RoundedToInt(metrics.mScrollableRect / scale);
-      // !!! WTF. this code is just wrong. See bug 881451.
       gfx::Point scrollOffset =
-        gfx::Point((metrics.mScrollOffset.x * metrics.LayersPixelsPerCSSPixel().scale) / scale.scale,
-                   (metrics.mScrollOffset.y * metrics.LayersPixelsPerCSSPixel().scale) / scale.scale);
+        gfx::Point((metrics.mScrollOffset.x * metrics.LayersPixelsPerCSSPixel().width) / scale.scale,
+                   (metrics.mScrollOffset.y * metrics.LayersPixelsPerCSSPixel().height) / scale.scale);
       const nsIntPoint contentOrigin(
         content.x - NS_lround(scrollOffset.x),
         content.y - NS_lround(scrollOffset.y));
       gfxRect contentRect = gfxRect(contentOrigin.x, contentOrigin.y,
                                     content.width, content.height);
       gfxRect contentBounds = scrollableLayer->GetEffectiveTransform().
         TransformBounds(contentRect);
 
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -63,22 +63,22 @@ static const TimeDuration ZOOM_TO_DURATI
 /**
  * Computed time function used for sampling frames of a zoom to animation.
  */
 StaticAutoPtr<ComputedTimingFunction> gComputedTimingFunction;
 
 /**
  * Maximum zoom amount, always used, even if a page asks for higher.
  */
-static const float MAX_ZOOM = 8.0f;
+static const double MAX_ZOOM = 8.0;
 
 /**
  * Minimum zoom amount, always used, even if a page asks for lower.
  */
-static const float MIN_ZOOM = 0.125f;
+static const double MIN_ZOOM = 0.125;
 
 /**
  * Amount of time before we timeout touch event listeners. For example, if
  * content is being unruly/slow and we don't get a response back within this
  * time, we will just pretend that content did not preventDefault any touch
  * events we dispatched to it.
  */
 static int gTouchListenerTimeout = 300;
@@ -386,17 +386,17 @@ nsEventStatus AsyncPanZoomController::On
 
   switch (mState) {
     case ANIMATING_ZOOM:
       // We just interrupted a double-tap animation, so force a redraw in case
       // this touchstart is just a tap that doesn't end up triggering a redraw.
       {
         MonitorAutoLock monitor(mMonitor);
         // Bring the resolution back in sync with the zoom.
-        SetZoomAndResolution(mFrameMetrics.mZoom);
+        SetZoomAndResolution(mFrameMetrics.mZoom.width);
         RequestContentRepaint();
         ScheduleComposite();
       }
       // Fall through.
     case FLING:
       CancelAnimation();
       // Fall through.
     case NOTHING:
@@ -540,17 +540,17 @@ nsEventStatus AsyncPanZoomController::On
   }
 
   float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
 
   {
     MonitorAutoLock monitor(mMonitor);
 
     CSSToScreenScale resolution = CalculateResolution(mFrameMetrics);
-    gfxFloat userZoom = mFrameMetrics.mZoom.scale;
+    gfxFloat userZoom = mFrameMetrics.mZoom.width;
     ScreenPoint focusPoint = aEvent.mFocusPoint;
 
     CSSPoint focusChange = (mLastZoomFocus - focusPoint) / resolution;
     // If displacing by the change in focus point will take us off page bounds,
     // then reduce the displacement such that it doesn't.
     if (mX.DisplacementWillOverscroll(focusChange.x) != Axis::OVERSCROLL_NONE) {
       focusChange.x -= mX.DisplacementWillOverscrollAmount(focusChange.x);
     }
@@ -559,29 +559,29 @@ nsEventStatus AsyncPanZoomController::On
     }
     ScrollBy(focusChange);
 
     // When we zoom in with focus, we can zoom too much towards the boundaries
     // that we actually go over them. These are the needed displacements along
     // either axis such that we don't overscroll the boundaries when zooming.
     gfx::Point neededDisplacement;
 
-    float maxZoom = mMaxZoom / CalculateIntrinsicScale(mFrameMetrics).scale;
-    float minZoom = mMinZoom / CalculateIntrinsicScale(mFrameMetrics).scale;
+    // Only do the scaling if we won't go over 8x zoom in or out.
+    bool doScale = (spanRatio > 1.0 && userZoom < mMaxZoom) ||
+                   (spanRatio < 1.0 && userZoom > mMinZoom);
 
-    bool doScale = (spanRatio > 1.0 && userZoom < maxZoom) ||
-                   (spanRatio < 1.0 && userZoom > minZoom);
+    // If this zoom will take it over 8x zoom in either direction, but it's not
+    // already there, then normalize it.
+    if (userZoom * spanRatio > mMaxZoom) {
+      spanRatio = userZoom / mMaxZoom;
+    } else if (userZoom * spanRatio < mMinZoom) {
+      spanRatio = userZoom / mMinZoom;
+    }
 
     if (doScale) {
-      if (userZoom * spanRatio > maxZoom) {
-        spanRatio = maxZoom / userZoom;
-      } else if (userZoom * spanRatio < minZoom) {
-        spanRatio = minZoom / userZoom;
-      }
-
       switch (mX.ScaleWillOverscroll(spanRatio, focusPoint.x))
       {
         case Axis::OVERSCROLL_NONE:
           break;
         case Axis::OVERSCROLL_MINUS:
         case Axis::OVERSCROLL_PLUS:
           neededDisplacement.x = -mX.ScaleWillOverscrollAmount(spanRatio, focusPoint.x);
           break;
@@ -780,17 +780,17 @@ bool AsyncPanZoomController::DoFling(con
   }
 
   bool shouldContinueFlingX = mX.FlingApplyFrictionOrCancel(aDelta),
        shouldContinueFlingY = mY.FlingApplyFrictionOrCancel(aDelta);
   // If we shouldn't continue the fling, let's just stop and repaint.
   if (!shouldContinueFlingX && !shouldContinueFlingY) {
     // Bring the resolution back in sync with the zoom, in case we scaled down
     // the zoom while accelerating.
-    SetZoomAndResolution(mFrameMetrics.mZoom);
+    SetZoomAndResolution(mFrameMetrics.mZoom.width);
     SendAsyncScrollEvent();
     RequestContentRepaint();
     mState = NOTHING;
     return false;
   }
 
   // We want to inversely scale it because when you're zoomed further in, a
   // larger swipe should move you a shorter distance.
@@ -820,20 +820,20 @@ void AsyncPanZoomController::ScrollBy(co
   CSSPoint newOffset = mFrameMetrics.mScrollOffset + aOffset;
   FrameMetrics metrics(mFrameMetrics);
   metrics.mScrollOffset = newOffset;
   mFrameMetrics = metrics;
 }
 
 void AsyncPanZoomController::ScaleWithFocus(float aZoom,
                                             const ScreenPoint& aFocus) {
-  float zoomFactor = aZoom / mFrameMetrics.mZoom.scale;
+  float zoomFactor = aZoom / mFrameMetrics.mZoom.width;
   CSSToScreenScale resolution = CalculateResolution(mFrameMetrics);
 
-  SetZoomAndResolution(ScreenToScreenScale(aZoom));
+  SetZoomAndResolution(aZoom);
 
   // If the new scale is very small, we risk multiplying in huge rounding
   // errors, so don't bother adjusting the scroll offset.
   if (resolution.scale >= 0.01f) {
     mFrameMetrics.mScrollOffset.x +=
       aFocus.x * (zoomFactor - 1.0) / resolution.scale;
     mFrameMetrics.mScrollOffset.y +=
       aFocus.y * (zoomFactor - 1.0) / resolution.scale;
@@ -953,27 +953,31 @@ const CSSRect AsyncPanZoomController::Ca
   } else if (scrollOffset.y < scrollableRect.y) {
     scrollOffset.y = scrollableRect.y;
   }
 
   CSSRect shiftedDisplayPort = displayPort + scrollOffset;
   return scrollableRect.ClampRect(shiftedDisplayPort) - scrollOffset;
 }
 
-/*static*/ CSSToScreenScale
+/*static*/ gfxSize
 AsyncPanZoomController::CalculateIntrinsicScale(const FrameMetrics& aMetrics)
 {
-  return CSSToScreenScale(gfxFloat(aMetrics.mCompositionBounds.width) /
-                          gfxFloat(aMetrics.mViewport.width));
+  gfxFloat intrinsicScale = (gfxFloat(aMetrics.mCompositionBounds.width) / 
+                             gfxFloat(aMetrics.mViewport.width));
+  return gfxSize(intrinsicScale, intrinsicScale);
 }
 
 /*static*/ CSSToScreenScale
 AsyncPanZoomController::CalculateResolution(const FrameMetrics& aMetrics)
 {
-  return CalculateIntrinsicScale(aMetrics) * aMetrics.mZoom;
+  gfxSize intrinsicScale = CalculateIntrinsicScale(aMetrics);
+  gfxSize userZoom = aMetrics.mZoom;
+  return CSSToScreenScale(intrinsicScale.width * userZoom.width,
+                          intrinsicScale.height * userZoom.height);
 }
 
 /*static*/ CSSRect
 AsyncPanZoomController::CalculateCompositedRectInCssPixels(const FrameMetrics& aMetrics)
 {
   CSSToScreenScale resolution = CalculateResolution(aMetrics);
   CSSIntRect rect = gfx::RoundedIn(aMetrics.mCompositionBounds / resolution);
   return CSSRect(rect);
@@ -1022,46 +1026,47 @@ void AsyncPanZoomController::RequestCont
   if (fabsf(oldDisplayPort.x - newDisplayPort.x) < EPSILON &&
       fabsf(oldDisplayPort.y - newDisplayPort.y) < EPSILON &&
       fabsf(oldDisplayPort.width - newDisplayPort.width) < EPSILON &&
       fabsf(oldDisplayPort.height - newDisplayPort.height) < EPSILON &&
       fabsf(mLastPaintRequestMetrics.mScrollOffset.x -
             mFrameMetrics.mScrollOffset.x) < EPSILON &&
       fabsf(mLastPaintRequestMetrics.mScrollOffset.y -
             mFrameMetrics.mScrollOffset.y) < EPSILON &&
-      mFrameMetrics.mResolution == mLastPaintRequestMetrics.mResolution) {
+      mFrameMetrics.mResolution.width == mLastPaintRequestMetrics.mResolution.width) {
     return;
   }
 
   SendAsyncScrollEvent();
 
   // Cache the zoom since we're temporarily changing it for
   // acceleration-scaled painting.
-  ScreenToScreenScale actualZoom = mFrameMetrics.mZoom;
+  gfxFloat actualZoom = mFrameMetrics.mZoom.width;
   // Calculate the factor of acceleration based on the faster of the two axes.
   float accelerationFactor =
     clamped(std::max(mX.GetAccelerationFactor(), mY.GetAccelerationFactor()),
-            MIN_ZOOM / 2.0f, MAX_ZOOM);
+            float(MIN_ZOOM) / 2.0f, float(MAX_ZOOM));
   // Scale down the resolution a bit based on acceleration.
-  mFrameMetrics.mZoom.scale /= accelerationFactor;
+  mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height =
+                              actualZoom / accelerationFactor;
 
   // This message is compressed, so fire whether or not we already have a paint
   // queued up. We need to know whether or not a paint was requested anyways,
   // for the purposes of content calling window.scrollTo().
   mPaintThrottler.PostTask(
     FROM_HERE,
     NewRunnableMethod(mGeckoContentController.get(),
                       &GeckoContentController::RequestContentRepaint,
                       mFrameMetrics));
   mFrameMetrics.mPresShellId = mLastContentPaintMetrics.mPresShellId;
   mLastPaintRequestMetrics = mFrameMetrics;
   mWaitingForContentToPaint = true;
 
   // Set the zoom back to what it was for the purpose of logic control.
-  mFrameMetrics.mZoom = actualZoom;
+  mFrameMetrics.mZoom = gfxSize(actualZoom, actualZoom);
 }
 
 void
 AsyncPanZoomController::FireAsyncScrollOnTimeout()
 {
   if (mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
     MonitorAutoLock monitor(mMonitor);
     SendAsyncScrollEvent();
@@ -1075,16 +1080,22 @@ bool AsyncPanZoomController::SampleConte
                                                             ScreenPoint& aScrollOffset) {
   // The eventual return value of this function. The compositor needs to know
   // whether or not to advance by a frame as soon as it can. For example, if a
   // fling is happening, it has to keep compositing so that the animation is
   // smooth. If an animation frame is requested, it is the compositor's
   // responsibility to schedule a composite.
   bool requestAnimationFrame = false;
 
+  const gfx3DMatrix& currentTransform = aLayer->GetTransform();
+
+  // Scales on the root layer, on what's currently painted.
+  LayerToCSSScale rootScale(currentTransform.GetXScale(),
+                            currentTransform.GetYScale());
+
   LayerPoint metricsScrollOffset;
   CSSPoint scrollOffset;
   CSSToScreenScale localScale;
   const FrameMetrics& frame = aLayer->GetFrameMetrics();
   {
     MonitorAutoLock mon(mMonitor);
 
     switch (mState) {
@@ -1097,33 +1108,34 @@ bool AsyncPanZoomController::SampleConte
       double animPosition = (aSampleTime - mAnimationStartTime) / ZOOM_TO_DURATION;
       if (animPosition > 1.0) {
         animPosition = 1.0;
       }
       // Sample the zoom at the current time point.  The sampled zoom
       // will affect the final computed resolution.
       double sampledPosition = gComputedTimingFunction->GetValue(animPosition);
 
-      ScreenToScreenScale startZoom = mStartZoomToMetrics.mZoom;
-      ScreenToScreenScale endZoom = mEndZoomToMetrics.mZoom;
-      mFrameMetrics.mZoom = ScreenToScreenScale(endZoom.scale * sampledPosition +
-                                                startZoom.scale * (1 - sampledPosition));
+      gfxFloat startZoom = mStartZoomToMetrics.mZoom.width;
+      gfxFloat endZoom = mEndZoomToMetrics.mZoom.width;
+      gfxFloat sampledZoom = (endZoom * sampledPosition +
+                              startZoom * (1 - sampledPosition));
+      mFrameMetrics.mZoom = gfxSize(sampledZoom, sampledZoom);
 
       mFrameMetrics.mScrollOffset = CSSPoint::FromUnknownPoint(gfx::Point(
         mEndZoomToMetrics.mScrollOffset.x * sampledPosition +
           mStartZoomToMetrics.mScrollOffset.x * (1 - sampledPosition),
         mEndZoomToMetrics.mScrollOffset.y * sampledPosition +
           mStartZoomToMetrics.mScrollOffset.y * (1 - sampledPosition)
       ));
 
       requestAnimationFrame = true;
 
       if (aSampleTime - mAnimationStartTime >= ZOOM_TO_DURATION) {
         // Bring the resolution in sync with the zoom.
-        SetZoomAndResolution(mFrameMetrics.mZoom);
+        SetZoomAndResolution(mFrameMetrics.mZoom.width);
         mState = NOTHING;
         SendAsyncScrollEvent();
         RequestContentRepaint();
       }
 
       break;
     }
     default:
@@ -1132,17 +1144,18 @@ bool AsyncPanZoomController::SampleConte
 
     // Current local transform; this is not what's painted but rather
     // what PZC has transformed due to touches like panning or
     // pinching. Eventually, the root layer transform will become this
     // during runtime, but we must wait for Gecko to repaint.
     localScale = CalculateResolution(mFrameMetrics);
 
     if (frame.IsScrollable()) {
-      metricsScrollOffset = frame.GetScrollOffsetInLayerPixels();
+      metricsScrollOffset = LayerPoint::FromUnknownPoint(
+        frame.GetScrollOffsetInLayerPixels());
     }
 
     scrollOffset = mFrameMetrics.mScrollOffset;
     mCurrentAsyncScrollOffset = mFrameMetrics.mScrollOffset;
   }
 
   // Cancel the mAsyncScrollTimeoutTask because we will fire a
   // mozbrowserasyncscroll event or renew the mAsyncScrollTimeoutTask again.
@@ -1166,23 +1179,18 @@ bool AsyncPanZoomController::SampleConte
   else {
     mAsyncScrollTimeoutTask =
       NewRunnableMethod(this, &AsyncPanZoomController::FireAsyncScrollOnTimeout);
     MessageLoop::current()->PostDelayedTask(FROM_HERE,
                                             mAsyncScrollTimeoutTask,
                                             mAsyncScrollTimeout);
   }
 
-  // Scales on the root layer, on what's currently painted.
-  const gfx3DMatrix& currentTransform = aLayer->GetTransform();
-  CSSToLayerScale rootScale = frame.mDevPixelsPerCSSPixel
-      / LayerToLayoutDeviceScale(currentTransform.GetXScale(), currentTransform.GetYScale());
-
-  LayerPoint translation = (scrollOffset * rootScale) - metricsScrollOffset;
-  *aNewTransform = ViewTransform(-translation, localScale / frame.mDevPixelsPerCSSPixel);
+  LayerPoint translation = (scrollOffset / rootScale) - metricsScrollOffset;
+  *aNewTransform = ViewTransform(-translation, localScale);
   aScrollOffset = scrollOffset * localScale;
 
   mLastSampleTime = aSampleTime;
 
   return requestAnimationFrame;
 }
 
 void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFrame, bool aIsFirstPaint) {
@@ -1262,17 +1270,17 @@ void AsyncPanZoomController::UpdateCompo
   mFrameMetrics.mCompositionBounds = aCompositionBounds;
 
   // If the window had 0 dimensions before, or does now, we don't want to
   // repaint or update the zoom since we'll run into rendering issues and/or
   // divide-by-zero. This manifests itself as the screen flashing. If the page
   // has gone out of view, the buffer will be cleared elsewhere anyways.
   if (aCompositionBounds.width && aCompositionBounds.height &&
       oldCompositionBounds.width && oldCompositionBounds.height) {
-    SetZoomAndResolution(mFrameMetrics.mZoom);
+    SetZoomAndResolution(mFrameMetrics.mZoom.width);
 
     // Repaint on a rotation so that our new resolution gets properly updated.
     RequestContentRepaint();
   }
 }
 
 void AsyncPanZoomController::CancelDefaultPanZoom() {
   mDisableNextTouchBatch = true;
@@ -1291,65 +1299,68 @@ void AsyncPanZoomController::ZoomToRect(
   SetState(ANIMATING_ZOOM);
 
   {
     MonitorAutoLock mon(mMonitor);
 
     ScreenIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
     CSSRect cssPageRect = mFrameMetrics.mScrollableRect;
     CSSPoint scrollOffset = mFrameMetrics.mScrollOffset;
-    float currentZoom = mFrameMetrics.mZoom.scale;
+    CSSToScreenScale resolution = CalculateResolution(mFrameMetrics);
+    gfxSize currentZoom = mFrameMetrics.mZoom;
     float targetZoom;
-    float intrinsicScale = CalculateIntrinsicScale(mFrameMetrics).scale;
+    gfxFloat targetResolution;
 
     // The minimum zoom to prevent over-zoom-out.
     // If the zoom factor is lower than this (i.e. we are zoomed more into the page),
     // then the CSS content rect, in layers pixels, will be smaller than the
     // composition bounds. If this happens, we can't fill the target composited
     // area with this frame.
-    float localMinZoom = std::max(mMinZoom,
-                         std::max(compositionBounds.width / cssPageRect.width,
-                                  compositionBounds.height / cssPageRect.height))
-                         / intrinsicScale;
-    float localMaxZoom = mMaxZoom / intrinsicScale;
+    float localMinZoom;
+    CSSRect compositedRect = CalculateCompositedRectInCssPixels(mFrameMetrics);
+    localMinZoom =
+      std::max(currentZoom.width / (cssPageRect.width / compositedRect.width),
+               currentZoom.height / (cssPageRect.height / compositedRect.height));
+    localMinZoom = std::max(localMinZoom, mMinZoom);
 
     if (!zoomToRect.IsEmpty()) {
       // Intersect the zoom-to-rect to the CSS rect to make sure it fits.
       zoomToRect = zoomToRect.Intersect(cssPageRect);
-      float targetResolution =
+      targetResolution =
         std::min(compositionBounds.width / zoomToRect.width,
                  compositionBounds.height / zoomToRect.height);
-      targetZoom = targetResolution / intrinsicScale;
+      targetZoom = float(targetResolution / resolution.scale) * currentZoom.width;
     }
     // 1. If the rect is empty, request received from browserElementScrolling.js
     // 2. currentZoom is equal to mMaxZoom and user still double-tapping it
     // 3. currentZoom is equal to localMinZoom and user still double-tapping it
     // Treat these three cases as a request to zoom out as much as possible.
     if (zoomToRect.IsEmpty() ||
-        (currentZoom == localMaxZoom && targetZoom >= localMaxZoom) ||
-        (currentZoom == localMinZoom && targetZoom <= localMinZoom)) {
-      CSSRect compositedRect = CalculateCompositedRectInCssPixels(mFrameMetrics);
+        (currentZoom.width == mMaxZoom && targetZoom >= mMaxZoom) ||
+        (currentZoom.width == localMinZoom && targetZoom <= localMinZoom)) {
+      CSSIntRect cssCompositionBounds = gfx::RoundedIn(compositionBounds / resolution);
+
       float y = scrollOffset.y;
       float newHeight =
-        cssPageRect.width * (compositedRect.height / compositedRect.width);
-      float dh = compositedRect.height - newHeight;
+        cssCompositionBounds.height * cssPageRect.width / cssCompositionBounds.width;
+      float dh = cssCompositionBounds.height - newHeight;
 
       zoomToRect = CSSRect(0.0f,
                            y + dh/2,
                            cssPageRect.width,
                            newHeight);
       zoomToRect = zoomToRect.Intersect(cssPageRect);
-      float targetResolution =
+      targetResolution =
         std::min(compositionBounds.width / zoomToRect.width,
                  compositionBounds.height / zoomToRect.height);
-      targetZoom = targetResolution / intrinsicScale;
+      targetZoom = float(targetResolution / resolution.scale) * currentZoom.width;
     }
 
-    targetZoom = clamped(targetZoom, localMinZoom, localMaxZoom);
-    mEndZoomToMetrics.mZoom = ScreenToScreenScale(targetZoom);
+    targetZoom = clamped(targetZoom, localMinZoom, mMaxZoom);
+    mEndZoomToMetrics.mZoom = gfxSize(targetZoom, targetZoom);
 
     // Adjust the zoomToRect to a sensible position to prevent overscrolling.
     FrameMetrics metricsAfterZoom = mFrameMetrics;
     metricsAfterZoom.mZoom = mEndZoomToMetrics.mZoom;
     CSSRect rectAfterZoom
       = CalculateCompositedRectInCssPixels(metricsAfterZoom);
 
     // If either of these conditions are met, the page will be
@@ -1422,32 +1433,29 @@ void AsyncPanZoomController::SetState(Pa
   mState = aState;
 }
 
 void AsyncPanZoomController::TimeoutTouchListeners() {
   mTouchListenerTimeoutTask = nullptr;
   ContentReceivedTouch(false);
 }
 
-void AsyncPanZoomController::SetZoomAndResolution(const ScreenToScreenScale& aZoom) {
+void AsyncPanZoomController::SetZoomAndResolution(float aZoom) {
   mMonitor.AssertCurrentThreadOwns();
-  mFrameMetrics.mZoom = aZoom;
+  mFrameMetrics.mZoom = gfxSize(aZoom, aZoom);
   CSSToScreenScale resolution = CalculateResolution(mFrameMetrics);
-  // We use ScreenToLayerScale(1) below in order to ask gecko to render
-  // what's currently visible on the screen. This is effectively turning
-  // the async zoom amount into the gecko zoom amount.
-  mFrameMetrics.mResolution = resolution / mFrameMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
+  mFrameMetrics.mResolution = gfxSize(resolution.scale, resolution.scale);
 }
 
 void AsyncPanZoomController::UpdateZoomConstraints(bool aAllowZoom,
                                                    float aMinZoom,
                                                    float aMaxZoom) {
   mAllowZoom = aAllowZoom;
-  mMinZoom = std::max(MIN_ZOOM, aMinZoom);
-  mMaxZoom = std::min(MAX_ZOOM, aMaxZoom);
+  mMinZoom = aMinZoom;
+  mMaxZoom = aMaxZoom;
 }
 
 void AsyncPanZoomController::PostDelayedTask(Task* aTask, int aDelayMs) {
   if (!mGeckoContentController) {
     return;
   }
 
   mGeckoContentController->PostDelayedTask(aTask, aDelayMs);
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -222,17 +222,17 @@ public:
     const gfx::Point& aVelocity,
     const gfx::Point& aAcceleration,
     double aEstimatedPaintDuration);
 
   /**
    * Return the scale factor needed to fit the viewport in |aMetrics|
    * into its composition bounds.
    */
-  static CSSToScreenScale CalculateIntrinsicScale(const FrameMetrics& aMetrics);
+  static gfxSize CalculateIntrinsicScale(const FrameMetrics& aMetrics);
 
   /**
    * Return the resolution that content should be rendered at given
    * the configuration in aFrameMetrics: viewport dimensions, zoom
    * factor, etc.  (The mResolution member of aFrameMetrics is
    * ignored.)
    */
   static CSSToScreenScale CalculateResolution(const FrameMetrics& aMetrics);
@@ -451,17 +451,17 @@ protected:
   void TimeoutTouchListeners();
 
   /**
    * Utility function that sets the zoom and resolution simultaneously. This is
    * useful when we want to repaint at the current zoom level.
    *
    * *** The monitor must be held while calling this.
    */
-  void SetZoomAndResolution(const ScreenToScreenScale& aZoom);
+  void SetZoomAndResolution(float aScale);
 
   /**
    * Timeout function for mozbrowserasyncscroll event. Because we throttle
    * mozbrowserasyncscroll events in some conditions, this function ensures
    * that the last mozbrowserasyncscroll event will be fired after a period of
    * time.
    */
   void FireAsyncScrollOnTimeout();
--- a/gfx/layers/ipc/Axis.cpp
+++ b/gfx/layers/ipc/Axis.cpp
@@ -313,17 +313,17 @@ float Axis::GetPageLength() {
   return GetRectLength(pageRect);
 }
 
 bool Axis::ScaleWillOverscrollBothSides(float aScale) {
   const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
 
   CSSRect cssContentRect = metrics.mScrollableRect;
 
-  CSSToScreenScale scale(metrics.mZoom.scale * aScale);
+  CSSToScreenScale scale(metrics.mZoom.width * aScale);
   CSSIntRect cssCompositionBounds = RoundedIn(metrics.mCompositionBounds / scale);
 
   return GetRectLength(cssContentRect) < GetRectLength(CSSRect(cssCompositionBounds));
 }
 
 AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController)
   : Axis(aAsyncPanZoomController)
 {
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -783,32 +783,16 @@ struct ParamTraits<nsIntSize>
 
   static bool Read(const Message* msg, void** iter, paramType* result)
   {
     return (ReadParam(msg, iter, &result->width) &&
             ReadParam(msg, iter, &result->height));
   }
 };
 
-template<class T, class U>
-struct ParamTraits< mozilla::gfx::ScaleFactor<T, U> >
-{
-  typedef mozilla::gfx::ScaleFactor<T, U> paramType;
-
-  static void Write(Message* msg, const paramType& param)
-  {
-    WriteParam(msg, param.scale);
-  }
-
-  static bool Read(const Message* msg, void** iter, paramType* result)
-  {
-    return (ReadParam(msg, iter, &result->scale));
-  }
-};
-
 template<class T>
 struct ParamTraits< mozilla::gfx::PointTyped<T> >
 {
   typedef mozilla::gfx::PointTyped<T> paramType;
 
   static void Write(Message* msg, const paramType& param)
   {
     WriteParam(msg, param.x);
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -10,61 +10,46 @@
 #include "mozilla/gfx/Point.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/ScaleFactor.h"
 #include "nsDeviceContext.h"
 
 namespace mozilla {
 
 struct CSSPixel;
-struct LayoutDevicePixel;
 struct LayerPixel;
 struct ScreenPixel;
 
 typedef gfx::PointTyped<CSSPixel> CSSPoint;
 typedef gfx::IntPointTyped<CSSPixel> CSSIntPoint;
 typedef gfx::SizeTyped<CSSPixel> CSSSize;
 typedef gfx::IntSizeTyped<CSSPixel> CSSIntSize;
 typedef gfx::RectTyped<CSSPixel> CSSRect;
 typedef gfx::IntRectTyped<CSSPixel> CSSIntRect;
 
-typedef gfx::PointTyped<LayoutDevicePixel> LayoutDevicePoint;
-typedef gfx::IntPointTyped<LayoutDevicePixel> LayoutDeviceIntPoint;
-typedef gfx::SizeTyped<LayoutDevicePixel> LayoutDeviceSize;
-typedef gfx::IntSizeTyped<LayoutDevicePixel> LayoutDeviceIntSize;
-typedef gfx::RectTyped<LayoutDevicePixel> LayoutDeviceRect;
-typedef gfx::IntRectTyped<LayoutDevicePixel> LayoutDeviceIntRect;
-
 typedef gfx::PointTyped<LayerPixel> LayerPoint;
 typedef gfx::IntPointTyped<LayerPixel> LayerIntPoint;
 typedef gfx::SizeTyped<LayerPixel> LayerSize;
 typedef gfx::IntSizeTyped<LayerPixel> LayerIntSize;
 typedef gfx::RectTyped<LayerPixel> LayerRect;
 typedef gfx::IntRectTyped<LayerPixel> LayerIntRect;
 
 typedef gfx::PointTyped<ScreenPixel> ScreenPoint;
 typedef gfx::IntPointTyped<ScreenPixel> ScreenIntPoint;
 typedef gfx::SizeTyped<ScreenPixel> ScreenSize;
 typedef gfx::IntSizeTyped<ScreenPixel> ScreenIntSize;
 typedef gfx::RectTyped<ScreenPixel> ScreenRect;
 typedef gfx::IntRectTyped<ScreenPixel> ScreenIntRect;
 
-typedef gfx::ScaleFactor<CSSPixel, LayoutDevicePixel> CSSToLayoutDeviceScale;
-typedef gfx::ScaleFactor<LayoutDevicePixel, CSSPixel> LayoutDeviceToCSSScale;
 typedef gfx::ScaleFactor<CSSPixel, LayerPixel> CSSToLayerScale;
 typedef gfx::ScaleFactor<LayerPixel, CSSPixel> LayerToCSSScale;
 typedef gfx::ScaleFactor<CSSPixel, ScreenPixel> CSSToScreenScale;
 typedef gfx::ScaleFactor<ScreenPixel, CSSPixel> ScreenToCSSScale;
-typedef gfx::ScaleFactor<LayoutDevicePixel, LayerPixel> LayoutDeviceToLayerScale;
-typedef gfx::ScaleFactor<LayerPixel, LayoutDevicePixel> LayerToLayoutDeviceScale;
-typedef gfx::ScaleFactor<LayoutDevicePixel, ScreenPixel> LayoutDeviceToScreenScale;
-typedef gfx::ScaleFactor<ScreenPixel, LayoutDevicePixel> ScreenToLayoutDeviceScale;
 typedef gfx::ScaleFactor<LayerPixel, ScreenPixel> LayerToScreenScale;
 typedef gfx::ScaleFactor<ScreenPixel, LayerPixel> ScreenToLayerScale;
-typedef gfx::ScaleFactor<ScreenPixel, ScreenPixel> ScreenToScreenScale;
 
 /*
  * The pixels that content authors use to specify sizes in.
  */
 struct CSSPixel {
 
   // Conversions from app units
 
@@ -108,33 +93,22 @@ struct CSSPixel {
     return nsRect(NSFloatPixelsToAppUnits(aRect.x, float(nsDeviceContext::AppUnitsPerCSSPixel())),
                   NSFloatPixelsToAppUnits(aRect.y, float(nsDeviceContext::AppUnitsPerCSSPixel())),
                   NSFloatPixelsToAppUnits(aRect.width, float(nsDeviceContext::AppUnitsPerCSSPixel())),
                   NSFloatPixelsToAppUnits(aRect.height, float(nsDeviceContext::AppUnitsPerCSSPixel())));
   }
 };
 
 /*
- * The pixels that are referred to as "device pixels" in layout code. In
- * general this is obtained by converting a value in app units value by the
- * nsDeviceContext::AppUnitsPerDevPixel() value. The size of these pixels
- * are affected by:
- * 1) the "full zoom" (see nsPresContext::SetFullZoom)
- * 2) the "widget scale" (see nsIWidget::GetDefaultScale)
- */
-struct LayoutDevicePixel {
-};
-
-/*
  * The pixels that layout rasterizes and delivers to the graphics code.
  * These are generally referred to as "device pixels" in layout code. Layer
  * pixels are affected by:
  * 1) the "display resolution" (see nsIPresShell::SetResolution)
  * 2) the "full zoom" (see nsPresContext::SetFullZoom)
- * 3) the "widget scale" (see nsIWidget::GetDefaultScale)
+ * 3) the "widget scale" (nsIWidget::GetDefaultScale)
  */
 struct LayerPixel {
 };
 
 /*
  * The pixels that are displayed on the screen.
  * On non-OMTC platforms this should be equivalent to LayerPixel units.
  * On OMTC platforms these may diverge from LayerPixel units temporarily,
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -626,20 +626,19 @@ static void RecordFrameMetrics(nsIFrame*
                                const nsRect& aViewport,
                                nsRect* aDisplayPort,
                                nsRect* aCriticalDisplayPort,
                                ViewID aScrollId,
                                const nsDisplayItem::ContainerParameters& aContainerParameters,
                                bool aMayHaveTouchListeners) {
   nsPresContext* presContext = aForFrame->PresContext();
   int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
-  LayoutDeviceToLayerScale resolution(aContainerParameters.mXScale, aContainerParameters.mYScale);
 
   nsIntRect visible = aVisibleRect.ScaleToNearestPixels(
-    resolution.scale, resolution.scale, auPerDevPixel);
+    aContainerParameters.mXScale, aContainerParameters.mYScale, auPerDevPixel);
   aRoot->SetVisibleRegion(nsIntRegion(visible));
 
   FrameMetrics metrics;
   metrics.mViewport = CSSRect::FromAppUnits(aViewport);
   if (aDisplayPort) {
     metrics.mDisplayPort = CSSRect::FromAppUnits(*aDisplayPort);
     if (aCriticalDisplayPort) {
       metrics.mCriticalDisplayPort = CSSRect::FromAppUnits(*aCriticalDisplayPort);
@@ -664,20 +663,20 @@ static void RecordFrameMetrics(nsIFrame*
   }
 
   metrics.mScrollId = aScrollId;
 
   nsIPresShell* presShell = presContext->GetPresShell();
   if (TabChild *tc = GetTabChildFrom(presShell)) {
     metrics.mZoom = tc->GetZoom();
   }
-  metrics.mResolution = resolution;
-
-  metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(
-    (float)nsPresContext::AppUnitsPerCSSPixel() / auPerDevPixel);
+  metrics.mResolution = gfxSize(presShell->GetXResolution(), presShell->GetYResolution());
+
+  metrics.mDevPixelsPerCSSPixel =
+    (float)nsPresContext::AppUnitsPerCSSPixel() / auPerDevPixel;
 
   metrics.mMayHaveTouchListeners = aMayHaveTouchListeners;
 
   if (nsIWidget* widget = aForFrame->GetNearestWidget()) {
     nsIntRect bounds;
     widget->GetBounds(bounds);
     metrics.mCompositionBounds = ScreenIntRect::FromUnknownRect(
       mozilla::gfx::IntRect(bounds.x, bounds.y, bounds.width, bounds.height));
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -162,26 +162,29 @@ ComputeShadowTreeTransform(nsIFrame* aCo
   //                                   document app units.
   //
   // So we set a compensating translation that moves the content document
   // pixels to where the user wants them to be.
   //
   nscoord auPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
   nsIntPoint scrollOffset =
     aConfig.mScrollOffset.ToNearestPixels(auPerDevPixel);
-  LayerIntPoint metricsScrollOffset = RoundedToInt(aMetrics->GetScrollOffsetInLayerPixels());
+  // metricsScrollOffset is in layer coordinates.
+  gfx::Point metricsScrollOffset = aMetrics->GetScrollOffsetInLayerPixels();
+  nsIntPoint roundedMetricsScrollOffset =
+    nsIntPoint(NS_lround(metricsScrollOffset.x), NS_lround(metricsScrollOffset.y));
 
   if (aRootFrameLoader->AsyncScrollEnabled() && !aMetrics->mDisplayPort.IsEmpty()) {
     // Only use asynchronous scrolling if it is enabled and there is a
     // displayport defined. It is useful to have a scroll layer that is
     // synchronously scrolled for identifying a scroll area before it is
     // being actively scrolled.
     nsIntPoint scrollCompensation(
-      (scrollOffset.x / aTempScaleX - metricsScrollOffset.x),
-      (scrollOffset.y / aTempScaleY - metricsScrollOffset.y));
+      (scrollOffset.x / aTempScaleX - roundedMetricsScrollOffset.x),
+      (scrollOffset.y / aTempScaleY - roundedMetricsScrollOffset.y));
 
     return ViewTransform(-scrollCompensation, aConfig.mXScale, aConfig.mYScale);
   } else {
     return ViewTransform(nsIntPoint(0, 0), 1, 1);
   }
 }
 
 // Use shadow layer tree to build display list for the browser's frame.
@@ -363,17 +366,17 @@ BuildViewMap(ViewMap& oldContentViews, V
   const ViewID scrollId = metrics.mScrollId;
   const gfx3DMatrix transform = aLayer->GetTransform();
   aXScale *= GetXScale(transform);
   aYScale *= GetYScale(transform);
 
   if (metrics.IsScrollable()) {
     nscoord auPerDevPixel = aFrameLoader->GetPrimaryFrameOfOwningContent()
                                         ->PresContext()->AppUnitsPerDevPixel();
-    nscoord auPerCSSPixel = auPerDevPixel * metrics.mDevPixelsPerCSSPixel.scale;
+    nscoord auPerCSSPixel = auPerDevPixel * metrics.mDevPixelsPerCSSPixel;
     nsContentView* view = FindViewForId(oldContentViews, scrollId);
     if (view) {
       // View already exists. Be sure to propagate scales for any values
       // that need to be calculated something in chrome-doc CSS pixels.
       ViewConfig config = view->GetViewConfig();
       aXScale *= config.mXScale;
       aYScale *= config.mYScale;
       view->mFrameLoader = aFrameLoader;
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -2109,17 +2109,17 @@ AndroidBridge::IsTablet()
     bool ret = env->CallStaticBooleanMethod(mGeckoAppShellClass, jIsTablet);
     if (jniFrame.CheckForException())
         return false;
 
     return ret;
 }
 
 void
-AndroidBridge::SetFirstPaintViewport(const LayerIntPoint& aOffset, const CSSToLayerScale& aZoom, const CSSRect& aCssPageRect)
+AndroidBridge::SetFirstPaintViewport(const LayerIntPoint& aOffset, float aZoom, const CSSRect& aCssPageRect)
 {
     AndroidGeckoLayerClient *client = mLayerClient;
     if (!client)
         return;
 
     client->SetFirstPaintViewport(aOffset, aZoom, aCssPageRect);
 }
 
@@ -2129,31 +2129,31 @@ AndroidBridge::SetPageRect(const CSSRect
     AndroidGeckoLayerClient *client = mLayerClient;
     if (!client)
         return;
 
     client->SetPageRect(aCssPageRect);
 }
 
 void
-AndroidBridge::SyncViewportInfo(const LayerIntRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution,
-                                bool aLayersUpdated, ScreenPoint& aScrollOffset, CSSToScreenScale& aScale,
+AndroidBridge::SyncViewportInfo(const LayerIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated,
+                                ScreenPoint& aScrollOffset, float& aScaleX, float& aScaleY,
                                 gfx::Margin& aFixedLayerMargins, ScreenPoint& aOffset)
 {
     AndroidGeckoLayerClient *client = mLayerClient;
     if (!client)
         return;
 
     client->SyncViewportInfo(aDisplayPort, aDisplayResolution, aLayersUpdated,
-                             aScrollOffset, aScale, aFixedLayerMargins,
+                             aScrollOffset, aScaleX, aScaleY, aFixedLayerMargins,
                              aOffset);
 }
 
 void AndroidBridge::SyncFrameMetrics(const ScreenPoint& aScrollOffset, float aZoom, const CSSRect& aCssPageRect,
-                                     bool aLayersUpdated, const CSSRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution,
+                                     bool aLayersUpdated, const CSSRect& aDisplayPort, float aDisplayResolution,
                                      bool aIsFirstPaint, gfx::Margin& aFixedLayerMargins, ScreenPoint& aOffset)
 {
     AndroidGeckoLayerClient *client = mLayerClient;
     if (!client)
         return;
 
     client->SyncFrameMetrics(aScrollOffset, aZoom, aCssPageRect,
                              aLayersUpdated, aDisplayPort, aDisplayResolution,
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -366,23 +366,23 @@ public:
     already_AddRefed<nsIMobileMessageCallback> DequeueSmsRequest(uint32_t aRequestId);
 
     bool IsTablet();
 
     void GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInfo);
     void EnableNetworkNotifications();
     void DisableNetworkNotifications();
 
-    void SetFirstPaintViewport(const LayerIntPoint& aOffset, const CSSToLayerScale& aZoom, const CSSRect& aCssPageRect);
+    void SetFirstPaintViewport(const LayerIntPoint& aOffset, float aZoom, const CSSRect& aCssPageRect);
     void SetPageRect(const CSSRect& aCssPageRect);
-    void SyncViewportInfo(const LayerIntRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution,
-                          bool aLayersUpdated, ScreenPoint& aScrollOffset, CSSToScreenScale& aScale,
+    void SyncViewportInfo(const LayerIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated,
+                          ScreenPoint& aScrollOffset, float& aScaleX, float& aScaleY,
                           gfx::Margin& aFixedLayerMargins, ScreenPoint& aOffset);
     void SyncFrameMetrics(const ScreenPoint& aScrollOffset, float aZoom, const CSSRect& aCssPageRect,
-                          bool aLayersUpdated, const CSSRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution,
+                          bool aLayersUpdated, const CSSRect& aDisplayPort, float aDisplayResolution,
                           bool aIsFirstPaint, gfx::Margin& aFixedLayerMargins, ScreenPoint& aOffset);
 
     void AddPluginView(jobject view, const gfxRect& rect, bool isFullScreen);
     void RemovePluginView(jobject view, bool isFullScreen);
 
     // These methods don't use a ScreenOrientation because it's an
     // enum and that would require including the header which requires
     // include IPC headers which requires including basictypes.h which
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -900,25 +900,25 @@ AndroidViewTransform::Init(jobject jobj)
 void
 AndroidProgressiveUpdateData::Init(jobject jobj)
 {
     NS_ABORT_IF_FALSE(wrapped_obj == nullptr, "Init called on non-null wrapped_obj!");
     wrapped_obj = jobj;
 }
 
 void
-AndroidGeckoLayerClient::SetFirstPaintViewport(const LayerIntPoint& aOffset, const CSSToLayerScale& aZoom, const CSSRect& aCssPageRect)
+AndroidGeckoLayerClient::SetFirstPaintViewport(const LayerIntPoint& aOffset, float aZoom, const CSSRect& aCssPageRect)
 {
     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.scale,
+    return env->CallVoidMethod(wrapped_obj, jSetFirstPaintViewport, (float)aOffset.x, (float)aOffset.y, aZoom,
                                aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost());
 }
 
 void
 AndroidGeckoLayerClient::SetPageRect(const CSSRect& aCssPageRect)
 {
     NS_ASSERTION(!isNull(), "SetPageRect called on null layer client!");
     JNIEnv *env = GetJNIForThread();    // this is called on the compositor thread
@@ -926,68 +926,68 @@ AndroidGeckoLayerClient::SetPageRect(con
         return;
 
     AutoLocalJNIFrame jniFrame(env, 0);
     return env->CallVoidMethod(wrapped_obj, jSetPageRect,
                                aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost());
 }
 
 void
-AndroidGeckoLayerClient::SyncViewportInfo(const LayerIntRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution,
-                                          bool aLayersUpdated, ScreenPoint& aScrollOffset, CSSToScreenScale& aScale,
+AndroidGeckoLayerClient::SyncViewportInfo(const LayerIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated,
+                                          ScreenPoint& aScrollOffset, float& aScaleX, float& aScaleY,
                                           gfx::Margin& aFixedLayerMargins, ScreenPoint& aOffset)
 {
     NS_ASSERTION(!isNull(), "SyncViewportInfo called on null layer client!");
     JNIEnv *env = GetJNIForThread();    // this is called on the compositor thread
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env);
 
     jobject viewTransformJObj = env->CallObjectMethod(wrapped_obj, jSyncViewportInfoMethod,
                                                       aDisplayPort.x, aDisplayPort.y,
                                                       aDisplayPort.width, aDisplayPort.height,
-                                                      aDisplayResolution.scale, aLayersUpdated);
+                                                      aDisplayResolution, aLayersUpdated);
     if (jniFrame.CheckForException())
         return;
 
     NS_ABORT_IF_FALSE(viewTransformJObj, "No view transform object!");
 
     AndroidViewTransform viewTransform;
     viewTransform.Init(viewTransformJObj);
 
     aScrollOffset = ScreenPoint(viewTransform.GetX(env), viewTransform.GetY(env));
-    aScale.scale = viewTransform.GetScale(env);
+    aScaleX = aScaleY = viewTransform.GetScale(env);
     viewTransform.GetFixedLayerMargins(env, aFixedLayerMargins);
 
     aOffset.x = viewTransform.GetOffsetX(env);
     aOffset.y = viewTransform.GetOffsetY(env);
 }
 
 void
 AndroidGeckoLayerClient::SyncFrameMetrics(const ScreenPoint& aScrollOffset, float aZoom, const CSSRect& aCssPageRect,
-                                          bool aLayersUpdated, const CSSRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution,
+                                          bool aLayersUpdated, const CSSRect& aDisplayPort, float aDisplayResolution,
                                           bool aIsFirstPaint, gfx::Margin& aFixedLayerMargins, ScreenPoint& aOffset)
 {
     NS_ASSERTION(!isNull(), "SyncFrameMetrics called on null layer client!");
     JNIEnv *env = GetJNIForThread();    // this is called on the compositor thread
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env);
 
     // convert the displayport rect from scroll-relative CSS pixels to document-relative device pixels
-    LayerRect dpUnrounded = aDisplayPort * aDisplayResolution;
+    LayerRect dpUnrounded = aDisplayPort * CSSToLayerScale(aDisplayResolution);
     dpUnrounded += LayerPoint::FromUnknownPoint(aScrollOffset.ToUnknownPoint());
     LayerIntRect dp = gfx::RoundedToInt(dpUnrounded);
 
     jobject viewTransformJObj = env->CallObjectMethod(wrapped_obj, jSyncFrameMetricsMethod,
             aScrollOffset.x, aScrollOffset.y, aZoom,
             aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost(),
-            aLayersUpdated, dp.x, dp.y, dp.width, dp.height, aDisplayResolution.scale,
+            aLayersUpdated, dp.x, dp.y, dp.width, dp.height, aDisplayResolution,
             aIsFirstPaint);
 
     if (jniFrame.CheckForException())
         return;
 
     NS_ABORT_IF_FALSE(viewTransformJObj, "No view transform object!");
 
     AndroidViewTransform viewTransform;
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -264,23 +264,23 @@ class AndroidGeckoLayerClient : public W
 public:
     static void InitGeckoLayerClientClass(JNIEnv *jEnv);
 
     void Init(jobject jobj);
 
     AndroidGeckoLayerClient() {}
     AndroidGeckoLayerClient(jobject jobj) { Init(jobj); }
 
-    void SetFirstPaintViewport(const LayerIntPoint& aOffset, const CSSToLayerScale& aZoom, const CSSRect& aCssPageRect);
+    void SetFirstPaintViewport(const LayerIntPoint& aOffset, float aZoom, const CSSRect& aCssPageRect);
     void SetPageRect(const CSSRect& aCssPageRect);
-    void SyncViewportInfo(const LayerIntRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution,
-                          bool aLayersUpdated, ScreenPoint& aScrollOffset, CSSToScreenScale& aScale,
+    void SyncViewportInfo(const LayerIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated,
+                          ScreenPoint& aScrollOffset, float& aScaleX, float& aScaleY,
                           gfx::Margin& aFixedLayerMargins, ScreenPoint& aOffset);
     void SyncFrameMetrics(const ScreenPoint& aScrollOffset, float aZoom, const CSSRect& aCssPageRect,
-                          bool aLayersUpdated, const CSSRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution,
+                          bool aLayersUpdated, const CSSRect& aDisplayPort, float aDisplayResolution,
                           bool aIsFirstPaint, gfx::Margin& aFixedLayerMargins, ScreenPoint& aOffset);
     bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const LayerRect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, gfx::Rect& aViewport, float& aScaleX, float& aScaleY);
     bool CreateFrame(AutoLocalJNIFrame *jniFrame, AndroidLayerRendererFrame& aFrame);
     bool ActivateProgram(AutoLocalJNIFrame *jniFrame);
     bool DeactivateProgram(AutoLocalJNIFrame *jniFrame);
     void GetDisplayPort(AutoLocalJNIFrame *jniFrame, bool aPageSizeUpdate, bool aIsBrowserContentDisplayed, int32_t tabId, nsIAndroidViewport* metrics, nsIAndroidDisplayport** displayPort);
     void ContentDocumentChanged(AutoLocalJNIFrame *jniFrame);
     bool IsContentDocumentDisplayed(AutoLocalJNIFrame *jniFrame);