Merge b2g28 to v1.3t. a=merge B2G_1_3T_20140317_MERGEDAY
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 17 Mar 2014 21:37:38 -0400
changeset 171121 106b3421bb2b0121c4054d95e529963653e0e60d
parent 171107 314c0c3a38a3c7c5815d17506338f33cd4c597ad (current diff)
parent 171120 66b284796eccfffbb0aaf7e05ece014e5bc43460 (diff)
child 171122 eb2d33b0471d26e4f2b29f591d8fe3c146f4953f
push id75
push userryanvm@gmail.com
push dateTue, 18 Mar 2014 01:44:59 +0000
reviewersmerge
milestone28.0
Merge b2g28 to v1.3t. a=merge
dom/ipc/TabChild.cpp
dom/network/src/NetworkStatsService.jsm
dom/network/src/NetworkStatsServiceProxy.js
dom/network/tests/unit_stats/test_networkstats_db.js
dom/network/tests/unit_stats/test_networkstats_service.js
dom/network/tests/unit_stats/test_networkstats_service_proxy.js
layout/base/nsDisplayList.cpp
--- a/.hgtags
+++ b/.hgtags
@@ -93,8 +93,9 @@ 5bb309998e7050c9ee80b0147de1e473f008e221
 cc37417e2c284aed960f98ffa479de4ccdd5c7c3 FIREFOX_AURORA_21_BASE
 1c070ab0f9db59f13423b9c1db60419f7a9098f9 FIREFOX_AURORA_22_BASE
 d7ce9089999719d5186595d160f25123a4e63e39 FIREFOX_AURORA_23_BASE
 8d3810543edccf4fbe458178b88dd4a6e420b010 FIREFOX_AURORA_24_BASE
 ad0ae007aa9e03cd74e9005cd6652e544139b3b5 FIREFOX_AURORA_25_BASE
 2520866d58740851d862c7c59246a4e3f8b4a176 FIREFOX_AURORA_26_BASE
 05025f4889a0bf4dc99ce0c244c750adc002f015 FIREFOX_AURORA_27_BASE
 d3997dda8ddc87fe632a7836982098584d5634b7 B2G_1_3_20140203_MERGEDAY
+e2c448e8e3b4a78fec0b37e9a078aaa254bac27b B2G_1_3_20140317_MERGEDAY
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0ab8a9cbcef5f23cec904a3d7f7675e44de29951"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="2ea2aab306bd1c941719160cdcb49ee9d755dc17"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e006b3f770f7ca77d4b007e568ccd050bd1a89e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="685f70050cb340e33811efe962b24d324c8f6f2f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="15d69a6789c638709911507f74d25c0425963636">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0ab8a9cbcef5f23cec904a3d7f7675e44de29951"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="2ea2aab306bd1c941719160cdcb49ee9d755dc17"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e006b3f770f7ca77d4b007e568ccd050bd1a89e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="685f70050cb340e33811efe962b24d324c8f6f2f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0ab8a9cbcef5f23cec904a3d7f7675e44de29951"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="2ea2aab306bd1c941719160cdcb49ee9d755dc17"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e006b3f770f7ca77d4b007e568ccd050bd1a89e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="685f70050cb340e33811efe962b24d324c8f6f2f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "remote": "", 
         "branch": "", 
         "revision": ""
     }, 
-    "revision": "947ff99ea34ba2478cbd9a99aa503bae0f03f375", 
+    "revision": "55b99ce9f94b47238f3fc6e5352f549dc462e918", 
     "repo_path": "/integration/gaia-1_3"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0ab8a9cbcef5f23cec904a3d7f7675e44de29951"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="2ea2aab306bd1c941719160cdcb49ee9d755dc17"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e006b3f770f7ca77d4b007e568ccd050bd1a89e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="9c4d35857e6a2843aa240e1465aa51780eaf99f6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="685f70050cb340e33811efe962b24d324c8f6f2f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0ab8a9cbcef5f23cec904a3d7f7675e44de29951"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="2ea2aab306bd1c941719160cdcb49ee9d755dc17"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e006b3f770f7ca77d4b007e568ccd050bd1a89e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="9c4d35857e6a2843aa240e1465aa51780eaf99f6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="685f70050cb340e33811efe962b24d324c8f6f2f"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0ab8a9cbcef5f23cec904a3d7f7675e44de29951"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="2ea2aab306bd1c941719160cdcb49ee9d755dc17"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e006b3f770f7ca77d4b007e568ccd050bd1a89e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="9c4d35857e6a2843aa240e1465aa51780eaf99f6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="685f70050cb340e33811efe962b24d324c8f6f2f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0ab8a9cbcef5f23cec904a3d7f7675e44de29951"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="2ea2aab306bd1c941719160cdcb49ee9d755dc17"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e006b3f770f7ca77d4b007e568ccd050bd1a89e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="9c4d35857e6a2843aa240e1465aa51780eaf99f6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="685f70050cb340e33811efe962b24d324c8f6f2f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="15d69a6789c638709911507f74d25c0425963636">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0ab8a9cbcef5f23cec904a3d7f7675e44de29951"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="2ea2aab306bd1c941719160cdcb49ee9d755dc17"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e006b3f770f7ca77d4b007e568ccd050bd1a89e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="685f70050cb340e33811efe962b24d324c8f6f2f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0ab8a9cbcef5f23cec904a3d7f7675e44de29951"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="2ea2aab306bd1c941719160cdcb49ee9d755dc17"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e006b3f770f7ca77d4b007e568ccd050bd1a89e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="9c4d35857e6a2843aa240e1465aa51780eaf99f6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="685f70050cb340e33811efe962b24d324c8f6f2f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -66,16 +66,17 @@
 #include "PCOMContentPermissionRequestChild.h"
 #include "PuppetWidget.h"
 #include "StructuredCloneUtils.h"
 #include "nsViewportInfo.h"
 #include "JavaScriptChild.h"
 #include "APZCCallbackHelper.h"
 #include "nsILoadContext.h"
 #include "ipc/nsGUIEventIPC.h"
+#include "UnitTransforms.h"
 
 #ifdef DEBUG
 #include "PCOMContentPermissionRequestChild.h"
 #endif /* DEBUG */
 
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
     NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
 
@@ -339,17 +340,19 @@ TabChild::Observe(nsISupports *aSubject,
         // 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.
         mLastRootMetrics.mViewport = CSSRect(CSSPoint(), kDefaultViewportSize);
-        mLastRootMetrics.mCompositionBounds = ScreenIntRect(ScreenIntPoint(), mInnerSize);
+        mLastRootMetrics.mCompositionBounds = ParentLayerIntRect(
+            ParentLayerIntPoint(),
+            ViewAs<ParentLayerPixel>(mInnerSize, PixelCastJustification::ScreenToParentLayerForRoot));
         mLastRootMetrics.mZoom = mLastRootMetrics.CalculateIntrinsicScale();
         mLastRootMetrics.mDevPixelsPerCSSPixel = mWidget->GetDefaultScale();
         // We use ScreenToLayerScale(1) below in order to turn the
         // async zoom amount into the gecko zoom amount.
         mLastRootMetrics.mCumulativeResolution =
           mLastRootMetrics.mZoom / mLastRootMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
         // This is the root layer, so the cumulative resolution is the same
         // as the resolution.
@@ -561,17 +564,19 @@ TabChild::HandlePossibleViewportChange()
 
   float oldScreenWidth = mLastRootMetrics.mCompositionBounds.width;
   if (!oldScreenWidth) {
     oldScreenWidth = mInnerSize.width;
   }
 
   FrameMetrics metrics(mLastRootMetrics);
   metrics.mViewport = CSSRect(CSSPoint(), viewport);
-  metrics.mCompositionBounds = ScreenIntRect(ScreenIntPoint(), mInnerSize);
+  metrics.mCompositionBounds = ParentLayerIntRect(
+      ParentLayerIntPoint(),
+      ViewAs<ParentLayerPixel>(mInnerSize, PixelCastJustification::ScreenToParentLayerForRoot));
 
   // This change to the zoom accounts for all types of changes I can conceive:
   // 1. screen size changes, CSS viewport does not (pages with no meta viewport
   //    or a fixed size viewport)
   // 2. screen size changes, CSS viewport also does (pages with a device-width
   //    viewport)
   // 3. screen size remains constant, but CSS viewport changes (meta viewport
   //    tag is added or removed)
@@ -610,17 +615,17 @@ TabChild::HandlePossibleViewportChange()
     // we have no idea how long painting will take.
     metrics, ScreenPoint(0.0f, 0.0f), 0.0);
   metrics.mCumulativeResolution = metrics.mZoom / metrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
   // This is the root layer, so the cumulative resolution is the same
   // as the resolution.
   metrics.mResolution = metrics.mCumulativeResolution / LayoutDeviceToParentLayerScale(1);
   utils->SetResolution(metrics.mResolution.scale, metrics.mResolution.scale);
 
-  CSSSize scrollPort = metrics.CalculateCompositedRectInCssPixels().Size();
+  CSSSize scrollPort = CSSSize(metrics.CalculateCompositedRectInCssPixels().Size());
   utils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height);
 
   // The call to GetPageSize forces a resize event to content, so we need to
   // make sure that we have the right CSS viewport and
   // scrollPositionClampingScrollPortSize set up before that happens.
 
   CSSSize pageSize = GetPageSize(document, viewport);
   if (!pageSize.width) {
@@ -1533,45 +1538,29 @@ TabChild::ProcessUpdateFrame(const Frame
         return aFrameMetrics;
     }
 
     nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
 
     FrameMetrics newMetrics = aFrameMetrics;
     APZCCallbackHelper::UpdateRootFrame(utils, newMetrics);
 
-    CSSRect cssCompositedRect = newMetrics.CalculateCompositedRectInCssPixels();
+    CSSRect cssCompositedRect = CSSRect(newMetrics.CalculateCompositedRectInCssPixels());
     // The BrowserElementScrolling helper must know about these updated metrics
     // for other functions it performs, such as double tap handling.
     // Note, %f must not be used because it is locale specific!
     nsCString data;
     data.AppendPrintf("{ \"x\" : %d", NS_lround(newMetrics.mScrollOffset.x));
     data.AppendPrintf(", \"y\" : %d", NS_lround(newMetrics.mScrollOffset.y));
     data.AppendLiteral(", \"viewport\" : ");
         data.AppendLiteral("{ \"width\" : ");
         data.AppendFloat(newMetrics.mViewport.width);
         data.AppendLiteral(", \"height\" : ");
         data.AppendFloat(newMetrics.mViewport.height);
         data.AppendLiteral(" }");
-    data.AppendLiteral(", \"displayPort\" : ");
-        data.AppendLiteral("{ \"x\" : ");
-        data.AppendFloat(newMetrics.mDisplayPort.x);
-        data.AppendLiteral(", \"y\" : ");
-        data.AppendFloat(newMetrics.mDisplayPort.y);
-        data.AppendLiteral(", \"width\" : ");
-        data.AppendFloat(newMetrics.mDisplayPort.width);
-        data.AppendLiteral(", \"height\" : ");
-        data.AppendFloat(newMetrics.mDisplayPort.height);
-        data.AppendLiteral(" }");
-    data.AppendLiteral(", \"compositionBounds\" : ");
-        data.AppendPrintf("{ \"x\" : %d", newMetrics.mCompositionBounds.x);
-        data.AppendPrintf(", \"y\" : %d", newMetrics.mCompositionBounds.y);
-        data.AppendPrintf(", \"width\" : %d", newMetrics.mCompositionBounds.width);
-        data.AppendPrintf(", \"height\" : %d", newMetrics.mCompositionBounds.height);
-        data.AppendLiteral(" }");
     data.AppendLiteral(", \"cssPageRect\" : ");
         data.AppendLiteral("{ \"x\" : ");
         data.AppendFloat(newMetrics.mScrollableRect.x);
         data.AppendLiteral(", \"y\" : ");
         data.AppendFloat(newMetrics.mScrollableRect.y);
         data.AppendLiteral(", \"width\" : ");
         data.AppendFloat(newMetrics.mScrollableRect.width);
         data.AppendLiteral(", \"height\" : ");
--- a/dom/network/src/NetworkStatsService.jsm
+++ b/dom/network/src/NetworkStatsService.jsm
@@ -36,16 +36,20 @@ const NETWORK_STATUS_READY   = 0;
 // enabled 3G since boot).
 const NETWORK_STATUS_STANDBY = 1;
 // Network is not present, but stored in database by the previous connections.
 const NETWORK_STATUS_AWAY    = 2;
 
 // The maximum traffic amount can be saved in the |cachedStats|.
 const MAX_CACHED_TRAFFIC = 500 * 1000 * 1000; // 500 MB
 
+const QUEUE_TYPE_UPDATE_STATS = 0;
+const QUEUE_TYPE_UPDATE_CACHE = 1;
+const QUEUE_TYPE_WRITE_CACHE = 2;
+
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gRil",
                                    "@mozilla.org/ril;1",
                                    "nsIRadioInterfaceLayer");
 
@@ -393,17 +397,16 @@ this.NetworkStatsService = {
    * Function called from manager to get stats from database.
    * In order to return updated stats, first is performed a call to
    * updateAllStats function, which will get last stats from netd
    * and update the database.
    * Then, depending on the request (stats per appId or total stats)
    * it retrieve them from database and return to the manager.
    */
   getSamples: function getSamples(mm, msg) {
-    let self = this;
     let network = msg.network;
     let netId = this.getNetworkId(network.id, network.type);
 
     let appId = 0;
     let appManifestURL = msg.appManifestURL;
     if (appManifestURL) {
       appId = appsService.getAppLocalIdByManifestURL(appManifestURL);
 
@@ -415,46 +418,54 @@ this.NetworkStatsService = {
       }
     }
 
     let serviceType = msg.serviceType || "";
 
     let start = new Date(msg.start);
     let end = new Date(msg.end);
 
+    let callback = (function (aError, aResult) {
+      this._db.find(function onStatsFound(aError, aResult) {
+        mm.sendAsyncMessage("NetworkStats:Get:Return",
+                            { id: msg.id, error: aError, result: aResult });
+      }, appId, serviceType, network, start, end, appManifestURL);
+    }).bind(this);
+
     this.validateNetwork(network, function onValidateNetwork(aNetId) {
       if (!aNetId) {
         mm.sendAsyncMessage("NetworkStats:Get:Return",
                             { id: msg.id, error: "Invalid connectionType", result: null });
         return;
       }
 
       // If network is currently active we need to update the cached stats first before
       // retrieving stats from the DB.
-      if (self._networks[aNetId].status == NETWORK_STATUS_READY) {
-        self.updateStats(aNetId, function onStatsUpdated(aResult, aMessage) {
-          debug("getstats for network " + network.id + " of type " + network.type);
-          debug("appId: " + appId + " from appManifestURL: " + appManifestURL);
+      if (this._networks[aNetId].status == NETWORK_STATUS_READY) {
+        debug("getstats for network " + network.id + " of type " + network.type);
+        debug("appId: " + appId + " from appManifestURL: " + appManifestURL);
+        debug("serviceType: " + serviceType);
 
-          self.updateCachedStats(function onStatsUpdated(aResult, aMessage) {
-            self._db.find(function onStatsFound(aError, aResult) {
-              mm.sendAsyncMessage("NetworkStats:Get:Return",
-                                  { id: msg.id, error: aError, result: aResult });
-            }, appId, serviceType, network, start, end, appManifestURL);
-          });
-        });
+        if (appId || serviceType) {
+          this.updateCachedStats(callback);
+          return;
+        }
+
+        this.updateStats(aNetId, function onStatsUpdated(aResult, aMessage) {
+          this.updateCachedStats(callback);
+        }.bind(this));
         return;
       }
 
       // Network not active, so no need to update
-      self._db.find(function onStatsFound(aError, aResult) {
+      this._db.find(function onStatsFound(aError, aResult) {
         mm.sendAsyncMessage("NetworkStats:Get:Return",
                             { id: msg.id, error: aError, result: aResult });
       }, appId, serviceType, network, start, end, appManifestURL);
-    });
+    }.bind(this));
   },
 
   clearInterfaceStats: function clearInterfaceStats(mm, msg) {
     let self = this;
     let network = msg.network;
 
     debug("clear stats for network " + network.id + " of type " + network.type);
 
@@ -510,85 +521,89 @@ this.NetworkStatsService = {
           mm.sendAsyncMessage("NetworkStats:ClearAll:Return",
                               { id: msg.id, error: aError, result: aResult });
         });
       });
     });
   },
 
   updateAllStats: function updateAllStats(aCallback) {
-    // Update |cachedStats|.
-    this.updateCachedStats();
-
     let elements = [];
     let lastElement = null;
+    let callback = (function (success, message) {
+      this.updateCachedStats(aCallback);
+    }).bind(this);
 
     // For each connectionType create an object containning the type
     // and the 'queueIndex', the 'queueIndex' is an integer representing
     // the index of a connection type in the global queue array. So, if
     // the connection type is already in the queue it is not appended again,
     // else it is pushed in 'elements' array, which later will be pushed to
     // the queue array.
     for (let netId in this._networks) {
       if (this._networks[netId].status != NETWORK_STATUS_READY) {
         continue;
       }
 
       lastElement = { netId: netId,
-                      queueIndex: this.updateQueueIndex(netId)};
+                      queueIndex: this.updateQueueIndex(netId) };
 
       if (lastElement.queueIndex == -1) {
-        elements.push({ netId: lastElement.netId, callbacks: [] });
+        elements.push({ netId:     lastElement.netId,
+                        callbacks: [],
+                        queueType: QUEUE_TYPE_UPDATE_STATS });
       }
     }
 
     if (!lastElement) {
       // No elements need to be updated, probably because status is different than
       // NETWORK_STATUS_READY.
       if (aCallback) {
         aCallback(true, "OK");
       }
       return;
     }
 
     if (elements.length > 0) {
       // If length of elements is greater than 0, callback is set to
       // the last element.
-      elements[elements.length - 1].callbacks.push(aCallback);
+      elements[elements.length - 1].callbacks.push(callback);
       this.updateQueue = this.updateQueue.concat(elements);
     } else {
       // Else, it means that all connection types are already in the queue to
       // be updated, so callback for this request is added to
       // the element in the main queue with the index of the last 'lastElement'.
       // But before is checked that element is still in the queue because it can
       // be processed while generating 'elements' array.
       let element = this.updateQueue[lastElement.queueIndex];
       if (aCallback &&
          (!element || element.netId != lastElement.netId)) {
         aCallback();
         return;
       }
 
-      this.updateQueue[lastElement.queueIndex].callbacks.push(aCallback);
+      this.updateQueue[lastElement.queueIndex].callbacks.push(callback);
     }
 
     // Call the function that process the elements of the queue.
     this.processQueue();
 
     if (DEBUG) {
       this.logAllRecords();
     }
   },
 
   updateStats: function updateStats(aNetId, aCallback) {
     // Check if the connection is in the main queue, push a new element
     // if it is not being processed or add a callback if it is.
     let index = this.updateQueueIndex(aNetId);
     if (index == -1) {
-      this.updateQueue.push({netId: aNetId, callbacks: [aCallback]});
+      this.updateQueue.push({ netId: aNetId,
+                              callbacks: [aCallback],
+                              queueType: QUEUE_TYPE_UPDATE_STATS });
     } else {
       this.updateQueue[index].callbacks.push(aCallback);
       return;
     }
 
     // Call the function that process the elements of the queue.
     this.processQueue();
   },
@@ -606,41 +621,49 @@ this.NetworkStatsService = {
    */
   processQueue: function processQueue(aResult, aMessage) {
     // If aResult is not undefined, the caller of the function is the result
     // of processing an element, so remove that element and call the callbacks
     // it has.
     if (aResult != undefined) {
       let item = this.updateQueue.shift();
       for (let callback of item.callbacks) {
-        if(callback) {
+        if (callback) {
           callback(aResult, aMessage);
         }
       }
     } else {
       // The caller is a function that has pushed new elements to the queue,
       // if isQueueRunning is false it means there is no processing currently
       // being done, so start.
       if (this.isQueueRunning) {
-        if(this.updateQueue.length > 1) {
-          return;
-        }
+        return;
       } else {
         this.isQueueRunning = true;
       }
     }
 
     // Check length to determine if queue is empty and stop processing.
     if (this.updateQueue.length < 1) {
       this.isQueueRunning = false;
       return;
     }
 
     // Call the update function for the next element.
-    this.update(this.updateQueue[0].netId, this.processQueue.bind(this));
+    switch (this.updateQueue[0].queueType) {
+      case QUEUE_TYPE_UPDATE_STATS:
+        this.update(this.updateQueue[0].netId, this.processQueue.bind(this));
+        break;
+      case QUEUE_TYPE_UPDATE_CACHE:
+        this.updateCache(this.processQueue.bind(this));
+        break;
+      case QUEUE_TYPE_WRITE_CACHE:
+        this.writeCache(this.updateQueue[0].stats, this.processQueue.bind(this));
+        break;
+    }
   },
 
   update: function update(aNetId, aCallback) {
     // Check if connection type is valid.
     if (!this._networks[aNetId]) {
       if (aCallback) {
         aCallback(false, "Invalid network " + aNetId);
       }
@@ -703,24 +726,21 @@ this.NetworkStatsService = {
    * Function responsible for receiving stats which are not from netd.
    */
   saveStats: function saveStats(aAppId, aServiceType, aNetwork, aTimeStamp,
                                 aRxBytes, aTxBytes, aIsAccumulative,
                                 aCallback) {
     let netId = this.convertNetworkInterface(aNetwork);
     if (!netId) {
       if (aCallback) {
-        aCallback.notify(false, "Invalid network type");
+        aCallback(false, "Invalid network type");
       }
       return;
     }
 
-    debug("saveStats: " + aAppId + " " + aServiceType + " " + netId + " " +
-          aTimeStamp + " " + aRxBytes + " " + aTxBytes);
-
     // Check if |aConnectionType|, |aAppId| and |aServiceType| are valid.
     // There are two invalid cases for the combination of |aAppId| and
     // |aServiceType|:
     // a. Both |aAppId| is non-zero and |aServiceType| is non-empty.
     // b. Both |aAppId| is zero and |aServiceType| is empty.
     if (!this._networks[netId] || (aAppId && aServiceType) ||
         (!aAppId && !aServiceType)) {
       debug("Invalid network interface, appId or serviceType");
@@ -731,114 +751,128 @@ this.NetworkStatsService = {
                   serviceType:    aServiceType,
                   networkId:      this._networks[netId].network.id,
                   networkType:    this._networks[netId].network.type,
                   date:           new Date(aTimeStamp),
                   rxBytes:        aRxBytes,
                   txBytes:        aTxBytes,
                   isAccumulative: aIsAccumulative };
 
+    this.updateQueue.push({ stats: stats,
+                            callbacks: [aCallback],
+                            queueType: QUEUE_TYPE_WRITE_CACHE });
+
+    this.processQueue();
+  },
+
+  /*
+   *
+   */
+  writeCache: function writeCache(aStats, aCallback) {
+    debug("saveStats: " + aStats.appId + " " + aStats.serviceType + " " +
+          aStats.networkId + " " + aStats.networkType + " " + aStats.date + " "
+          + aStats.date + " " + aStats.rxBytes + " " + aStats.txBytes);
+
     // Generate an unique key from |appId|, |serviceType| and |netId|,
     // which is used to retrieve data in |cachedStats|.
-    let key = stats.appId + "" + stats.serviceType + "" + netId;
+    let netId = this.getNetworkId(aStats.networkId, aStats.networkType);
+    let key = aStats.appId + "" + aStats.serviceType + "" + netId;
 
     // |cachedStats| only keeps the data with the same date.
     // If the incoming date is different from |cachedStatsDate|,
     // both |cachedStats| and |cachedStatsDate| will get updated.
-    let diff = (this._db.normalizeDate(stats.date) -
+    let diff = (this._db.normalizeDate(aStats.date) -
                 this._db.normalizeDate(this.cachedStatsDate)) /
                this._db.sampleRate;
     if (diff != 0) {
-      this.updateCachedStats(function onUpdated(success, message) {
-        this.cachedStatsDate = stats.date;
-        this.cachedStats[key] = stats;
-
-        if (!aCallback) {
-          return;
-        }
+      this.updateCache(function onUpdated(success, message) {
+        this.cachedStatsDate = aStats.date;
+        this.cachedStats[key] = aStats;
 
-        if (!success) {
-          aCallback.notify(false, message);
-          return;
+        if (aCallback) {
+          aCallback(true, "ok");
         }
-
-        aCallback.notify(true, "ok");
       }.bind(this));
-
       return;
     }
 
     // Try to find the matched row in the cached by |appId| and |connectionType|.
     // If not found, save the incoming data into the cached.
     let cachedStats = this.cachedStats[key];
     if (!cachedStats) {
-      this.cachedStats[key] = stats;
+      this.cachedStats[key] = aStats;
+      if (aCallback) {
+        aCallback(true, "ok");
+      }
       return;
     }
 
     // Find matched row, accumulate the traffic amount.
-    cachedStats.rxBytes += stats.rxBytes;
-    cachedStats.txBytes += stats.txBytes;
+    cachedStats.rxBytes += aStats.rxBytes;
+    cachedStats.txBytes += aStats.txBytes;
 
     // If new rxBytes or txBytes exceeds MAX_CACHED_TRAFFIC
     // the corresponding row will be saved to indexedDB.
     // Then, the row will be removed from the cached.
     if (cachedStats.rxBytes > MAX_CACHED_TRAFFIC ||
         cachedStats.txBytes > MAX_CACHED_TRAFFIC) {
-      this._db.saveStats(cachedStats,
-        function (error, result) {
-          debug("Application stats inserted in indexedDB");
+      this._db.saveStats(cachedStats, function (error, result) {
+        debug("Application stats inserted in indexedDB");
+        if (aCallback) {
+          aCallback(true, "ok");
         }
-      );
+      });
       delete this.cachedStats[key];
+      return;
+    }
+
+    if (aCallback) {
+      aCallback(true, "ok");
     }
   },
 
   updateCachedStats: function updateCachedStats(aCallback) {
-    debug("updateCachedStats: " + this.cachedStatsDate);
+    this.updateQueue.push({ callbacks: [aCallback],
+                            queueType: QUEUE_TYPE_UPDATE_CACHE });
+
+    this.processQueue();
+  },
+
+  updateCache: function updateCache(aCallback) {
+    debug("updateCache: " + this.cachedStatsDate);
 
     let stats = Object.keys(this.cachedStats);
     if (stats.length == 0) {
       // |cachedStats| is empty, no need to update.
       if (aCallback) {
         aCallback(true, "no need to update");
       }
-
       return;
     }
 
     let index = 0;
     this._db.saveStats(this.cachedStats[stats[index]],
-      function onSavedStats(error, result) {
-        if (DEBUG) {
-          debug("Application stats inserted in indexedDB");
-        }
+                       function onSavedStats(error, result) {
+      debug("Application stats inserted in indexedDB");
 
-        // Clean up the |cachedStats| after updating.
-        if (index == stats.length - 1) {
-          this.cachedStats = Object.create(null);
-
-          if (!aCallback) {
-            return;
-          }
+      // Clean up the |cachedStats| after updating.
+      if (index == stats.length - 1) {
+        this.cachedStats = Object.create(null);
 
-          if (error) {
-            aCallback(false, error);
-            return;
-          }
-
+        if (aCallback) {
           aCallback(true, "ok");
-          return;
         }
+        return;
+      }
 
-        // Update is not finished, keep updating.
-        index += 1;
-        this._db.saveStats(this.cachedStats[stats[index]],
-                           onSavedStats.bind(this, error, result));
-      }.bind(this));
+      // Update is not finished, keep updating.
+      index += 1;
+      this._db.saveStats(this.cachedStats[stats[index]],
+                         onSavedStats.bind(this, error, result));
+    }.bind(this));
   },
 
   get maxCachedTraffic () {
     return MAX_CACHED_TRAFFIC;
   },
 
   logAllRecords: function logAllRecords() {
     this._db.logAllRecords(function onResult(aError, aResult) {
--- a/dom/network/src/NetworkStatsServiceProxy.js
+++ b/dom/network/src/NetworkStatsServiceProxy.js
@@ -39,16 +39,20 @@ NetworkStatsServiceProxy.prototype = {
       return;
     }
 
     if (DEBUG) {
       debug("saveAppStats: " + aAppId + " " + aNetwork.type + " " + aTimeStamp +
             " " + aRxBytes + " " + aTxBytes + " " + aIsAccumulative);
     }
 
+    if (aCallback) {
+      aCallback = aCallback.notify;
+    }
+
     NetworkStatsService.saveStats(aAppId, "", aNetwork, aTimeStamp,
                                   aRxBytes, aTxBytes, aIsAccumulative,
                                   aCallback);
   },
 
   /*
    * Function called in the points of different system services
    * to pass the per-serive stats to NetworkStatsService.
@@ -64,16 +68,20 @@ NetworkStatsServiceProxy.prototype = {
     }
 
     if (DEBUG) {
       debug("saveServiceStats: " + aServiceType + " " + aNetwork.type + " " +
             aTimeStamp + " " + aRxBytes + " " + aTxBytes + " " +
             aIsAccumulative);
     }
 
+    if (aCallback) {
+      aCallback = aCallback.notify;
+    }
+
     NetworkStatsService.saveStats(0, aServiceType ,aNetwork, aTimeStamp,
                                   aRxBytes, aTxBytes, aIsAccumulative,
                                   aCallback);
   },
 
   classID : NETWORKSTATSSERVICEPROXY_CID,
   QueryInterface : XPCOMUtils.generateQI([nsINetworkStatsServiceProxy]),
 }
--- a/dom/network/tests/unit_stats/test_networkstats_db.js
+++ b/dom/network/tests/unit_stats/test_networkstats_db.js
@@ -180,21 +180,18 @@ add_test(function test_internalSaveStats
       do_check_eq(result[0].rxTotalBytes, stats.rxTotalBytes);
       do_check_eq(result[0].txTotalBytes, stats.txTotalBytes);
       run_next_test();
     });
   });
 });
 
 add_test(function test_internalSaveStats_arraySamples() {
-  var networks = getNetworks();
-
-  netStatsDb.clearStats(networks, function (error, result) {
-    do_check_eq(error, null);
-
+  clearStore('net_stats_store', function() {
+    var networks = getNetworks();
     var network = [networks[0].id, networks[0].type];
 
     var samples = 2;
     var stats = [];
     for (var i = 0; i < samples; i++) {
       stats.push({ appId:         0,
                    serviceType:   "",
                    network:       network,
@@ -210,22 +207,19 @@ add_test(function test_internalSaveStats
     netStatsDb.dbNewTxn("net_stats_store", "readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, stats);
     }, function(error, result) {
       do_check_eq(error, null);
 
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
 
-        // Result has one sample more than samples because clear inserts
-        // an empty sample to keep totalBytes synchronized with netd counters
-        result.shift();
         do_check_eq(result.length, samples);
         var success = true;
-        for (var i = 1; i < samples; i++) {
+        for (var i = 0; i < samples; i++) {
           if (result[i].appId != stats[i].appId ||
               result[i].serviceType != stats[i].serviceType ||
               !compareNetworks(result[i].network, stats[i].network) ||
               result[i].timestamp != stats[i].timestamp ||
               result[i].rxBytes != stats[i].rxBytes ||
               result[i].txBytes != stats[i].txBytes ||
               result[i].rxSystemBytes != stats[i].rxSystemBytes ||
               result[i].txSystemBytes != stats[i].txSystemBytes ||
@@ -238,21 +232,18 @@ add_test(function test_internalSaveStats
         do_check_true(success);
         run_next_test();
       });
     });
   });
 });
 
 add_test(function test_internalRemoveOldStats() {
-  var networks = getNetworks();
-
-  netStatsDb.clearStats(networks, function (error, result) {
-    do_check_eq(error, null);
-
+  clearStore('net_stats_store', function() {
+    var networks = getNetworks();
     var network = [networks[0].id, networks[0].type];
     var samples = 10;
     var stats = [];
     for (var i = 0; i < samples - 1; i++) {
       stats.push({ appId:               0, serviceType: "",
                    network:       network, timestamp:     Date.now() + (10 * i),
                    rxBytes:             0, txBytes:       0,
                    rxSystemBytes:    1234, txSystemBytes: 1234,
@@ -279,18 +270,17 @@ add_test(function test_internalRemoveOld
 
         run_next_test();
       });
     });
   });
 });
 
 function processSamplesDiff(networks, lastStat, newStat, callback) {
-  netStatsDb.clearStats(networks, function (error, result){
-    do_check_eq(error, null);
+  clearStore('net_stats_store', function() {
     netStatsDb.dbNewTxn("net_stats_store", "readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, lastStat);
     }, function(error, result) {
       netStatsDb.dbNewTxn("net_stats_store", "readwrite", function(txn, store) {
         let request = store.index("network").openCursor(newStat.network, "prev");
         request.onsuccess = function onsuccess(event) {
           let cursor = event.target.result;
           do_check_neq(cursor, null);
@@ -455,39 +445,33 @@ add_test(function test_saveAppStats() {
                 serviceType:    "",
                 networkId:      networks[0].id,
                 networkType:    networks[0].type,
                 date:           new Date(),
                 rxBytes:        2234,
                 txBytes:        2234,
                 isAccumulative: false };
 
-  netStatsDb.clearStats(networks, function (error, result) {
-    do_check_eq(error, null);
+  clearStore('net_stats_store', function() {
     netStatsDb.saveStats(stats, function(error, result) {
       do_check_eq(error, null);
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
-        // The clear function clears all records of the datbase but
-        // inserts a new element for each [appId, connectionId, connectionType]
-        // record to keep the track of rxTotalBytes / txTotalBytes.
-        // So at this point, we have two records, one for the appId 0 used in
-        // past tests and the new one for appId 1
-        do_check_eq(result.length, 2);
-        do_check_eq(result[1].appId, stats.appId);
-        do_check_eq(result[1].serviceType, stats.serviceType);
-        do_check_true(compareNetworks(result[1].network, network));
+        do_check_eq(result.length, 1);
+        do_check_eq(result[0].appId, stats.appId);
+        do_check_eq(result[0].serviceType, stats.serviceType);
+        do_check_true(compareNetworks(result[0].network, network));
         let timestamp = filterTimestamp(stats.date);
-        do_check_eq(result[1].timestamp, timestamp);
-        do_check_eq(result[1].rxBytes, stats.rxBytes);
-        do_check_eq(result[1].txBytes, stats.txBytes);
-        do_check_eq(result[1].rxSystemBytes, 0);
-        do_check_eq(result[1].txSystemBytes, 0);
-        do_check_eq(result[1].rxTotalBytes, 0);
-        do_check_eq(result[1].txTotalBytes, 0);
+        do_check_eq(result[0].timestamp, timestamp);
+        do_check_eq(result[0].rxBytes, stats.rxBytes);
+        do_check_eq(result[0].txBytes, stats.txBytes);
+        do_check_eq(result[0].rxSystemBytes, 0);
+        do_check_eq(result[0].txSystemBytes, 0);
+        do_check_eq(result[0].rxTotalBytes, 0);
+        do_check_eq(result[0].txTotalBytes, 0);
         run_next_test();
       });
     });
   });
 });
 
 add_test(function test_saveServiceStats() {
   var networks = getNetworks();
@@ -497,46 +481,41 @@ add_test(function test_saveServiceStats(
                 serviceType:    "FakeType",
                 networkId:      networks[0].id,
                 networkType:    networks[0].type,
                 date:           new Date(),
                 rxBytes:        2234,
                 txBytes:        2234,
                 isAccumulative: false };
 
-  netStatsDb.clearStats(networks, function (error, result) {
-    do_check_eq(error, null);
+  clearStore('net_stats_store', function() {
     netStatsDb.saveStats(stats, function(error, result) {
       do_check_eq(error, null);
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
-        // Again, at this point, we have two records, one for the appId 0 and 
-        // empty serviceType used in past tests and the new one for appId 0 and
-        // non-empty serviceType.
-        do_check_eq(result.length, 2);
-        do_check_eq(result[1].appId, stats.appId);
-        do_check_eq(result[1].serviceType, stats.serviceType);
-        do_check_true(compareNetworks(result[1].network, network));
+        do_check_eq(result.length, 1);
+        do_check_eq(result[0].appId, stats.appId);
+        do_check_eq(result[0].serviceType, stats.serviceType);
+        do_check_true(compareNetworks(result[0].network, network));
         let timestamp = filterTimestamp(stats.date);
-        do_check_eq(result[1].timestamp, timestamp);
-        do_check_eq(result[1].rxBytes, stats.rxBytes);
-        do_check_eq(result[1].txBytes, stats.txBytes);
-        do_check_eq(result[1].rxSystemBytes, 0);
-        do_check_eq(result[1].txSystemBytes, 0);
-        do_check_eq(result[1].rxTotalBytes, 0);
-        do_check_eq(result[1].txTotalBytes, 0);
+        do_check_eq(result[0].timestamp, timestamp);
+        do_check_eq(result[0].rxBytes, stats.rxBytes);
+        do_check_eq(result[0].txBytes, stats.txBytes);
+        do_check_eq(result[0].rxSystemBytes, 0);
+        do_check_eq(result[0].txSystemBytes, 0);
+        do_check_eq(result[0].rxTotalBytes, 0);
+        do_check_eq(result[0].txTotalBytes, 0);
         run_next_test();
       });
     });
   });
 });
 
-function prepareFind(network, stats, callback) {
-  netStatsDb.clearStats(network, function (error, result) {
-    do_check_eq(error, null);
+function prepareFind(stats, callback) {
+  clearStore('net_stats_store', function() {
     netStatsDb.dbNewTxn("net_stats_store", "readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, stats);
     }, function(error, result) {
         callback(error, result);
     });
   });
 }
 
@@ -564,17 +543,17 @@ add_test(function test_find () {
 
     stats.push({ appId:                 appId, serviceType:   serviceType,
                  network:       networkMobile, timestamp:     saveDate + (sampleRate * i),
                  rxBytes:                   0, txBytes:       10,
                  rxSystemBytes:             0, txSystemBytes: 0,
                  rxTotalBytes:              0, txTotalBytes:  0 });
   }
 
-  prepareFind(networks[0], stats, function(error, result) {
+  prepareFind(stats, function(error, result) {
     do_check_eq(error, null);
     netStatsDb.find(function (error, result) {
       do_check_eq(error, null);
       do_check_eq(result.serviceType, serviceType);
       do_check_eq(result.network.id, networks[0].id);
       do_check_eq(result.network.type, networks[0].type);
       do_check_eq(result.start.getTime(), start.getTime());
       do_check_eq(result.end.getTime(), end.getTime());
@@ -608,17 +587,17 @@ add_test(function test_findAppStats () {
                  rxTotalBytes:           0, txTotalBytes: 0 });
 
     stats.push({ appId:                appId, serviceType:  serviceType,
                  network:      networkMobile, timestamp:    saveDate + (sampleRate * i),
                  rxBytes:                  0, txBytes:      10,
                  rxTotalBytes:             0, txTotalBytes: 0 });
   }
 
-  prepareFind(networks[0], stats, function(error, result) {
+  prepareFind(stats, function(error, result) {
     do_check_eq(error, null);
     netStatsDb.find(function (error, result) {
       do_check_eq(error, null);
       do_check_eq(result.serviceType, serviceType);
       do_check_eq(result.network.id, networks[0].id);
       do_check_eq(result.network.type, networks[0].type);
       do_check_eq(result.start.getTime(), start.getTime());
       do_check_eq(result.end.getTime(), end.getTime());
@@ -652,17 +631,17 @@ add_test(function test_findServiceStats 
                  rxTotalBytes:           0, txTotalBytes: 0 });
 
     stats.push({ appId:                appId, serviceType:  serviceType,
                  network:      networkMobile, timestamp:    saveDate + (sampleRate * i),
                  rxBytes:                  0, txBytes:      10,
                  rxTotalBytes:             0, txTotalBytes: 0 });
   }
 
-  prepareFind(networks[0], stats, function(error, result) {
+  prepareFind(stats, function(error, result) {
     do_check_eq(error, null);
     netStatsDb.find(function (error, result) {
       do_check_eq(error, null);
       do_check_eq(result.serviceType, serviceType);
       do_check_eq(result.network.id, networks[0].id);
       do_check_eq(result.network.type, networks[0].type);
       do_check_eq(result.start.getTime(), start.getTime());
       do_check_eq(result.end.getTime(), end.getTime());
@@ -727,31 +706,24 @@ add_test(function test_saveMultipleAppSt
     rxBytes:                  0, txBytes:        10,
     serviceType:             "", isAccumulative: false
   };
 
   let keys = Object.keys(cached);
   let index = 0;
 
   networks.push(networkMobile);
-  netStatsDb.clearStats(networks, function (error, result) {
-    do_check_eq(error, null);
+
+  clearStore('net_stats_store', function() {
     netStatsDb.saveStats(cached[keys[index]],
       function callback(error, result) {
         do_check_eq(error, null);
 
         if (index == keys.length - 1) {
           netStatsDb.logAllRecords(function(error, result) {
-            // Again, result has two samples more than expected samples because
-            // clear inserts one empty sample for each network to keep totalBytes
-            // synchronized with netd counters. so the first two samples have to
-            // be discarted.
-            result.shift();
-            result.shift();
-
             do_check_eq(error, null);
             do_check_eq(result.length, 6);
             do_check_eq(result[0].serviceType, serviceType);
             do_check_eq(result[3].appId, 1);
             do_check_true(compareNetworks(result[0].network, [networkWifi.id, networkWifi.type]));
             do_check_eq(result[0].rxBytes, 0);
             do_check_eq(result[0].txBytes, 10);
             run_next_test();
--- a/dom/network/tests/unit_stats/test_networkstats_service.js
+++ b/dom/network/tests/unit_stats/test_networkstats_service.js
@@ -2,16 +2,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 const NETWORK_STATUS_READY   = 0;
 const NETWORK_STATUS_STANDBY = 1;
 const NETWORK_STATUS_AWAY    = 2;
 
+const QUEUE_TYPE_UPDATE_STATS = 0;
+
 var wifiId = '00';
 
 function getNetworks(callback) {
   NetworkStatsService._db.getAvailableNetworks(function onGetNetworks(aError, aResult) {
     callback(aError, aResult);
   });
 }
 
@@ -75,21 +77,21 @@ add_test(function test_update() {
     NetworkStatsService.update(netId, function (success, msg) {
       do_check_eq(success, true);
       run_next_test();
     });
   });
 });
 
 add_test(function test_updateQueueIndex() {
-  NetworkStatsService.updateQueue = [{netId: 0, callbacks: null},
-                                     {netId: 1, callbacks: null},
-                                     {netId: 2, callbacks: null},
-                                     {netId: 3, callbacks: null},
-                                     {netId: 4, callbacks: null}];
+  NetworkStatsService.updateQueue = [{netId: 0, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
+                                     {netId: 1, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
+                                     {netId: 2, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
+                                     {netId: 3, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS},
+                                     {netId: 4, callbacks: null, queueType: QUEUE_TYPE_UPDATE_STATS}];
   var index = NetworkStatsService.updateQueueIndex(3);
   do_check_eq(index, 3);
   index = NetworkStatsService.updateQueueIndex(10);
   do_check_eq(index, -1);
 
   NetworkStatsService.updateQueue = [];
   run_next_test();
 });
--- a/dom/network/tests/unit_stats/test_networkstats_service_proxy.js
+++ b/dom/network/tests/unit_stats/test_networkstats_service_proxy.js
@@ -31,181 +31,170 @@ function mokConvertNetworkInterface() {
 
     return netId;
   };
 }
 
 add_test(function test_saveAppStats() {
   var cachedStats = NetworkStatsService.cachedStats;
   var timestamp = NetworkStatsService.cachedStatsDate.getTime();
-  var samples = 5;
 
   // Create to fake nsINetworkInterfaces. As nsINetworkInterface can not
   // be instantiated, these two vars will emulate it by filling the properties
   // that will be used.
   var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
   var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
 
   // Insert fake mobile network interface in NetworkStatsService
   var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
 
   do_check_eq(Object.keys(cachedStats).length, 0);
 
-  for (var i = 0; i < samples; i++) {
-    nssProxy.saveAppStats(1, wifi, timestamp, 10, 20, false);
-
-    nssProxy.saveAppStats(1, mobile, timestamp, 10, 20, false);
-  }
-
-  var key1 = 1 + "" + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
-  var key2 = 1 + "" + mobileNetId + "";
+  nssProxy.saveAppStats(1, wifi, timestamp, 10, 20, false,
+                        function (success, message) {
+    do_check_eq(success, true);
+    nssProxy.saveAppStats(1, mobile, timestamp, 10, 20, false,
+                          function (success, message) {
+      var key1 = 1 + "" + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
+      var key2 = 1 + "" + mobileNetId + "";
 
-  do_check_eq(Object.keys(cachedStats).length, 2);
-  do_check_eq(cachedStats[key1].appId, 1);
-  do_check_eq(cachedStats[key1].serviceType.length, 0);
-  do_check_eq(cachedStats[key1].networkId, wifi.id);
-  do_check_eq(cachedStats[key1].networkType, wifi.type);
-  do_check_eq(new Date(cachedStats[key1].date).getTime() / 1000,
-              Math.floor(timestamp / 1000));
-  do_check_eq(cachedStats[key1].rxBytes, 50);
-  do_check_eq(cachedStats[key1].txBytes, 100);
-  do_check_eq(cachedStats[key2].appId, 1);
-  do_check_eq(cachedStats[key1].serviceType.length, 0);
-  do_check_eq(cachedStats[key2].networkId, mobile.id);
-  do_check_eq(cachedStats[key2].networkType, mobile.type);
-  do_check_eq(new Date(cachedStats[key2].date).getTime() / 1000,
-              Math.floor(timestamp / 1000));
-  do_check_eq(cachedStats[key2].rxBytes, 50);
-  do_check_eq(cachedStats[key2].txBytes, 100);
+      do_check_eq(Object.keys(cachedStats).length, 2);
+      do_check_eq(cachedStats[key1].appId, 1);
+      do_check_eq(cachedStats[key1].serviceType.length, 0);
+      do_check_eq(cachedStats[key1].networkId, wifi.id);
+      do_check_eq(cachedStats[key1].networkType, wifi.type);
+      do_check_eq(new Date(cachedStats[key1].date).getTime() / 1000,
+                  Math.floor(timestamp / 1000));
+      do_check_eq(cachedStats[key1].rxBytes, 10);
+      do_check_eq(cachedStats[key1].txBytes, 20);
+      do_check_eq(cachedStats[key2].appId, 1);
+      do_check_eq(cachedStats[key1].serviceType.length, 0);
+      do_check_eq(cachedStats[key2].networkId, mobile.id);
+      do_check_eq(cachedStats[key2].networkType, mobile.type);
+      do_check_eq(new Date(cachedStats[key2].date).getTime() / 1000,
+                  Math.floor(timestamp / 1000));
+      do_check_eq(cachedStats[key2].rxBytes, 10);
+      do_check_eq(cachedStats[key2].txBytes, 20);
 
-  run_next_test();
+      run_next_test();
+    });
+  });
 });
 
 add_test(function test_saveServiceStats() {
   var timestamp = NetworkStatsService.cachedStatsDate.getTime();
-  var samples = 5;
 
   // Create to fake nsINetworkInterfaces. As nsINetworkInterface can not
   // be instantiated, these two vars will emulate it by filling the properties
   // that will be used.
   var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
   var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
 
   // Insert fake mobile network interface in NetworkStatsService
   var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
 
-  NetworkStatsService.updateCachedStats(
-    function (success, msg) {
-      do_check_eq(success, true);
+  NetworkStatsService.updateCachedStats(function (success, msg) {
+    do_check_eq(success, true);
 
-      var cachedStats = NetworkStatsService.cachedStats;
-      do_check_eq(Object.keys(cachedStats).length, 0);
+    var cachedStats = NetworkStatsService.cachedStats;
+    do_check_eq(Object.keys(cachedStats).length, 0);
 
-      var serviceType = 'FakeType';
-      for (var i = 0; i < samples; i++) {
-        nssProxy.saveServiceStats(serviceType, wifi, timestamp, 10, 20, false);
-
-        nssProxy.saveServiceStats(serviceType, mobile, timestamp, 10, 20, false);
-      }
-
-      var key1 = 0 + "" + serviceType +
-                 NetworkStatsService.getNetworkId(wifi.id, wifi.type);
-      var key2 = 0 + "" + serviceType + mobileNetId + "";
+    var serviceType = 'FakeType';
+    nssProxy.saveServiceStats(serviceType, wifi, timestamp, 10, 20, false,
+                              function (success, message) {
+      do_check_eq(success, true);
+      nssProxy.saveServiceStats(serviceType, mobile, timestamp, 10, 20, false,
+                                function (success, message) {
+        do_check_eq(success, true);
+        var key1 = 0 + "" + serviceType +
+                   NetworkStatsService.getNetworkId(wifi.id, wifi.type);
+        var key2 = 0 + "" + serviceType + mobileNetId + "";
 
-      do_check_eq(Object.keys(cachedStats).length, 2);
-      do_check_eq(cachedStats[key1].appId, 0);
-      do_check_eq(cachedStats[key1].serviceType, serviceType);
-      do_check_eq(cachedStats[key1].networkId, wifi.id);
-      do_check_eq(cachedStats[key1].networkType, wifi.type);
-      do_check_eq(new Date(cachedStats[key1].date).getTime() / 1000,
-                  Math.floor(timestamp / 1000));
-      do_check_eq(cachedStats[key1].rxBytes, 50);
-      do_check_eq(cachedStats[key1].txBytes, 100);
-      do_check_eq(cachedStats[key2].appId, 0);
-      do_check_eq(cachedStats[key1].serviceType, serviceType);
-      do_check_eq(cachedStats[key2].networkId, mobile.id);
-      do_check_eq(cachedStats[key2].networkType, mobile.type);
-      do_check_eq(new Date(cachedStats[key2].date).getTime() / 1000,
-                  Math.floor(timestamp / 1000));
-      do_check_eq(cachedStats[key2].rxBytes, 50);
-      do_check_eq(cachedStats[key2].txBytes, 100);
-    }
-  );
+        do_check_eq(Object.keys(cachedStats).length, 2);
+        do_check_eq(cachedStats[key1].appId, 0);
+        do_check_eq(cachedStats[key1].serviceType, serviceType);
+        do_check_eq(cachedStats[key1].networkId, wifi.id);
+        do_check_eq(cachedStats[key1].networkType, wifi.type);
+        do_check_eq(new Date(cachedStats[key1].date).getTime() / 1000,
+                    Math.floor(timestamp / 1000));
+        do_check_eq(cachedStats[key1].rxBytes, 10);
+        do_check_eq(cachedStats[key1].txBytes, 20);
+        do_check_eq(cachedStats[key2].appId, 0);
+        do_check_eq(cachedStats[key1].serviceType, serviceType);
+        do_check_eq(cachedStats[key2].networkId, mobile.id);
+        do_check_eq(cachedStats[key2].networkType, mobile.type);
+        do_check_eq(new Date(cachedStats[key2].date).getTime() / 1000,
+                    Math.floor(timestamp / 1000));
+        do_check_eq(cachedStats[key2].rxBytes, 10);
+        do_check_eq(cachedStats[key2].txBytes, 20);
 
-  run_next_test();
+        run_next_test();
+      });
+    });
+  });
 });
 
 add_test(function test_saveStatsWithDifferentDates() {
   var today = NetworkStatsService.cachedStatsDate;
   var tomorrow = new Date(today.getTime() + (24 * 60 * 60 * 1000));
 
-  var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
   var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
 
-  var key = 1 + "" + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
-
-  NetworkStatsService.updateCachedStats(
-    function (success, msg) {
-      do_check_eq(success, true);
-
-      do_check_eq(Object.keys(NetworkStatsService.cachedStats).length, 0);
+  NetworkStatsService.updateCachedStats(function (success, message) {
+    do_check_eq(success, true);
 
-      nssProxy.saveAppStats(1, wifi, today.getTime(), 10, 20, false);
-
-      nssProxy.saveAppStats(1, mobile, today.getTime(), 10, 20, false);
-
-      var saveStatsCb = {
-        notify: function notify(success, message) {
-          do_check_eq(success, true);
+    do_check_eq(Object.keys(NetworkStatsService.cachedStats).length, 0);
+    nssProxy.saveAppStats(1, mobile, today.getTime(), 10, 20, false,
+                          function (success, message) {
+      do_check_eq(success, true);
+      nssProxy.saveAppStats(2, mobile, tomorrow.getTime(), 30, 40, false,
+                            function (success, message) {
+        do_check_eq(success, true);
 
-          var cachedStats = NetworkStatsService.cachedStats;
-          var key = 2 + "" +
-                    NetworkStatsService.getNetworkId(mobile.id, mobile.type);
-          do_check_eq(Object.keys(cachedStats).length, 1);
-          do_check_eq(cachedStats[key].appId, 2);
-          do_check_eq(cachedStats[key].networkId, mobile.id);
-          do_check_eq(cachedStats[key].networkType, mobile.type);
-          do_check_eq(new Date(cachedStats[key].date).getTime() / 1000,
-                      Math.floor(tomorrow.getTime() / 1000));
-          do_check_eq(cachedStats[key].rxBytes, 30);
-          do_check_eq(cachedStats[key].txBytes, 40);
+        var cachedStats = NetworkStatsService.cachedStats;
+        var key = 2 + "" +
+                  NetworkStatsService.getNetworkId(mobile.id, mobile.type);
+        do_check_eq(Object.keys(cachedStats).length, 1);
+        do_check_eq(cachedStats[key].appId, 2);
+        do_check_eq(cachedStats[key].networkId, mobile.id);
+        do_check_eq(cachedStats[key].networkType, mobile.type);
+        do_check_eq(new Date(cachedStats[key].date).getTime() / 1000,
+                    Math.floor(tomorrow.getTime() / 1000));
+        do_check_eq(cachedStats[key].rxBytes, 30);
+        do_check_eq(cachedStats[key].txBytes, 40);
 
-          run_next_test();
-        }
-      };
-
-      nssProxy.saveAppStats(2, mobile, tomorrow.getTime(), 30, 40, false,
-                            saveStatsCb);
-    }
-  );
+        run_next_test();
+      });
+    });
+  });
 });
 
 add_test(function test_saveStatsWithMaxCachedTraffic() {
   var timestamp = NetworkStatsService.cachedStatsDate.getTime();
   var maxtraffic = NetworkStatsService.maxCachedTraffic;
   var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
 
-  NetworkStatsService.updateCachedStats(
-    function (success, msg) {
-      do_check_eq(success, true);
-
-      var cachedStats = NetworkStatsService.cachedStats;
-      do_check_eq(Object.keys(cachedStats).length, 0);
-
-      nssProxy.saveAppStats(1, wifi, timestamp, 10, 20, false);
+  NetworkStatsService.updateCachedStats(function (success, message) {
+    do_check_eq(success, true);
 
+    var cachedStats = NetworkStatsService.cachedStats;
+    do_check_eq(Object.keys(cachedStats).length, 0);
+    nssProxy.saveAppStats(1, wifi, timestamp, 10, 20, false,
+                          function (success, message) {
+      do_check_eq(success, true);
       do_check_eq(Object.keys(cachedStats).length, 1);
-
-      nssProxy.saveAppStats(1, wifi, timestamp, maxtraffic, 20, false);
+      nssProxy.saveAppStats(1, wifi, timestamp, maxtraffic, 20, false,
+                            function (success, message) {
+        do_check_eq(success, true);
+        do_check_eq(Object.keys(cachedStats).length, 0);
 
-      do_check_eq(Object.keys(cachedStats).length, 0);
-
-      run_next_test();
-    }
-  );
+        run_next_test();
+      });
+    });
+  });
 });
 
 function run_test() {
   do_get_profile();
 
   Cu.import("resource://gre/modules/NetworkStatsService.jsm");
 
   // Function convertNetworkInterface of NetworkStatsService causes errors when dealing
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -582,16 +582,17 @@ struct ParamTraits<mozilla::layers::Fram
     WriteParam(aMsg, aParam.mZoom);
     WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel);
     WriteParam(aMsg, aParam.mMayHaveTouchListeners);
     WriteParam(aMsg, aParam.mPresShellId);
     WriteParam(aMsg, aParam.mIsRoot);
     WriteParam(aMsg, aParam.mHasScrollgrab);
     WriteParam(aMsg, aParam.mUpdateScrollOffset);
     WriteParam(aMsg, aParam.mScrollGeneration);
+    WriteParam(aMsg, aParam.mTransformScale);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     return (ReadParam(aMsg, aIter, &aResult->mScrollableRect) &&
             ReadParam(aMsg, aIter, &aResult->mViewport) &&
             ReadParam(aMsg, aIter, &aResult->mScrollOffset) &&
             ReadParam(aMsg, aIter, &aResult->mDisplayPort) &&
@@ -602,17 +603,18 @@ struct ParamTraits<mozilla::layers::Fram
             ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) &&
             ReadParam(aMsg, aIter, &aResult->mZoom) &&
             ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) &&
             ReadParam(aMsg, aIter, &aResult->mMayHaveTouchListeners) &&
             ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
             ReadParam(aMsg, aIter, &aResult->mIsRoot) &&
             ReadParam(aMsg, aIter, &aResult->mHasScrollgrab) &&
             ReadParam(aMsg, aIter, &aResult->mUpdateScrollOffset) &&
-            ReadParam(aMsg, aIter, &aResult->mScrollGeneration));
+            ReadParam(aMsg, aIter, &aResult->mScrollGeneration) &&
+            ReadParam(aMsg, aIter, &aResult->mTransformScale));
   }
 };
 
 template<>
 struct ParamTraits<mozilla::layers::TextureFactoryIdentifier>
 {
   typedef mozilla::layers::TextureFactoryIdentifier paramType;
 
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -12,27 +12,47 @@
 #include "mozilla/gfx/Rect.h"           // for RoundedIn
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
 
 namespace IPC {
 template <typename T> struct ParamTraits;
 } // namespace IPC
 
 namespace mozilla {
-namespace layers {
 
-// The layer coordinates of the parent layer. Like the layer coordinates
-// of the current layer (LayerPixel) but doesn't include the current layer's
-// resolution.
+// The layer coordinates of the parent layer.
+// This can be arrived at in two ways:
+//   - Start with the CSS coordinates of the parent layer (note: NOT the
+//     CSS coordinates of the current layer, that will give you the wrong
+//     answer), multiply by the device scale and the resolutions of all
+//     layers from the root down to and including the parent.
+//   - Start with global screen coordinates and unapply all CSS and async
+//     transforms from the root down to and including the parent.
+// It's helpful to look at https://wiki.mozilla.org/Platform/GFX/APZ#Coordinate_systems
+// to get a picture of how the various coordinate systems relate to each other.
 struct ParentLayerPixel {};
 
+typedef gfx::PointTyped<ParentLayerPixel> ParentLayerPoint;
+typedef gfx::RectTyped<ParentLayerPixel> ParentLayerRect;
+typedef gfx::SizeTyped<ParentLayerPixel> ParentLayerSize;
+
+typedef gfx::IntMarginTyped<ParentLayerPixel> ParentLayerIntMargin;
+typedef gfx::IntPointTyped<ParentLayerPixel> ParentLayerIntPoint;
+typedef gfx::IntRectTyped<ParentLayerPixel> ParentLayerIntRect;
+typedef gfx::IntSizeTyped<ParentLayerPixel> ParentLayerIntSize;
+
+typedef gfx::ScaleFactor<CSSPixel, ParentLayerPixel> CSSToParentLayerScale;
 typedef gfx::ScaleFactor<LayoutDevicePixel, ParentLayerPixel> LayoutDeviceToParentLayerScale;
+typedef gfx::ScaleFactor<ScreenPixel, ParentLayerPixel> ScreenToParentLayerScale;
+
 typedef gfx::ScaleFactor<ParentLayerPixel, LayerPixel> ParentLayerToLayerScale;
+typedef gfx::ScaleFactor<ParentLayerPixel, ScreenPixel> ParentLayerToScreenScale;
 
-typedef gfx::ScaleFactor<ParentLayerPixel, ScreenPixel> ParentLayerToScreenScale;
+
+namespace layers {
 
 /**
  * The viewport and displayport metrics for the painted frame at the
  * time of a layer-tree transaction.  These metrics are especially
  * useful for shadow layers, because the metrics values are updated
  * atomically with new pixels.
  */
 struct FrameMetrics {
@@ -50,16 +70,17 @@ public:
     , mCriticalDisplayPort(0, 0, 0, 0)
     , mViewport(0, 0, 0, 0)
     , mScrollOffset(0, 0)
     , mScrollId(NULL_SCROLL_ID)
     , mScrollableRect(0, 0, 0, 0)
     , mResolution(1)
     , mCumulativeResolution(1)
     , mZoom(1)
+    , mTransformScale(1)
     , mDevPixelsPerCSSPixel(1)
     , mPresShellId(-1)
     , mMayHaveTouchListeners(false)
     , mIsRoot(false)
     , mHasScrollgrab(false)
     , mUpdateScrollOffset(false)
     , mScrollGeneration(0)
   {}
@@ -126,44 +147,52 @@ public:
   // because the scrollableRect can be smaller if the content is not large
   // and the scrollableRect hasn't been updated yet.
   // We move the scrollableRect up because we don't know if we can move it
   // down. i.e. we know that scrollableRect can go back as far as zero.
   // but we don't know how much further ahead it can go.
   CSSRect GetExpandedScrollableRect() const
   {
     CSSRect scrollableRect = mScrollableRect;
-    CSSRect compBounds = CalculateCompositedRectInCssPixels();
+    CSSRect compBounds = CSSRect(CalculateCompositedRectInCssPixels());
     if (scrollableRect.width < compBounds.width) {
       scrollableRect.x = std::max(0.f,
                                   scrollableRect.x - (compBounds.width - scrollableRect.width));
       scrollableRect.width = compBounds.width;
     }
 
     if (scrollableRect.height < compBounds.height) {
       scrollableRect.y = std::max(0.f,
                                   scrollableRect.y - (compBounds.height - scrollableRect.height));
       scrollableRect.height = compBounds.height;
     }
 
     return scrollableRect;
   }
 
-  /**
-   * Return the scale factor needed to fit the viewport
-   * into its composition bounds.
-   */
+  // Return the scale factor needed to fit the viewport
+  // into its composition bounds.
   CSSToScreenScale CalculateIntrinsicScale() const
   {
     return CSSToScreenScale(float(mCompositionBounds.width) / float(mViewport.width));
   }
 
-  CSSRect CalculateCompositedRectInCssPixels() const
+  // Return the scale factor for converting from CSS pixels (for this layer)
+  // to layer pixels of our parent layer. Much as mZoom is used to interface
+  // between inputs we get in screen pixels and quantities in CSS pixels,
+  // this is used to interface between mCompositionBounds and quantities
+  // in CSS pixels.
+  CSSToParentLayerScale GetZoomToParent() const
   {
-    return CSSRect(gfx::RoundedIn(mCompositionBounds / mZoom));
+    return mZoom * mTransformScale;
+  }
+
+  CSSIntRect CalculateCompositedRectInCssPixels() const
+  {
+    return gfx::RoundedIn(mCompositionBounds / GetZoomToParent());
   }
 
   // ---------------------------------------------------------------------------
   // The following metrics are all in widget space/device pixels.
   //
 
   // This is the area within the widget that we're compositing to. It is relative
   // to the layer tree origin.
@@ -175,17 +204,17 @@ public:
   // where we're prerendering the wrong regions and the content may be clipped,
   // or too much of it prerendered. If the composition dimensions are the same as the
   // viewport dimensions, there is no need for this and we can just use the viewport
   // instead.
   //
   // This value is valid for nested scrollable layers as well, and is still
   // relative to the layer tree origin. This value is provided by Gecko at
   // layout/paint time.
-  ScreenIntRect mCompositionBounds;
+  ParentLayerIntRect mCompositionBounds;
 
   // ---------------------------------------------------------------------------
   // The following metrics are all in CSS pixels. They are not in any uniform
   // space, so each is explained separately.
   //
 
   // The area of a frame's contents that has been painted, relative to the
   // viewport. It is in the same coordinate space as |mViewport|. For example,
@@ -273,16 +302,27 @@ public:
   LayoutDeviceToLayerScale mCumulativeResolution;
 
   // The "user zoom". Content is painted by gecko at mResolution * mDevPixelsPerCSSPixel,
   // but will be drawn to the screen at mZoom. In the steady state, the
   // two will be the same, but during an async zoom action the two may
   // diverge. This information is initialized in Gecko but updated in the APZC.
   CSSToScreenScale mZoom;
 
+  // The conversion factor between local screen pixels (the coordinate
+  // system in which APZCs receive input events) and our parent layer's
+  // layer pixels (the coordinate system of mCompositionBounds).
+  // This consists of the scale of the local CSS transform and the
+  // nontransient async transform.
+  // TODO: APZ does not currently work well if there is a CSS transform
+  //       on the layer being scrolled that's not just a scale that's
+  //       the same in both directions. When we fix this, mTransformScale
+  //       will probably need to turn into a matrix.
+  ScreenToParentLayerScale mTransformScale;
+
   // 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;
 
   uint32_t mPresShellId;
 
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -470,18 +470,18 @@ ClientLayerManager::GetBackendName(nsASt
     case LAYERS_D3D10: aName.AssignLiteral("Direct3D 10"); return;
     case LAYERS_D3D11: aName.AssignLiteral("Direct3D 11"); return;
     default: NS_RUNTIMEABORT("Invalid backend");
   }
 }
 
 bool
 ClientLayerManager::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent,
-                                              ScreenRect& aCompositionBounds,
-                                              CSSToScreenScale& aZoom,
+                                              ParentLayerRect& aCompositionBounds,
+                                              CSSToParentLayerScale& aZoom,
                                               bool aDrawingCritical)
 {
   aZoom.scale = 1.0;
 #ifdef MOZ_WIDGET_ANDROID
   Layer* primaryScrollable = GetPrimaryScrollableLayer();
   if (primaryScrollable) {
     const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
 
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -130,18 +130,18 @@ public:
    * aDrawingCritical will be true if the current drawing operation is using
    * the critical displayport.
    * Returns true if the update should continue, or false if it should be
    * cancelled.
    * This is only called if gfxPlatform::UseProgressiveTilePainting() returns
    * true.
    */
   bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent,
-                                 ScreenRect& aCompositionBounds,
-                                 CSSToScreenScale& aZoom,
+                                 ParentLayerRect& aCompositionBounds,
+                                 CSSToParentLayerScale& aZoom,
                                  bool aDrawingCritical);
 
 #ifdef DEBUG
   bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
   bool InDrawing() { return mPhase == PHASE_DRAWING; }
   bool InForward() { return mPhase == PHASE_FORWARD; }
 #endif
   bool InTransaction() { return mPhase != PHASE_NONE; }
--- a/gfx/layers/client/ClientTiledThebesLayer.cpp
+++ b/gfx/layers/client/ClientTiledThebesLayer.cpp
@@ -1,15 +1,16 @@
 /* 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 "ClientTiledThebesLayer.h"
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "Units.h"                      // for ScreenIntRect, CSSPoint, etc
+#include "UnitTransforms.h"             // for TransformTo
 #include "ClientLayerManager.h"         // for ClientLayerManager, etc
 #include "gfx3DMatrix.h"                // for gfx3DMatrix
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "gfxPoint.h"                   // for gfxSize
 #include "gfxRect.h"                    // for gfxRect
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Rect.h"           // for Rect, RectTyped
@@ -39,21 +40,19 @@ ClientTiledThebesLayer::~ClientTiledTheb
 
 void
 ClientTiledThebesLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
 {
   aAttrs = ThebesLayerAttributes(GetValidRegion());
 }
 
 static LayoutDeviceRect
-ApplyScreenToLayoutTransform(const gfx3DMatrix& aTransform, const ScreenRect& aScreenRect)
+ApplyParentLayerToLayoutTransform(const gfx3DMatrix& aTransform, const ParentLayerRect& aParentLayerRect)
 {
-  gfxRect input(aScreenRect.x, aScreenRect.y, aScreenRect.width, aScreenRect.height);
-  gfxRect output = aTransform.TransformBounds(input);
-  return LayoutDeviceRect(output.x, output.y, output.width, output.height);
+  return TransformTo<LayoutDevicePixel>(aTransform, aParentLayerRect);
 }
 
 void
 ClientTiledThebesLayer::BeginPaint()
 {
   if (ClientManager()->IsRepeatTransaction()) {
     return;
   }
@@ -76,38 +75,40 @@ ClientTiledThebesLayer::BeginPaint()
     //     composition bounds to empty so that progressive updates are disabled.
     NS_WARNING("Tiled Thebes layer with no scrollable container parent");
     mPaintData.mCompositionBounds.SetEmpty();
     return;
   }
 
   const FrameMetrics& metrics = scrollParent->GetFrameMetrics();
 
-  // Calculate the transform required to convert screen space into transformed
-  // layout device space.
-  gfx3DMatrix layoutToScreen = GetEffectiveTransform();
+  // Calculate the transform required to convert parent layer space into
+  // transformed layout device space.
+  gfx3DMatrix layoutToParentLayer = GetEffectiveTransform();
   for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
     if (parent->UseIntermediateSurface()) {
-      layoutToScreen *= parent->GetEffectiveTransform();
+      layoutToParentLayer *= parent->GetEffectiveTransform();
     }
   }
-  layoutToScreen.ScalePost(metrics.mCumulativeResolution.scale,
-                           metrics.mCumulativeResolution.scale,
-                           1.f);
+  layoutToParentLayer.ScalePost(metrics.GetParentResolution().scale,
+                                metrics.GetParentResolution().scale,
+                                1.f);
 
-  mPaintData.mTransformScreenToLayout = layoutToScreen.Inverse();
+  mPaintData.mTransformParentLayerToLayout = layoutToParentLayer.Inverse();
 
   // Compute the critical display port in layer space.
   mPaintData.mLayoutCriticalDisplayPort.SetEmpty();
   if (!metrics.mCriticalDisplayPort.IsEmpty()) {
     // Convert the display port to screen space first so that we can transform
     // it into layout device space.
-    const ScreenRect& criticalDisplayPort = metrics.mCriticalDisplayPort * metrics.mZoom;
+    const ParentLayerRect& criticalDisplayPort = metrics.mCriticalDisplayPort
+                                               * metrics.mDevPixelsPerCSSPixel
+                                               * metrics.GetParentResolution();
     LayoutDeviceRect transformedCriticalDisplayPort =
-      ApplyScreenToLayoutTransform(mPaintData.mTransformScreenToLayout, criticalDisplayPort);
+      ApplyParentLayerToLayoutTransform(mPaintData.mTransformParentLayerToLayout, criticalDisplayPort);
     mPaintData.mLayoutCriticalDisplayPort =
       LayoutDeviceIntRect::ToUntyped(RoundedOut(transformedCriticalDisplayPort));
   }
 
   // Calculate the frame resolution. Because this is Gecko-side, before any
   // async transforms have occurred, we can use mZoom for this.
   mPaintData.mResolution = metrics.mZoom;
 
@@ -115,18 +116,18 @@ ClientTiledThebesLayer::BeginPaint()
   // composition bounds.
   mPaintData.mCompositionBounds.SetEmpty();
   mPaintData.mScrollOffset.MoveTo(0, 0);
   Layer* primaryScrollable = ClientManager()->GetPrimaryScrollableLayer();
   if (primaryScrollable) {
     const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
     mPaintData.mScrollOffset = metrics.mScrollOffset * metrics.mZoom;
     mPaintData.mCompositionBounds =
-      ApplyScreenToLayoutTransform(mPaintData.mTransformScreenToLayout,
-                                   ScreenRect(metrics.mCompositionBounds));
+      ApplyParentLayerToLayoutTransform(mPaintData.mTransformParentLayerToLayout,
+                                        ParentLayerRect(metrics.mCompositionBounds));
   }
 }
 
 void
 ClientTiledThebesLayer::EndPaint(bool aFinish)
 {
   if (!aFinish && !mPaintData.mPaintFinished) {
     return;
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -415,18 +415,18 @@ BasicTiledLayerBuffer::ValidateTile(Basi
 #endif
     aTile = ValidateTileInternal(aTile, aTileOrigin, *rect);
   }
 
   return aTile;
 }
 
 static LayoutDeviceRect
-TransformCompositionBounds(const ScreenRect& aCompositionBounds,
-                           const CSSToScreenScale& aZoom,
+TransformCompositionBounds(const ParentLayerRect& aCompositionBounds,
+                           const CSSToParentLayerScale& aZoom,
                            const ScreenPoint& aScrollOffset,
                            const CSSToScreenScale& aResolution,
                            const gfx3DMatrix& aTransformScreenToLayout)
 {
   // Transform the current composition bounds into transformed layout device
   // space by compensating for the difference in resolution and subtracting the
   // old composition bounds origin.
   ScreenRect offsetViewportRect = (aCompositionBounds / aZoom) * aResolution;
@@ -467,35 +467,35 @@ BasicTiledLayerBuffer::ComputeProgressiv
 
   // Find out if we have any non-stale content to update.
   nsIntRegion staleRegion;
   staleRegion.And(aInvalidRegion, aOldValidRegion);
 
   // Find out the current view transform to determine which tiles to draw
   // first, and see if we should just abort this paint. Aborting is usually
   // caused by there being an incoming, more relevant paint.
-  ScreenRect compositionBounds;
-  CSSToScreenScale zoom;
+  ParentLayerRect compositionBounds;
+  CSSToParentLayerScale zoom;
   if (mManager->ProgressiveUpdateCallback(!staleRegion.Contains(aInvalidRegion),
                                           compositionBounds, zoom,
                                           !drawingLowPrecision)) {
     // We ignore if front-end wants to abort if this is the first,
     // non-low-precision paint, as in that situation, we're about to override
     // front-end's page/viewport metrics.
     if (!aPaintData->mFirstPaint || drawingLowPrecision) {
       PROFILER_LABEL("ContentClient", "Abort painting");
       aRegionToPaint.SetEmpty();
       return aIsRepeated;
     }
   }
 
   // Transform the screen coordinates into transformed layout device coordinates.
   LayoutDeviceRect transformedCompositionBounds =
     TransformCompositionBounds(compositionBounds, zoom, aPaintData->mScrollOffset,
-                            aPaintData->mResolution, aPaintData->mTransformScreenToLayout);
+                               aPaintData->mResolution, aPaintData->mTransformParentLayerToLayout);
 
   // Paint tiles that have stale content or that intersected with the screen
   // at the time of issuing the draw command in a single transaction first.
   // This is to avoid rendering glitches on animated page content, and when
   // layers change size/shape.
   LayoutDeviceRect coherentUpdateRect =
     transformedCompositionBounds.Intersect(aPaintData->mCompositionBounds);
 
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -113,20 +113,20 @@ struct BasicTiledLayerPaintData {
   /*
    * The scroll offset of the content from the nearest ancestor layer that
    * represents scrollable content with a display port set, for the last
    * layer update transaction.
    */
   ScreenPoint mLastScrollOffset;
 
   /*
-   * The transform matrix to go from Screen units to transformed LayoutDevice
-   * units.
+   * The transform matrix to go from ParentLayer units to transformed
+   * LayoutDevice units.
    */
-  gfx3DMatrix mTransformScreenToLayout;
+  gfx3DMatrix mTransformParentLayerToLayout;
 
   /*
    * The critical displayport of the content from the nearest ancestor layer
    * that represents scrollable content with a display port set. Empty if a
    * critical displayport is not set.
    *
    * This is in transformed LayoutDevice coordinates, but is stored as an
    * nsIntRect for convenience when intersecting with the layer's mValidRegion.
--- a/gfx/layers/composite/APZCTreeManager.cpp
+++ b/gfx/layers/composite/APZCTreeManager.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/layers/AsyncPanZoomController.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/mozalloc.h"           // for operator new
 #include "mozilla/TouchEvents.h"
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
 #include "nsThreadUtils.h"              // for NS_IsMainThread
+#include "UnitTransforms.h"             // for ViewAs
 
 #include <algorithm>                    // for std::stable_sort
 
 #define APZC_LOG(...)
 // #define APZC_LOG(...) printf_stderr("APZC: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
@@ -109,30 +110,32 @@ APZCTreeManager::UpdatePanZoomController
                                              bool aIsFirstPaint, uint64_t aFirstPaintLayersId,
                                              nsTArray< nsRefPtr<AsyncPanZoomController> >* aApzcsToDestroy)
 {
   mTreeLock.AssertCurrentThreadOwns();
 
   ContainerLayer* container = aLayer->AsContainerLayer();
   AsyncPanZoomController* apzc = nullptr;
   if (container) {
-    if (container->GetFrameMetrics().IsScrollable()) {
+    const FrameMetrics& metrics = container->GetFrameMetrics();
+    if (metrics.IsScrollable()) {
       const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
       if (state && state->mController.get()) {
         // If we get here, aLayer is a scrollable container layer and somebody
         // has registered a GeckoContentController for it, so we need to ensure
         // it has an APZC instance to manage its scrolling.
 
         apzc = container->GetAsyncPanZoomController();
 
         // If the content represented by the container layer has changed (which may
         // be possible because of DLBI heuristics) then we don't want to keep using
         // the same old APZC for the new content. Null it out so we run through the
         // code to find another one or create one.
-        if (apzc && !apzc->Matches(ScrollableLayerGuid(aLayersId, container->GetFrameMetrics()))) {
+        ScrollableLayerGuid guid(aLayersId, metrics);
+        if (apzc && !apzc->Matches(guid)) {
           apzc = nullptr;
         }
 
         // If the container doesn't have an APZC already, try to find one of our
         // pre-existing ones that matches. In particular, if we find an APZC whose
         // ScrollableLayerGuid is the same, then we know what happened is that the
         // layout of the page changed causing the layer tree to be rebuilt, but the
         // underlying content for which the APZC was originally created is still
@@ -163,29 +166,32 @@ APZCTreeManager::UpdatePanZoomController
           // building the tree. Also remove it from the set of APZCs that are going
           // to be destroyed, because it's going to remain active.
           aApzcsToDestroy->RemoveElement(apzc);
           apzc->SetPrevSibling(nullptr);
           apzc->SetLastChild(nullptr);
         }
         APZC_LOG("Using APZC %p for layer %p with identifiers %lld %lld\n", apzc, aLayer, aLayersId, container->GetFrameMetrics().mScrollId);
 
-        apzc->NotifyLayersUpdated(container->GetFrameMetrics(),
+        apzc->NotifyLayersUpdated(metrics,
                                   aIsFirstPaint && (aLayersId == aFirstPaintLayersId));
 
         // Use the composition bounds as the hit test region.
         // Optionally, the GeckoContentController can provide a touch-sensitive
         // region that constrains all frames associated with the controller.
         // In this case we intersect the composition bounds with that region.
-        ScreenRect visible(container->GetFrameMetrics().mCompositionBounds);
+        ParentLayerRect visible(metrics.mCompositionBounds);
         CSSRect touchSensitiveRegion;
         if (state->mController->GetTouchSensitiveRegion(&touchSensitiveRegion)) {
+          // Note: we assume here that touchSensitiveRegion is in the CSS pixels
+          // of our parent layer, which makes this coordinate conversion
+          // correct.
           visible = visible.Intersect(touchSensitiveRegion
-                                      * container->GetFrameMetrics().LayersPixelsPerCSSPixel()
-                                      * LayerToScreenScale(1.0));
+                                      * metrics.mDevPixelsPerCSSPixel
+                                      * metrics.GetParentResolution());
         }
         apzc->SetLayerHitTestData(visible, aTransform, aLayer->GetTransform());
         APZC_LOG("Setting rect(%f %f %f %f) as visible region for APZC %p\n", visible.x, visible.y,
                                                                               visible.width, visible.height,
                                                                               apzc);
 
         // Bind the APZC instance into the tree of APZCs
         if (aNextSibling) {
@@ -811,35 +817,31 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPan
   mTreeLock.AssertCurrentThreadOwns();
 
   // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
   // explained in the comment on GetInputTransforms. This function will recurse with aApzc at L and P, and the
   // comments explain what values are stored in the variables at these two levels. All the comments
   // use standard matrix notation where the leftmost matrix in a multiplication is applied first.
 
   // ancestorUntransform takes points from aApzc's parent APZC's layer coordinates
-  // to aApzc's screen coordinates.
+  // to aApzc's parent layer's layer coordinates.
   // It is OC.Inverse() * NC.Inverse() * MC.Inverse() at recursion level for L,
   //   and RC.Inverse() * QC.Inverse()                at recursion level for P.
   gfx3DMatrix ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
 
-  // hitTestTransform takes points from aApzc's parent APZC's layer coordinates to
-  // the hit test space (which is aApzc's transient coordinates).
-  // It is OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LN.Inverse() at L,
-  //   and RC.Inverse() * QC.Inverse() * PC.Inverse() * PN.Inverse()                at P.
-  gfx3DMatrix hitTestTransform = ancestorUntransform
-                               * aApzc->GetCSSTransform().Inverse()
-                               * aApzc->GetNontransientAsyncTransform().Inverse();
-  gfxPoint hitTestPointForThisLayer = hitTestTransform.ProjectPoint(aHitTestPoint);
+  // Hit testing for this layer takes place in our parent layer coordinates,
+  // since the composition bounds (used to initialize the visible rect against
+  // which we hit test are in those coordinates).
+  gfxPoint hitTestPointForThisLayer = ancestorUntransform.ProjectPoint(aHitTestPoint);
   APZC_LOG("Untransformed %f %f to transient coordinates %f %f for hit-testing APZC %p\n",
            aHitTestPoint.x, aHitTestPoint.y,
            hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, aApzc);
 
   // childUntransform takes points from aApzc's parent APZC's layer coordinates
-  // to aApzc's layer coordinates (which are aApzc's children's screen coordinates).
+  // to aApzc's layer coordinates (which are aApzc's children's ParentLayer coordinates).
   // It is OC.Inverse() * NC.Inverse() * MC.Inverse() * LC.Inverse() * LA.Inverse() at L
   //   and RC.Inverse() * QC.Inverse() * PC.Inverse() * PA.Inverse()                at P.
   gfx3DMatrix childUntransform = ancestorUntransform
                                * aApzc->GetCSSTransform().Inverse()
                                * gfx3DMatrix(aApzc->GetCurrentAsyncTransform()).Inverse();
   gfxPoint hitTestPointForChildLayers = childUntransform.ProjectPoint(aHitTestPoint);
   APZC_LOG("Untransformed %f %f to layer coordinates %f %f for APZC %p\n",
            aHitTestPoint.x, aHitTestPoint.y,
@@ -848,17 +850,17 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPan
   // This walks the tree in depth-first, reverse order, so that it encounters
   // APZCs front-to-back on the screen.
   for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
     AsyncPanZoomController* match = GetAPZCAtPoint(child, hitTestPointForChildLayers);
     if (match) {
       return match;
     }
   }
-  if (aApzc->VisibleRegionContains(ScreenPoint(hitTestPointForThisLayer.x, hitTestPointForThisLayer.y))) {
+  if (aApzc->VisibleRegionContains(ViewAs<ParentLayerPixel>(hitTestPointForThisLayer))) {
     APZC_LOG("Successfully matched untransformed point %f %f to visible region for APZC %p\n",
              hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, aApzc);
     return aApzc;
   }
   return nullptr;
 }
 
 /* This function sets the aTransformToApzcOut and aTransformToGeckoOut out-parameters
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -361,17 +361,17 @@ ContainerRender(ContainerT* aContainer,
                                         transform);
   }
 
   if (aContainer->GetFrameMetrics().IsScrollable()) {
     gfx::Matrix4x4 transform;
     ToMatrix4x4(aContainer->GetEffectiveTransform(), transform);
 
     const FrameMetrics& frame = aContainer->GetFrameMetrics();
-    LayerRect layerBounds = ScreenRect(frame.mCompositionBounds) * ScreenToLayerScale(1.0);
+    LayerRect layerBounds = ParentLayerRect(frame.mCompositionBounds) * ParentLayerToLayerScale(1.0);
     gfx::Rect rect(layerBounds.x, layerBounds.y, layerBounds.width, layerBounds.height);
     gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
     aManager->GetCompositor()->DrawDiagnostics(DIAGNOSTIC_CONTAINER,
                                                rect, clipRect,
                                                transform);
   }
 }
 
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -10,16 +10,17 @@
 #include <algorithm>                    // for max, min
 #include "AnimationCommon.h"            // for ComputedTimingFunction
 #include "AsyncPanZoomController.h"     // for AsyncPanZoomController, etc
 #include "CompositorParent.h"           // for CompositorParent
 #include "FrameMetrics.h"               // for FrameMetrics, etc
 #include "GestureEventListener.h"       // for GestureEventListener
 #include "InputData.h"                  // for MultiTouchInput, etc
 #include "Units.h"                      // for CSSRect, CSSPoint, etc
+#include "UnitTransforms.h"             // for TransformTo
 #include "base/message_loop.h"          // for MessageLoop
 #include "base/task.h"                  // for NewRunnableMethod, etc
 #include "base/tracked.h"               // for FROM_HERE
 #include "gfxTypes.h"                   // for gfxFloat
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/BasicEvents.h"        // for Modifiers, MODIFIER_*
 #include "mozilla/ClearOnShutdown.h"    // for ClearOnShutdown
 #include "mozilla/Constants.h"          // for M_PI
@@ -708,17 +709,17 @@ nsEventStatus AsyncPanZoomController::On
 
 nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
   APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
   if (!mZoomConstraints.mAllowZoom) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
   SetState(PINCHING);
-  mLastZoomFocus = aEvent.mFocusPoint - mFrameMetrics.mCompositionBounds.TopLeft();
+  mLastZoomFocus = ToParentLayerCoords(aEvent.mFocusPoint) - mFrameMetrics.mCompositionBounds.TopLeft();
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
   APZC_LOG("%p got a scale in state %d\n", this, mState);
   if (mState != PINCHING) {
     return nsEventStatus_eConsumeNoDefault;
@@ -730,18 +731,18 @@ nsEventStatus AsyncPanZoomController::On
     return nsEventStatus_eConsumeNoDefault;
   }
 
   float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
 
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
-    CSSToScreenScale userZoom = mFrameMetrics.mZoom;
-    ScreenPoint focusPoint = aEvent.mFocusPoint - mFrameMetrics.mCompositionBounds.TopLeft();
+    CSSToParentLayerScale userZoom = mFrameMetrics.GetZoomToParent();
+    ParentLayerPoint focusPoint = ToParentLayerCoords(aEvent.mFocusPoint) - mFrameMetrics.mCompositionBounds.TopLeft();
     CSSPoint cssFocusPoint = focusPoint / userZoom;
 
     CSSPoint focusChange = (mLastZoomFocus - focusPoint) / userZoom;
     // 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);
     }
@@ -750,18 +751,18 @@ 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.
     CSSPoint neededDisplacement;
 
-    CSSToScreenScale realMinZoom = mZoomConstraints.mMinZoom;
-    CSSToScreenScale realMaxZoom = mZoomConstraints.mMaxZoom;
+    CSSToParentLayerScale realMinZoom = mZoomConstraints.mMinZoom * mFrameMetrics.mTransformScale;
+    CSSToParentLayerScale realMaxZoom = mZoomConstraints.mMaxZoom * mFrameMetrics.mTransformScale;
     realMinZoom.scale = std::max(realMinZoom.scale,
                                  mFrameMetrics.mCompositionBounds.width / mFrameMetrics.mScrollableRect.width);
     realMinZoom.scale = std::max(realMinZoom.scale,
                                  mFrameMetrics.mCompositionBounds.height / mFrameMetrics.mScrollableRect.height);
     if (realMaxZoom < realMinZoom) {
       realMaxZoom = realMinZoom;
     }
 
@@ -1202,18 +1203,17 @@ EnlargeDisplayPortAlongAxis(float* aOutO
 /* static */
 const CSSRect AsyncPanZoomController::CalculatePendingDisplayPort(
   const FrameMetrics& aFrameMetrics,
   const ScreenPoint& aVelocity,
   double aEstimatedPaintDuration)
 {
   // convert to milliseconds
   double estimatedPaintDurationMillis = aEstimatedPaintDuration * 1000;
-
-  CSSRect compositionBounds = aFrameMetrics.CalculateCompositedRectInCssPixels();
+  CSSRect compositionBounds(aFrameMetrics.CalculateCompositedRectInCssPixels());
   CSSPoint scrollOffset = aFrameMetrics.mScrollOffset;
   CSSRect displayPort(scrollOffset, compositionBounds.Size());
   CSSPoint velocity = aVelocity / aFrameMetrics.mZoom;
 
   CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
 
   float xSkateSizeMultiplier = gXSkateSizeMultiplier,
         ySkateSizeMultiplier = gYSkateSizeMultiplier;
@@ -1397,17 +1397,17 @@ bool AsyncPanZoomController::SampleConte
 
     requestAnimationFrame = UpdateAnimation(aSampleTime);
 
     aScrollOffset = mFrameMetrics.mScrollOffset * mFrameMetrics.mZoom;
     *aNewTransform = GetCurrentAsyncTransform();
 
     LogRendertraceRect("viewport", "red",
       CSSRect(mFrameMetrics.mScrollOffset,
-              ScreenSize(mFrameMetrics.mCompositionBounds.Size()) / mFrameMetrics.mZoom));
+              ParentLayerSize(mFrameMetrics.mCompositionBounds.Size()) / mFrameMetrics.GetZoomToParent()));
 
     mCurrentAsyncScrollOffset = mFrameMetrics.mScrollOffset;
   }
 
   // Cancel the mAsyncScrollTimeoutTask because we will fire a
   // mozbrowserasyncscroll event or renew the mAsyncScrollTimeoutTask again.
   if (mAsyncScrollTimeoutTask) {
     mAsyncScrollTimeoutTask->Cancel();
@@ -1446,17 +1446,17 @@ ViewTransform AsyncPanZoomController::Ge
   }
 
   CSSPoint currentScrollOffset = mFrameMetrics.mScrollOffset;
 
   // If checkerboarding has been disallowed, clamp the scroll position to stay
   // within rendered content.
   if (!gAllowCheckerboarding &&
       !mLastContentPaintMetrics.mDisplayPort.IsEmpty()) {
-    CSSRect compositedRect = mLastContentPaintMetrics.CalculateCompositedRectInCssPixels();
+    CSSRect compositedRect(mLastContentPaintMetrics.CalculateCompositedRectInCssPixels());
     CSSPoint maxScrollOffset = lastPaintScrollOffset +
       CSSPoint(mLastContentPaintMetrics.mDisplayPort.XMost() - compositedRect.width,
                mLastContentPaintMetrics.mDisplayPort.YMost() - compositedRect.height);
     CSSPoint minScrollOffset = lastPaintScrollOffset + mLastContentPaintMetrics.mDisplayPort.TopLeft();
 
     if (minScrollOffset.x < maxScrollOffset.x) {
       currentScrollOffset.x = clamped(currentScrollOffset.x, minScrollOffset.x, maxScrollOffset.x);
     }
@@ -1489,16 +1489,17 @@ gfx3DMatrix AsyncPanZoomController::GetT
   return gfx3DMatrix::Translation(scrollChange.x, scrollChange.y, 0) *
          gfx3DMatrix::ScalingMatrix(zoomChange, zoomChange, 1);
 }
 
 void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
   mLastContentPaintMetrics = aLayerMetrics;
+  UpdateTransformScale();
 
   bool isDefault = mFrameMetrics.IsDefault();
   mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
   APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
 
   LogRendertraceRect("page", "brown", aLayerMetrics.mScrollableRect);
   LogRendertraceRect("painted displayport", "green",
     aLayerMetrics.mDisplayPort + aLayerMetrics.mScrollOffset);
@@ -1578,66 +1579,66 @@ const FrameMetrics& AsyncPanZoomControll
 }
 
 void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
   SetState(ANIMATING_ZOOM);
 
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
-    ScreenIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
+    ParentLayerIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
     CSSRect cssPageRect = mFrameMetrics.mScrollableRect;
     CSSPoint scrollOffset = mFrameMetrics.mScrollOffset;
-    CSSToScreenScale currentZoom = mFrameMetrics.mZoom;
-    CSSToScreenScale targetZoom;
+    CSSToParentLayerScale currentZoom = mFrameMetrics.GetZoomToParent();
+    CSSToParentLayerScale targetZoom;
 
     // 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.
-    CSSToScreenScale localMinZoom(std::max(mZoomConstraints.mMinZoom.scale,
-                                  std::max(compositionBounds.width / cssPageRect.width,
-                                           compositionBounds.height / cssPageRect.height)));
-    CSSToScreenScale localMaxZoom = mZoomConstraints.mMaxZoom;
+    CSSToParentLayerScale localMinZoom(std::max((mZoomConstraints.mMinZoom * mFrameMetrics.mTransformScale).scale,
+                                       std::max(compositionBounds.width / cssPageRect.width,
+                                                compositionBounds.height / cssPageRect.height)));
+    CSSToParentLayerScale localMaxZoom = mZoomConstraints.mMaxZoom * mFrameMetrics.mTransformScale;
 
     if (!aRect.IsEmpty()) {
       // Intersect the zoom-to-rect to the CSS rect to make sure it fits.
       aRect = aRect.Intersect(cssPageRect);
-      targetZoom = CSSToScreenScale(std::min(compositionBounds.width / aRect.width,
-                                             compositionBounds.height / aRect.height));
+      targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
+                                                  compositionBounds.height / aRect.height));
     }
     // 1. If the rect is empty, request received from browserElementScrolling.js
     // 2. currentZoom is equal to mZoomConstraints.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 (aRect.IsEmpty() ||
         (currentZoom == localMaxZoom && targetZoom >= localMaxZoom) ||
         (currentZoom == localMinZoom && targetZoom <= localMinZoom)) {
-      CSSRect compositedRect = mFrameMetrics.CalculateCompositedRectInCssPixels();
+      CSSRect compositedRect = CSSRect(mFrameMetrics.CalculateCompositedRectInCssPixels());
       float y = scrollOffset.y;
       float newHeight =
         cssPageRect.width * (compositedRect.height / compositedRect.width);
       float dh = compositedRect.height - newHeight;
 
       aRect = CSSRect(0.0f,
-                           y + dh/2,
-                           cssPageRect.width,
-                           newHeight);
+                      y + dh/2,
+                      cssPageRect.width,
+                      newHeight);
       aRect = aRect.Intersect(cssPageRect);
-      targetZoom = CSSToScreenScale(std::min(compositionBounds.width / aRect.width,
-                                             compositionBounds.height / aRect.height));
+      targetZoom = CSSToParentLayerScale(std::min(compositionBounds.width / aRect.width,
+                                                  compositionBounds.height / aRect.height));
     }
 
     targetZoom.scale = clamped(targetZoom.scale, localMinZoom.scale, localMaxZoom.scale);
     FrameMetrics endZoomToMetrics = mFrameMetrics;
-    endZoomToMetrics.mZoom = targetZoom;
+    endZoomToMetrics.mZoom = targetZoom / mFrameMetrics.mTransformScale;
 
     // Adjust the zoomToRect to a sensible position to prevent overscrolling.
-    CSSRect rectAfterZoom = endZoomToMetrics.CalculateCompositedRectInCssPixels();
+    CSSRect rectAfterZoom = CSSRect(endZoomToMetrics.CalculateCompositedRectInCssPixels());
 
     // If either of these conditions are met, the page will be
     // overscrolled after zoomed
     if (aRect.y + rectAfterZoom.height > cssPageRect.height) {
       aRect.y = cssPageRect.height - rectAfterZoom.height;
       aRect.y = aRect.y > 0 ? aRect.y : 0;
     }
     if (aRect.x + rectAfterZoom.width > cssPageRect.width) {
@@ -1774,17 +1775,17 @@ void AsyncPanZoomController::SendAsyncSc
   bool isRoot;
   CSSRect contentRect;
   CSSSize scrollableSize;
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
     isRoot = mFrameMetrics.mIsRoot;
     scrollableSize = mFrameMetrics.mScrollableRect.Size();
-    contentRect = mFrameMetrics.CalculateCompositedRectInCssPixels();
+    contentRect = CSSRect(mFrameMetrics.CalculateCompositedRectInCssPixels());
     contentRect.MoveTo(mCurrentAsyncScrollOffset);
   }
 
   controller->SendAsyncScrollDOMEvent(isRoot, contentRect, scrollableSize);
 }
 
 bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid)
 {
@@ -1796,10 +1797,24 @@ void AsyncPanZoomController::GetGuid(Scr
   if (!aGuidOut) {
     return;
   }
   aGuidOut->mLayersId = mLayersId;
   aGuidOut->mScrollId = mFrameMetrics.mScrollId;
   aGuidOut->mPresShellId = mFrameMetrics.mPresShellId;
 }
 
+ParentLayerPoint AsyncPanZoomController::ToParentLayerCoords(const ScreenPoint& aPoint)
+{
+  return TransformTo<ParentLayerPixel>(GetNontransientAsyncTransform() * GetCSSTransform(), aPoint);
+}
+
+void AsyncPanZoomController::UpdateTransformScale()
+{
+  gfx3DMatrix nontransientTransforms = GetNontransientAsyncTransform() * GetCSSTransform();
+  if (fabsf(nontransientTransforms.GetXScale() - nontransientTransforms.GetYScale()) > EPSILON) {
+    NS_WARNING("The x- and y-scales of the nontransient transforms should be equal");
+  }
+  mFrameMetrics.mTransformScale.scale = nontransientTransforms.GetXScale();
+}
+
 }
 }
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -529,16 +529,26 @@ private:
   enum AxisLockMode {
     FREE,     /* No locking at all */
     STANDARD, /* Default axis locking mode that remains locked until pan ends*/
     STICKY,   /* Allow lock to be broken, with hysteresis */
   };
 
   static AxisLockMode GetAxisLockMode();
 
+  // Convert a point from local screen coordinates to parent layer coordinates.
+  // This is a common operation as inputs from the tree manager are in screen
+  // coordinates but the composition bounds is in parent layer coordinates.
+  ParentLayerPoint ToParentLayerCoords(const ScreenPoint& aPoint);
+
+  // Update mFrameMetrics.mTransformScale. This should be called whenever
+  // our CSS transform or the non-transient part of our async transform
+  // changes, as it corresponds to the scale portion of those transforms.
+  void UpdateTransformScale();
+
   uint64_t mLayersId;
   nsRefPtr<CompositorParent> mCompositorParent;
   TaskThrottler mPaintThrottler;
 
   /* Access to the following two fields is protected by the mRefPtrMonitor,
      since they are accessed on the UI thread but can be cleared on the
      compositor thread. */
   nsRefPtr<GeckoContentController> mGeckoContentController;
@@ -593,17 +603,17 @@ private:
   // The last time the compositor has sampled the content transform for this
   // frame.
   TimeStamp mLastSampleTime;
   // The last time a touch event came through on the UI thread.
   uint32_t mLastEventTime;
 
   // Stores the previous focus point if there is a pinch gesture happening. Used
   // to allow panning by moving multiple fingers (thus moving the focus point).
-  ScreenPoint mLastZoomFocus;
+  ParentLayerPoint mLastZoomFocus;
 
   // Stores the state of panning and zooming this frame. This is protected by
   // |mMonitor|; that is, it should be held whenever this is updated.
   PanZoomState mState;
 
   // The last time and offset we fire the mozbrowserasyncscroll event when
   // compositor has sampled the content transform for this frame.
   TimeStamp mLastAsyncScrollTime;
@@ -674,40 +684,41 @@ private:
   nsRefPtr<AsyncPanZoomController> mPrevSibling;
   nsRefPtr<AsyncPanZoomController> mParent;
 
   /* The functions and members in this section are used to maintain the
    * area that this APZC instance is responsible for. This is used when
    * hit-testing to see which APZC instance should handle touch events.
    */
 public:
-  void SetLayerHitTestData(const ScreenRect& aRect, const gfx3DMatrix& aTransformToLayer,
+  void SetLayerHitTestData(const ParentLayerRect& aRect, const gfx3DMatrix& aTransformToLayer,
                            const gfx3DMatrix& aTransformForLayer) {
     mVisibleRect = aRect;
     mAncestorTransform = aTransformToLayer;
     mCSSTransform = aTransformForLayer;
+    UpdateTransformScale();
   }
 
   gfx3DMatrix GetAncestorTransform() const {
     return mAncestorTransform;
   }
 
   gfx3DMatrix GetCSSTransform() const {
     return mCSSTransform;
   }
 
-  bool VisibleRegionContains(const ScreenPoint& aPoint) const {
+  bool VisibleRegionContains(const ParentLayerPoint& aPoint) const {
     return mVisibleRect.Contains(aPoint);
   }
 
 private:
   /* This is the visible region of the layer that this APZC corresponds to, in
    * that layer's screen pixels (the same coordinate system in which this APZC
    * receives events in ReceiveInputEvent()). */
-  ScreenRect mVisibleRect;
+  ParentLayerRect mVisibleRect;
   /* This is the cumulative CSS transform for all the layers between the parent
    * APZC and this one (not inclusive) */
   gfx3DMatrix mAncestorTransform;
   /* This is the CSS transform for this APZC's layer. */
   gfx3DMatrix mCSSTransform;
 };
 
 class AsyncPanZoomAnimation {
--- a/gfx/layers/ipc/Axis.cpp
+++ b/gfx/layers/ipc/Axis.cpp
@@ -268,34 +268,34 @@ float Axis::GetPageEnd() {
 
 float Axis::GetOrigin() {
   CSSPoint origin = mAsyncPanZoomController->GetFrameMetrics().mScrollOffset;
   return GetPointOffset(origin);
 }
 
 float Axis::GetCompositionLength() {
   const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
-  CSSRect cssCompositedRect = metrics.CalculateCompositedRectInCssPixels();
+  CSSRect cssCompositedRect = CSSRect(metrics.CalculateCompositedRectInCssPixels());
   return GetRectLength(cssCompositedRect);
 }
 
 float Axis::GetPageStart() {
   CSSRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect;
   return GetRectOffset(pageRect);
 }
 
 float Axis::GetPageLength() {
   CSSRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect;
   return GetRectLength(pageRect);
 }
 
 bool Axis::ScaleWillOverscrollBothSides(float aScale) {
   const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
 
-  CSSToScreenScale scale(metrics.mZoom.scale * aScale);
+  CSSToParentLayerScale scale(metrics.GetZoomToParent().scale * aScale);
   CSSRect cssCompositionBounds = metrics.mCompositionBounds / scale;
 
   return GetRectLength(metrics.mScrollableRect) < GetRectLength(cssCompositionBounds);
 }
 
 AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController)
   : Axis(aAsyncPanZoomController)
 {
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -118,17 +118,17 @@ public:
   }
 };
 
 static
 FrameMetrics TestFrameMetrics() {
   FrameMetrics fm;
 
   fm.mDisplayPort = CSSRect(0, 0, 10, 10);
-  fm.mCompositionBounds = ScreenIntRect(0, 0, 10, 10);
+  fm.mCompositionBounds = ParentLayerIntRect(0, 0, 10, 10);
   fm.mCriticalDisplayPort = CSSRect(0, 0, 10, 10);
   fm.mScrollableRect = CSSRect(0, 0, 100, 100);
   fm.mViewport = CSSRect(0, 0, 10, 10);
 
   return fm;
 }
 
 static
@@ -231,17 +231,17 @@ TEST(AsyncPanZoomController, Constructor
 }
 
 TEST(AsyncPanZoomController, Pinch) {
   nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
 
   FrameMetrics fm;
   fm.mViewport = CSSRect(0, 0, 980, 480);
-  fm.mCompositionBounds = ScreenIntRect(200, 200, 100, 200);
+  fm.mCompositionBounds = ParentLayerIntRect(200, 200, 100, 200);
   fm.mScrollableRect = CSSRect(0, 0, 980, 1000);
   fm.mScrollOffset = CSSPoint(300, 300);
   fm.mZoom = CSSToScreenScale(2.0);
   apzc->SetFrameMetrics(fm);
   apzc->UpdateZoomConstraints(ZoomConstraints(true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
   // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
 
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
@@ -274,17 +274,17 @@ TEST(AsyncPanZoomController, Pinch) {
 }
 
 TEST(AsyncPanZoomController, Overzoom) {
   nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
   nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
 
   FrameMetrics fm;
   fm.mViewport = CSSRect(0, 0, 100, 100);
-  fm.mCompositionBounds = ScreenIntRect(0, 0, 100, 100);
+  fm.mCompositionBounds = ParentLayerIntRect(0, 0, 100, 100);
   fm.mScrollableRect = CSSRect(0, 0, 125, 150);
   fm.mScrollOffset = CSSPoint(10, 0);
   fm.mZoom = CSSToScreenScale(1.0);
   apzc->SetFrameMetrics(fm);
   apzc->UpdateZoomConstraints(ZoomConstraints(true, CSSToScreenScale(0.25), CSSToScreenScale(4.0)));
   // the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
 
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
@@ -352,17 +352,17 @@ TEST(AsyncPanZoomController, ComplexTran
   transforms[0].ScalePost(0.5f, 0.5f, 1.0f); // this results from the 2.0 resolution on the root layer
   transforms[1].ScalePost(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer
 
   nsTArray<nsRefPtr<Layer> > layers;
   nsRefPtr<LayerManager> lm;
   nsRefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
 
   FrameMetrics metrics;
-  metrics.mCompositionBounds = ScreenIntRect(0, 0, 24, 24);
+  metrics.mCompositionBounds = ParentLayerIntRect(0, 0, 24, 24);
   metrics.mDisplayPort = CSSRect(-1, -1, 6, 6);
   metrics.mViewport = CSSRect(0, 0, 4, 4);
   metrics.mScrollOffset = CSSPoint(10, 10);
   metrics.mScrollableRect = CSSRect(0, 0, 50, 50);
   metrics.mCumulativeResolution = LayoutDeviceToLayerScale(2);
   metrics.mResolution = ParentLayerToLayerScale(2);
   metrics.mZoom = CSSToScreenScale(6);
   metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(3);
@@ -650,18 +650,18 @@ SetScrollableFrameMetrics(Layer* aLayer,
                           // The scrollable rect is only used in HitTesting2,
                           // HitTesting1 doesn't care about it.
                           CSSRect aScrollableRect = CSSRect(-1, -1, -1, -1))
 {
   ContainerLayer* container = aLayer->AsContainerLayer();
   FrameMetrics metrics;
   metrics.mScrollId = aScrollId;
   nsIntRect layerBound = aLayer->GetVisibleRegion().GetBounds();
-  metrics.mCompositionBounds = ScreenIntRect(layerBound.x, layerBound.y,
-                                             layerBound.width, layerBound.height);
+  metrics.mCompositionBounds = ParentLayerIntRect(layerBound.x, layerBound.y,
+                                                  layerBound.width, layerBound.height);
   metrics.mScrollableRect = aScrollableRect;
   metrics.mScrollOffset = CSSPoint(0, 0);
   container->SetFrameMetrics(metrics);
 }
 
 static already_AddRefed<AsyncPanZoomController>
 GetTargetAPZC(APZCTreeManager* manager, const ScreenPoint& aPoint,
               gfx3DMatrix& aTransformToApzcOut, gfx3DMatrix& aTransformToGeckoOut)
new file mode 100644
--- /dev/null
+++ b/layout/base/UnitTransforms.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef MOZ_UNIT_TRANSFORMS_H_
+#define MOZ_UNIT_TRANSFORMS_H_
+
+#include "Units.h"
+
+namespace mozilla {
+
+// Convenience functions for converting an entity from one strongly-typed
+// coordinate system to another without changing the values it stores (this
+// can be thought of as a cast).
+// To use these functions, you must provide a justification for each use!
+// Feel free to add more justifications to PixelCastJustification, along with
+// a comment that explains under what circumstances it is appropriate to use.
+
+MOZ_BEGIN_ENUM_CLASS(PixelCastJustification, uint8_t)
+  // For the root layer, Screen Pixel = Parent Layer Pixel.
+  ScreenToParentLayerForRoot
+MOZ_END_ENUM_CLASS(PixelCastJustification)
+
+template <class TargetUnits, class SourceUnits>
+gfx::SizeTyped<TargetUnits> ViewAs(const gfx::SizeTyped<SourceUnits>& aSize, PixelCastJustification) {
+  return gfx::SizeTyped<TargetUnits>(aSize.width, aSize.height);
+}
+template <class TargetUnits, class SourceUnits>
+gfx::IntSizeTyped<TargetUnits> ViewAs(const gfx::IntSizeTyped<SourceUnits>& aSize, PixelCastJustification) {
+  return gfx::IntSizeTyped<TargetUnits>(aSize.width, aSize.height);
+}
+
+// Convenience functions for casting untyped entities to typed entities.
+// Using these functions does not require a justification, but once we convert
+// all code to use strongly typed units they should not be needed any longer.
+template <class TargetUnits>
+gfx::PointTyped<TargetUnits> ViewAs(const gfxPoint& aPoint) {
+  return gfx::PointTyped<TargetUnits>(aPoint.x, aPoint.y);
+}
+template <class TargetUnits>
+gfx::RectTyped<TargetUnits> ViewAs(const gfxRect& aRect) {
+  return gfx::RectTyped<TargetUnits>(aRect.x, aRect.y, aRect.width, aRect.height);
+}
+
+// Convenience functions for casting typed entities to untyped entities.
+// Using these functions does not require a justification, but once we convert
+// all code to use strongly typed units they should not be needed any longer.
+template <class SourceUnits>
+gfxPoint ViewAsUntyped(const gfx::PointTyped<SourceUnits>& aPoint) {
+  return gfxPoint(aPoint.x, aPoint.y);
+}
+template <class SourceUnits>
+gfxRect ViewAsUntyped(const gfx::RectTyped<SourceUnits>& aRect) {
+  return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
+}
+
+// Convenience functions for transforming an entity from one strongly-typed
+// coordinate system to another using the provided transformation matrix.
+template <typename TargetUnits, typename SourceUnits>
+static gfx::PointTyped<TargetUnits> TransformTo(const gfx3DMatrix& aTransform,
+                                                const gfx::PointTyped<SourceUnits>& aPoint)
+{
+  return ViewAs<TargetUnits>(aTransform.Transform(ViewAsUntyped(aPoint)));
+}
+template <typename TargetUnits, typename SourceUnits>
+static gfx::RectTyped<TargetUnits> TransformTo(const gfx3DMatrix& aTransform,
+                                               const gfx::RectTyped<SourceUnits>& aRect)
+{
+  return ViewAs<TargetUnits>(aTransform.TransformBounds(ViewAsUntyped(aRect)));
+}
+
+
+}
+
+#endif
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -272,11 +272,11 @@ gfx::MarginTyped<dst> operator*(const gf
 template<class src, class dst>
 gfx::MarginTyped<dst> operator/(const gfx::MarginTyped<src>& aMargin, const gfx::ScaleFactor<dst, src>& aScale) {
   return gfx::MarginTyped<dst>(aMargin.top / aScale.scale,
                                aMargin.right / aScale.scale,
                                aMargin.bottom / aScale.scale,
                                aMargin.left / aScale.scale);
 }
 
-};
+}
 
 #endif
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -51,16 +51,17 @@ EXPORTS += [
     'nsPresArena.h',
     'nsPresContext.h',
     'nsPresState.h',
     'nsRefreshDriver.h',
     'nsStyleChangeList.h',
     'ScrollbarStyles.h',
     'StackArena.h',
     'Units.h',
+    'UnitTransforms.h',
 ]
 
 EXPORTS.mozilla += [
     'PaintTracker.h',
 ]
 
 UNIFIED_SOURCES += [
     'ActiveLayerTracker.cpp',
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -615,25 +615,16 @@ static void UnmarkFrameForDisplay(nsIFra
   for (nsIFrame* f = aFrame; f;
        f = nsLayoutUtils::GetParentOrPlaceholderFor(f)) {
     if (!(f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO))
       return;
     f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
   }
 }
 
-static void AdjustForScrollBars(ScreenIntRect& aToAdjust, nsIScrollableFrame* aScrollableFrame) {
-  if (aScrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) {
-    nsMargin sizes = aScrollableFrame->GetActualScrollbarSizes();
-    // Scrollbars are not subject to scaling, so CSS pixels = screen pixels for them.
-    ScreenIntMargin boundMargins = RoundedToInt(CSSMargin::FromAppUnits(sizes) * CSSToScreenScale(1.0f));
-    aToAdjust.Deflate(boundMargins);
-  }
-}
-
 static void RecordFrameMetrics(nsIFrame* aForFrame,
                                nsIFrame* aScrollFrame,
                                const nsIFrame* aReferenceFrame,
                                ContainerLayer* aRoot,
                                const nsRect& aVisibleRect,
                                const nsRect& aViewport,
                                nsRect* aDisplayPort,
                                nsRect* aCriticalDisplayPort,
@@ -738,43 +729,51 @@ static void RecordFrameMetrics(nsIFrame*
   // Calculate the composition bounds as the size of the scroll frame and
   // its origin relative to the reference frame.
   // If aScrollFrame is null, we are in a document without a root scroll frame,
   // so it's a xul document. In this case, use the size of the viewport frame.
   nsIFrame* frameForCompositionBoundsCalculation = aScrollFrame ? aScrollFrame : aForFrame;
   nsRect compositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame),
                            frameForCompositionBoundsCalculation->GetSize());
   metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(compositionBounds, auPerDevPixel)
-                             * metrics.mCumulativeResolution
-                             * layerToScreenScale);
-
-  // For the root scroll frame of the root content document, clamp the
-  // composition bounds to the widget bounds. This is necessary because, if
-  // the page is zoomed in, the frame's size might be larger than the widget
-  // bounds, but we don't want the composition bounds to be.
-  bool useWidgetBounds = false;
+                             * metrics.GetParentResolution());
+
+  // For the root scroll frame of the root content document, the above calculation
+  // will yield the size of the viewport frame as the composition bounds, which
+  // doesn't actually correspond to what is visible when
+  // nsIDOMWindowUtils::setCSSViewport has been called to modify the visible area of
+  // the prescontext that the viewport frame is reflowed into. In that case if our
+  // document has a widget then the widget's bounds will correspond to what is
+  // visible. If we don't have a widget the root view's bounds correspond to what
+  // would be visible because they don't get modified by setCSSViewport.
   bool isRootContentDocRootScrollFrame = presContext->IsRootContentDocument()
                                       && aScrollFrame == presShell->GetRootScrollFrame();
   if (isRootContentDocRootScrollFrame) {
-    if (nsIWidget* widget = aForFrame->GetNearestWidget()) {
-      nsIntRect bounds;
-      widget->GetBounds(bounds);
-      ScreenIntRect screenBounds = ScreenIntRect::FromUnknownRect(mozilla::gfx::IntRect(
-          bounds.x, bounds.y, bounds.width, bounds.height));
-      AdjustForScrollBars(screenBounds, scrollableFrame);
-      metrics.mCompositionBounds = metrics.mCompositionBounds.ForceInside(screenBounds);
-      useWidgetBounds = true;
+    if (nsIFrame* rootFrame = presShell->GetRootFrame()) {
+      if (nsView* view = rootFrame->GetView()) {
+        nsIWidget* widget = view->GetWidget();
+        if (widget) {
+          nsIntRect bounds;
+          widget->GetBounds(bounds);
+          metrics.mCompositionBounds = ParentLayerIntRect::FromUnknownRect(mozilla::gfx::IntRect(
+              bounds.x, bounds.y, bounds.width, bounds.height));
+        } else {
+          metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(view->GetBounds(), auPerDevPixel)
+                                     * metrics.GetParentResolution());
+        }
+      }
     }
   }
 
   // Adjust composition bounds for the size of scroll bars.
-  // If the widget bounds were used to clamp the composition bounds,
-  // this adjustment was already made to the widget bounds.
-  if (!useWidgetBounds) {
-    AdjustForScrollBars(metrics.mCompositionBounds, scrollableFrame);
+  if (scrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) {
+    nsMargin sizes = scrollableFrame->GetActualScrollbarSizes();
+    // Scrollbars are not subject to scaling, so CSS pixels = layer pixels for them.
+    ParentLayerIntMargin boundMargins = RoundedToInt(CSSMargin::FromAppUnits(sizes) * CSSToParentLayerScale(1.0f));
+    metrics.mCompositionBounds.Deflate(boundMargins);
   }
 
   metrics.mPresShellId = presShell->GetPresShellId();
 
   // If the scroll frame's content is marked 'scrollgrab', record this
   // in the FrameMetrics so APZ knows to provide the scroll grabbing
   // behaviour.
   if (aScrollFrame && nsContentUtils::HasScrollgrab(aScrollFrame->GetContent())) {
--- a/security/manager/boot/src/nsSTSPreloadList.errors
+++ b/security/manager/boot/src/nsSTSPreloadList.errors
@@ -28,17 +28,16 @@ crypto.is: did not receive HSTS header
 csawctf.poly.edu: did not receive HSTS header
 dl.google.com: did not receive HSTS header
 docs.google.com: did not receive HSTS header
 drive.google.com: did not receive HSTS header
 dropcam.com: did not receive HSTS header
 email.lookout.com: could not connect to host
 emailprivacytester.com: did not receive HSTS header
 encrypted.google.com: did not receive HSTS header
-errors.zenpayroll.com: could not connect to host
 espra.com: could not connect to host
 fatzebra.com.au: did not receive HSTS header
 fj.simple.com: did not receive HSTS header
 get.zenpayroll.com: did not receive HSTS header
 glass.google.com: did not receive HSTS header
 gmail.com: did not receive HSTS header
 gocardless.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-b28_v1_3-l64-hsts-0000000000/getHSTSPreloadList.js :: processStsHeader :: line 124"  data: no]
 googlemail.com: did not receive HSTS header
@@ -73,18 +72,16 @@ passport.yandex.com: did not receive HST
 passport.yandex.com.tr: did not receive HSTS header
 passport.yandex.kz: did not receive HSTS header
 passport.yandex.ru: did not receive HSTS header
 passport.yandex.ua: did not receive HSTS header
 paypal.com: max-age too low: 14400
 payroll.xero.com: max-age too low: 3600
 platform.lookout.com: could not connect to host
 play.google.com: did not receive HSTS header
-plus.google.com: did not receive HSTS header
-plus.sandbox.google.com: did not receive HSTS header
 prodpad.com: did not receive HSTS header
 profiles.google.com: did not receive HSTS header
 rapidresearch.me: did not receive HSTS header
 sah3.net: could not connect to host
 saturngames.co.uk: did not receive HSTS header
 script.google.com: did not receive HSTS header
 security.google.com: did not receive HSTS header
 serverdensity.io: did not receive HSTS header
--- a/security/manager/boot/src/nsSTSPreloadList.inc
+++ b/security/manager/boot/src/nsSTSPreloadList.inc
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
 /* nsSiteSecurityService.cpp, you shouldn't be #including it.     */
 /*****************************************************************************/
 
 #include <stdint.h>
-const PRTime gPreloadListExpirationTime = INT64_C(1405163271028000);
+const PRTime gPreloadListExpirationTime = INT64_C(1405764347410000);
 
 class nsSTSPreload
 {
   public:
     const char *mHost;
     const bool mIncludeSubdomains;
 };
 
@@ -127,18 +127,21 @@ static const nsSTSPreload kSTSPreloadLis
   { "passwd.io", true },
   { "paste.linode.com", false },
   { "pastebin.linode.com", false },
   { "pay.gigahost.dk", true },
   { "paymill.com", true },
   { "paymill.de", false },
   { "piratenlogin.de", true },
   { "pixi.me", true },
+  { "plus.google.com", false },
+  { "plus.sandbox.google.com", false },
   { "publications.qld.gov.au", false },
   { "riseup.net", true },
+  { "roddis.net", false },
   { "romab.com", true },
   { "roundcube.mayfirst.org", false },
   { "sandbox.mydigipass.com", false },
   { "securityheaders.com", true },
   { "shodan.io", true },
   { "silentcircle.com", false },
   { "simbolo.co.uk", false },
   { "simple.com", false },
@@ -173,16 +176,17 @@ static const nsSTSPreload kSTSPreloadLis
   { "www.irccloud.com", false },
   { "www.linode.com", false },
   { "www.lookout.com", false },
   { "www.makeyourlaws.org", false },
   { "www.mydigipass.com", false },
   { "www.mylookout.com", false },
   { "www.noisebridge.net", false },
   { "www.opsmate.com", true },
+  { "www.roddis.net", false },
   { "www.simbolo.co.uk", false },
   { "www.simple.com", false },
   { "www.therapynotes.com", false },
   { "www.torproject.org", false },
   { "www.twitter.com", false },
   { "www.zenpayroll.com", false },
   { "zenpayroll.com", false },
 };
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -1918,17 +1918,17 @@ AndroidBridge::IsContentDocumentDisplaye
     JNIEnv* env = GetJNIEnv();
     if (!env || !mLayerClient)
         return false;
 
     return mLayerClient->IsContentDocumentDisplayed();
 }
 
 bool
-AndroidBridge::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const LayerRect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, ScreenRect& aCompositionBounds, CSSToScreenScale& aZoom)
+AndroidBridge::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const LayerRect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, ParentLayerRect& aCompositionBounds, CSSToParentLayerScale& aZoom)
 {
     GeckoLayerClient *client = mLayerClient;
     if (!client) {
         ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__);
         return false;
     }
 
     jobject progressiveUpdateDataJObj = client->ProgressiveUpdateCallback(aHasPendingNewThebesContent,
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -172,17 +172,17 @@ public:
     bool GetThreadNameJavaProfiling(uint32_t aThreadId, nsCString & aResult);
     bool GetFrameNameJavaProfiling(uint32_t aThreadId, uint32_t aSampleId, uint32_t aFrameId, nsCString & aResult);
 
     nsresult CaptureThumbnail(nsIDOMWindow *window, int32_t bufW, int32_t bufH, int32_t tabId, jobject buffer);
     void GetDisplayPort(bool aPageSizeUpdate, bool aIsBrowserContentDisplayed, int32_t tabId, nsIAndroidViewport* metrics, nsIAndroidDisplayport** displayPort);
     void ContentDocumentChanged();
     bool IsContentDocumentDisplayed();
 
-    bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const LayerRect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, ScreenRect& aCompositionBounds, CSSToScreenScale& aZoom);
+    bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const LayerRect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, ParentLayerRect& aCompositionBounds, CSSToParentLayerScale& aZoom);
 
     void SetLayerClient(JNIEnv* env, jobject jobj);
     GeckoLayerClient* GetLayerClient() { return mLayerClient; }
 
     bool GetHandlersForURL(const nsAString& aURL,
                            nsIMutableArray* handlersArray = nullptr,
                            nsIHandlerApp **aDefaultApp = nullptr,
                            const nsAString& aAction = EmptyString());
--- a/widget/xpwidgets/APZCCallbackHelper.cpp
+++ b/widget/xpwidgets/APZCCallbackHelper.cpp
@@ -83,17 +83,17 @@ MaybeAlignAndClampDisplayPort(mozilla::l
   CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
   displayPort = scrollableRect.Intersect(displayPort + aActualScrollOffset)
     - aActualScrollOffset;
 }
 
 static void
 RecenterDisplayPort(mozilla::layers::FrameMetrics& aFrameMetrics)
 {
-    CSSRect compositionBounds = aFrameMetrics.CalculateCompositedRectInCssPixels();
+    CSSRect compositionBounds(aFrameMetrics.CalculateCompositedRectInCssPixels());
     aFrameMetrics.mDisplayPort.x = (compositionBounds.width - aFrameMetrics.mDisplayPort.width) / 2;
     aFrameMetrics.mDisplayPort.y = (compositionBounds.height - aFrameMetrics.mDisplayPort.height) / 2;
 }
 
 static CSSPoint
 ScrollFrameTo(nsIScrollableFrame* aFrame, const CSSPoint& aPoint, bool& aSuccessOut)
 {
   aSuccessOut = false;
@@ -148,17 +148,17 @@ APZCCallbackHelper::UpdateRootFrame(nsID
 
     // Set the scroll port size, which determines the scroll range. For example if
     // a 500-pixel document is shown in a 100-pixel frame, the scroll port length would
     // be 100, and gecko would limit the maximum scroll offset to 400 (so as to prevent
     // overscroll). Note that if the content here was zoomed to 2x, the document would
     // be 1000 pixels long but the frame would still be 100 pixels, and so the maximum
     // scroll range would be 900. Therefore this calculation depends on the zoom applied
     // to the content relative to the container.
-    CSSSize scrollPort = aMetrics.CalculateCompositedRectInCssPixels().Size();
+    CSSSize scrollPort = CSSSize(aMetrics.CalculateCompositedRectInCssPixels().Size());
     aUtils->SetScrollPositionClampingScrollPortSize(scrollPort.width, scrollPort.height);
 
     // Scroll the window to the desired spot
     nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.mScrollId);
     bool scrollUpdated = false;
     CSSPoint actualScrollOffset = ScrollFrameTo(sf, aMetrics.mScrollOffset, scrollUpdated);
 
     if (scrollUpdated) {
@@ -180,17 +180,17 @@ APZCCallbackHelper::UpdateRootFrame(nsID
     // The mZoom variable on the frame metrics stores the CSS-to-screen scale for this
     // frame. This scale includes all of the (cumulative) resolutions set on the presShells
     // from the root down to this frame. However, when setting the resolution, we only
     // want the piece of the resolution that corresponds to this presShell, rather than
     // all of the cumulative stuff, so we need to divide out the parent resolutions.
     // Finally, we multiply by a ScreenToLayerScale of 1.0f because the goal here is to
     // take the async zoom calculated by the APZC and tell gecko about it (turning it into
     // a "sync" zoom) which will update the resolution at which the layer is painted.
-    mozilla::layers::ParentLayerToLayerScale presShellResolution =
+    ParentLayerToLayerScale presShellResolution =
         aMetrics.mZoom
         / aMetrics.mDevPixelsPerCSSPixel
         / aMetrics.GetParentResolution()
         * ScreenToLayerScale(1.0f);
     aUtils->SetResolution(presShellResolution.scale, presShellResolution.scale);
 
     // Finally, we set the displayport.
     nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aMetrics.mScrollId);