Use widgets for APZ checks, rather than gfxPrefs. (bug 1162064, r=kats,mstange)
authorDavid Anderson <danderson@mozilla.com>
Thu, 04 Jun 2015 16:51:10 -0400
changeset 277961 a509c974aa1f07e07b650c18c4dbc91a1e84336b
parent 277960 b485fe97c0486f188b47aae272e7e1019746009d
child 277962 227d356ac030dd6f310931e9e88f1cf872a70b09
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats, mstange
bugs1162064
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Use widgets for APZ checks, rather than gfxPrefs. (bug 1162064, r=kats,mstange)
docshell/base/nsDocShell.cpp
dom/base/nsDOMWindowUtils.cpp
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
gfx/layers/Layers.h
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/ClientLayerManager.h
gfx/layers/client/ClientTiledPaintedLayer.cpp
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
gfx/layers/ipc/CompositorParent.cpp
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/gfxPrefs.h
image/ImageFactory.cpp
layout/base/AccessibleCaretEventHub.cpp
layout/base/FrameLayerBuilder.cpp
layout/base/SelectionCarets.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsIPresShell.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
layout/generic/nsGfxScrollFrame.cpp
layout/ipc/RenderFrameParent.cpp
layout/ipc/RenderFrameParent.h
layout/tools/reftest/reftest.js
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13857,17 +13857,22 @@ nsDocShell::GetAppManifestURL(nsAString&
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut)
 {
-  *aOut = Preferences::GetBool("layers.async-pan-zoom.enabled", false);
+  if (nsIPresShell* presShell = GetPresShell()) {
+    *aOut = presShell->AsyncPanZoomEnabled();
+    return NS_OK;
+  }
+
+  *aOut = false;
   return NS_OK;
 }
 
 bool
 nsDocShell::HasUnloadedParent()
 {
   nsRefPtr<nsDocShell> parent = GetParentDocshell();
   while (parent) {
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -373,19 +373,22 @@ nsDOMWindowUtils::SetDisplayPortForEleme
                      nsPresContext::CSSPixelsToAppUnits(aYPx),
                      nsPresContext::CSSPixelsToAppUnits(aWidthPx),
                      nsPresContext::CSSPixelsToAppUnits(aHeightPx));
 
   content->SetProperty(nsGkAtoms::DisplayPort,
                        new DisplayPortPropertyData(displayport, aPriority),
                        nsINode::DeleteProperty<DisplayPortPropertyData>);
 
-  if (nsLayoutUtils::UsesAsyncScrolling() && gfxPrefs::LayoutUseContainersForRootFrames()) {
+  if (gfxPrefs::LayoutUseContainersForRootFrames()) {
     nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
-    if (rootScrollFrame && content == rootScrollFrame->GetContent()) {
+    if (rootScrollFrame &&
+        content == rootScrollFrame->GetContent() &&
+        nsLayoutUtils::UsesAsyncScrolling(rootScrollFrame))
+    {
       // We are setting a root displayport for a document.
       // The pres shell needs a special flag set.
       presShell->SetIgnoreViewportScrolling(true);
     }
   }
 
   nsIFrame* rootFrame = presShell->FrameManager()->GetRootFrame();
   if (rootFrame) {
@@ -856,17 +859,17 @@ nsDOMWindowUtils::SendWheelEvent(float a
 
   nsPresContext* presContext = GetPresContext();
   NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
 
   wheelEvent.refPoint = nsContentUtils::ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
 
   widget->DispatchAPZAwareEvent(&wheelEvent);
 
-  if (gfxPrefs::AsyncPanZoomEnabled()) {
+  if (widget->AsyncPanZoomEnabled()) {
     // Computing overflow deltas is not compatible with APZ, so if APZ is
     // enabled, we skip testing it.
     return NS_OK;
   }
 
   bool failedX = false;
   if ((aOptions & WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO) &&
       wheelEvent.overflowDeltaX != 0) {
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -288,17 +288,18 @@ TabChildBase::GetPageSize(nsCOMPtr<nsIDo
   }
   return CSSSize(std::max(htmlWidth, bodyWidth),
                  std::max(htmlHeight, bodyHeight));
 }
 
 bool
 TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
 {
-  if (!gfxPrefs::AsyncPanZoomEnabled()) {
+  nsIWidget* widget = WebWidget();
+  if (!widget || !widget->AsyncPanZoomEnabled()) {
     return false;
   }
 
   TABC_LOG("HandlePossibleViewportChange aOldScreenSize=%s mInnerSize=%s\n",
     Stringify(aOldScreenSize).c_str(), Stringify(mInnerSize).c_str());
 
   nsCOMPtr<nsIDocument> document(GetDocument());
   if (!document) {
@@ -894,16 +895,23 @@ TabChild::TabChild(nsIContentChild* aMan
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
   , mDPI(0)
   , mDefaultScale(0)
   , mIPCOpen(true)
   , mParentIsActive(false)
 {
+  // In the general case having the TabParent tell us if APZ is enabled or not
+  // doesn't really work because the TabParent itself may not have a reference
+  // to the owning widget during initialization. Instead we assume that this
+  // TabChild corresponds to a widget type that would have APZ enabled, and just
+  // check the other conditions necessary for enabling APZ.
+  mAsyncPanZoomEnabled = gfxPlatform::AsyncPanZoomEnabled();
+
   // preloaded TabChild should not be added to child map
   if (mUniqueId) {
     MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end());
     NestedTabChildMap()[mUniqueId] = this;
   }
 }
 
 NS_IMETHODIMP
@@ -939,17 +947,17 @@ TabChild::Observe(nsISupports *aSubject,
         CSSRect rect;
         sscanf(NS_ConvertUTF16toUTF8(aData).get(),
                "{\"x\":%f,\"y\":%f,\"w\":%f,\"h\":%f}",
                &rect.x, &rect.y, &rect.width, &rect.height);
         SendZoomToRect(presShellId, viewId, rect);
       }
     }
   } else if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) {
-    if (gfxPrefs::AsyncPanZoomEnabled()) {
+    if (AsyncPanZoomEnabled()) {
       nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject));
       nsCOMPtr<nsIDocument> doc(GetDocument());
 
       if (SameCOMIdentity(subject, doc)) {
         nsCOMPtr<nsIPresShell> shell(doc->GetShell());
         if (shell) {
           shell->SetIsFirstPaint(true);
         }
@@ -997,17 +1005,17 @@ TabChild::OnProgressChange(nsIWebProgres
 }
 
 NS_IMETHODIMP
 TabChild::OnLocationChange(nsIWebProgress* aWebProgress,
                            nsIRequest* aRequest,
                            nsIURI *aLocation,
                            uint32_t aFlags)
 {
-  if (!gfxPrefs::AsyncPanZoomEnabled()) {
+  if (!AsyncPanZoomEnabled()) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMWindow> window;
   aWebProgress->GetDOMWindow(getter_AddRefs(window));
   if (!window) {
     return NS_OK;
   }
@@ -2032,16 +2040,17 @@ TabChild::RecvShow(const ScreenIntSize& 
 
 #ifdef MOZ_WIDGET_GONK
     MaybeRequestPreinitCamera();
 #endif
 
     bool res = InitTabChildGlobal();
     ApplyShowInfo(aInfo);
     RecvParentActivated(aParentIsActive);
+
     return res;
 }
 
 bool
 TabChild::RecvUpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size,
                                const ScreenOrientation& orientation, const LayoutDeviceIntPoint& chromeDisp)
 {
     if (!mRemoteFrame) {
@@ -2217,27 +2226,27 @@ TabChild::RecvRealMouseButtonEvent(const
   return true;
 }
 
 bool
 TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
                               const ScrollableLayerGuid& aGuid,
                               const uint64_t& aInputBlockId)
 {
-  if (gfxPrefs::AsyncPanZoomEnabled()) {
+  if (AsyncPanZoomEnabled()) {
     nsCOMPtr<nsIDocument> document(GetDocument());
     APZCCallbackHelper::SendSetTargetAPZCNotification(WebWidget(), document, aEvent, aGuid,
         aInputBlockId);
   }
 
   WidgetWheelEvent event(aEvent);
   event.widget = mWidget;
   APZCCallbackHelper::DispatchWidgetEvent(event);
 
-  if (gfxPrefs::AsyncPanZoomEnabled()) {
+  if (AsyncPanZoomEnabled()) {
     mAPZEventState->ProcessWheelEvent(event, aGuid, aInputBlockId);
   }
   return true;
 }
 
 bool
 TabChild::RecvMouseScrollTestEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
 {
@@ -2409,30 +2418,30 @@ TabChild::RecvRealTouchEvent(const Widge
   TABC_LOG("Receiving touch event of type %d\n", aEvent.message);
 
   WidgetTouchEvent localEvent(aEvent);
   localEvent.widget = mWidget;
 
   APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
       mWidget->GetDefaultScale(), GetPresShellResolution());
 
-  if (localEvent.message == NS_TOUCH_START && gfxPrefs::AsyncPanZoomEnabled()) {
+  if (localEvent.message == NS_TOUCH_START && AsyncPanZoomEnabled()) {
     if (gfxPrefs::TouchActionEnabled()) {
       APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(WebWidget(),
           localEvent, aInputBlockId, mSetAllowedTouchBehaviorCallback);
     }
     nsCOMPtr<nsIDocument> document = GetDocument();
     APZCCallbackHelper::SendSetTargetAPZCNotification(WebWidget(), document,
         localEvent, aGuid, aInputBlockId);
   }
 
   // Dispatch event to content (potentially a long-running operation)
   nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
 
-  if (!gfxPrefs::AsyncPanZoomEnabled()) {
+  if (!AsyncPanZoomEnabled()) {
     UpdateTapState(localEvent, status);
     return true;
   }
 
   mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId, aApzResponse);
   return true;
 }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -503,16 +503,17 @@ public:
     LayoutDeviceIntPoint GetChromeDisplacement() { return mChromeDisp; };
 
     bool IPCOpen() { return mIPCOpen; }
 
     bool ParentIsActive()
     {
       return mParentIsActive;
     }
+    bool AsyncPanZoomEnabled() { return mAsyncPanZoomEnabled; }
 
 protected:
     virtual ~TabChild();
 
     virtual PRenderFrameChild* AllocPRenderFrameChild() override;
     virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
     virtual bool RecvDestroy() override;
     virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override;
@@ -639,16 +640,17 @@ private:
     bool mDestroyed;
     // Position of tab, relative to parent widget (typically the window)
     LayoutDeviceIntPoint mChromeDisp;
     TabId mUniqueId;
     float mDPI;
     double mDefaultScale;
     bool mIPCOpen;
     bool mParentIsActive;
+    bool mAsyncPanZoomEnabled;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 }
 }
 
 #endif // mozilla_dom_TabChild_h
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2808,17 +2808,17 @@ TabParent::GetWidget() const
   return widget.forget();
 }
 
 void
 TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
                                        uint64_t* aOutInputBlockId,
                                        nsEventStatus* aOutApzResponse)
 {
-  if (gfxPrefs::AsyncPanZoomEnabled()) {
+  if (AsyncPanZoomEnabled()) {
     if (aOutTargetGuid) {
       *aOutTargetGuid = InputAPZContext::GetTargetLayerGuid();
 
       // There may be cases where the APZ hit-testing code came to a different
       // conclusion than the main-thread hit-testing code as to where the event
       // is destined. In such cases the layersId of the APZ result may not match
       // the layersId of this renderframe. In such cases the main-thread hit-
       // testing code "wins" so we need to update the guid to reflect this.
@@ -2988,17 +2988,17 @@ TabParent::InjectTouchEvent(const nsAStr
 
   SendRealTouchEvent(event);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom)
 {
-  *useAsyncPanZoom = gfxPrefs::AsyncPanZoomEnabled();
+  *useAsyncPanZoom = AsyncPanZoomEnabled();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabParent::SetIsDocShellActive(bool isActive)
 {
   unused << SendSetIsDocShellActive(isActive);
   return NS_OK;
@@ -3395,16 +3395,23 @@ void
 TabParent::TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
                                  int32_t& aDragAreaX, int32_t& aDragAreaY)
 {
   aSurface = mDnDVisualization.forget();
   aDragAreaX = mDragAreaX;
   aDragAreaY = mDragAreaY;
 }
 
+bool
+TabParent::AsyncPanZoomEnabled() const
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  return widget && widget->AsyncPanZoomEnabled();
+}
+
 NS_IMETHODIMP
 FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
 {
   nsAuthInformationHolder* holder =
     static_cast<nsAuthInformationHolder*>(aAuthInfo);
 
   if (!net::gNeckoChild->SendOnAuthAvailable(mCallbackId,
                                              holder->User(),
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -509,16 +509,18 @@ private:
     layout::RenderFrameParent* GetRenderFrame();
     nsRefPtr<nsIContentParent> mManager;
     void TryCacheDPIAndScale();
 
     nsresult UpdatePosition();
 
     CSSPoint AdjustTapToChildWidget(const CSSPoint& aPoint);
 
+    bool AsyncPanZoomEnabled() const;
+
     // Update state prior to routing an APZ-aware event to the child process.
     // |aOutTargetGuid| will contain the identifier
     // of the APZC instance that handled the event. aOutTargetGuid may be null.
     // |aOutInputBlockId| will contain the identifier of the input block
     // that this event was added to, if there was one. aOutInputBlockId may be null.
     // |aOutApzResponse| will contain the response that the APZ gave when processing
     // the input block; this is used for generating appropriate pointercancel events.
     void ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -663,16 +663,20 @@ public:
   }
 
   virtual float RequestProperty(const nsAString& property) { return -1; }
 
   const TimeStamp& GetAnimationReadyTime() const {
     return mAnimationReadyTime;
   }
 
+  virtual bool AsyncPanZoomEnabled() const {
+    return false;
+  }
+
 protected:
   nsRefPtr<Layer> mRoot;
   gfx::UserData mUserData;
   bool mDestroyed;
   bool mSnapEffectiveTransforms;
 
   nsIntRegion mRegionToClear;
 
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -208,17 +208,17 @@ ClientLayerManager::BeginTransactionWith
   // composited (including resampling) asynchronously before we get
   // a chance to repaint, so we have to ensure that it's all valid
   // and not rotated.
   //
   // Desktop does not support async zoom yet, so we ignore this for those
   // platforms.
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
   if (mWidget && mWidget->GetOwningTabChild()) {
-    mCompositorMightResample = gfxPrefs::AsyncPanZoomEnabled();
+    mCompositorMightResample = AsyncPanZoomEnabled();
   }
 #endif
 
   // If we have a non-default target, we need to let our shadow manager draw
   // to it. This will happen at the end of the transaction.
   if (aTarget && XRE_GetProcessType() == GeckoProcessType_Default) {
     mShadowTarget = aTarget;
   } else {
@@ -777,16 +777,22 @@ ClientLayerManager::ProgressiveUpdateCal
   aMetrics.SetScrollOffset(scrollOffset / zoom);
   aMetrics.SetZoom(CSSToParentLayerScale2D(zoom));
   return ret;
 #else
   return false;
 #endif
 }
 
+bool
+ClientLayerManager::AsyncPanZoomEnabled() const
+{
+  return mWidget && mWidget->AsyncPanZoomEnabled();
+}
+
 ClientLayer::~ClientLayer()
 {
   if (HasShadow()) {
     PLayerChild::Send__delete__(GetShadow());
   }
   MOZ_COUNT_DTOR(ClientLayer);
 }
 
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -245,16 +245,19 @@ public:
   }
 
   // Get a copy of the compositor-side APZ test data for our layers ID.
   void GetCompositorSideAPZTestData(APZTestData* aData) const;
 
   void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) { mTransactionIdAllocator = aAllocator; }
 
   float RequestProperty(const nsAString& aProperty) override;
+
+  bool AsyncPanZoomEnabled() const override;
+
 protected:
   enum TransactionPhase {
     PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
   };
   TransactionPhase mPhase;
 
 private:
   // Listen memory-pressure event for ClientLayerManager
--- a/gfx/layers/client/ClientTiledPaintedLayer.cpp
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -252,17 +252,17 @@ ClientTiledPaintedLayer::UseProgressiveD
 
   if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) {
     // This layer is fixed-position and so even if it does have a scrolling
     // ancestor it will likely be entirely on-screen all the time, so we
     // should draw it all at once
     return false;
   }
 
-  if (gfxPrefs::AsyncPanZoomEnabled()) {
+  if (ClientManager()->AsyncPanZoomEnabled()) {
     LayerMetricsWrapper scrollAncestor;
     GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
     MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is non-empty
     const FrameMetrics& parentMetrics = scrollAncestor.Metrics();
     if (!IsScrollingOnCompositor(parentMetrics)) {
       return false;
     }
   }
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -1324,16 +1324,22 @@ LayerManagerComposite::NotifyShadowTreeT
 
 void
 LayerComposite::SetLayerManager(LayerManagerComposite* aManager)
 {
   mCompositeManager = aManager;
   mCompositor = aManager->GetCompositor();
 }
 
+bool
+LayerManagerComposite::AsyncPanZoomEnabled() const
+{
+  return mCompositor->GetWidget()->AsyncPanZoomEnabled();
+}
+
 nsIntRegion
 LayerComposite::GetFullyRenderedRegion() {
   if (TiledLayerComposer* tiled = GetTiledLayerComposer()) {
     nsIntRegion shadowVisibleRegion = GetShadowVisibleRegion();
     // Discard the region which hasn't been drawn yet when doing
     // progressive drawing. Note that if the shadow visible region
     // shrunk the tiled valig region may not have discarded this yet.
     shadowVisibleRegion.And(shadowVisibleRegion, tiled->GetValidRegion());
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -244,16 +244,18 @@ public:
   }
 
   void UnusedApzTransformWarning() {
     mUnusedApzTransformWarning = true;
   }
 
   bool LastFrameMissedHWC() { return mLastFrameMissedHWC; }
 
+  bool AsyncPanZoomEnabled() const override;
+
 private:
   /** Region we're clipping our current drawing to. */
   nsIntRegion mClippingRegion;
   gfx::IntRect mRenderBounds;
 
   /** Current root layer. */
   LayerComposite* RootLayer() const;
 
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -45,17 +45,16 @@
 #include "mozilla/Telemetry.h"
 #ifdef MOZ_WIDGET_GTK
 #include "basic/X11BasicCompositor.h" // for X11BasicCompositor
 #endif
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION, etc
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsIWidget.h"                  // for nsIWidget
-#include "nsIXULRuntime.h"              // for mozilla::BrowserTabsRemoteAutostart
 #include "nsTArray.h"                   // for nsTArray
 #include "nsThreadUtils.h"              // for NS_IsMainThread
 #include "nsXULAppAPI.h"                // for XRE_GetIOMessageLoop
 #ifdef XP_WIN
 #include "mozilla/layers/CompositorD3D11.h"
 #include "mozilla/layers/CompositorD3D9.h"
 #endif
 #include "GeckoProfiler.h"
@@ -658,24 +657,19 @@ CompositorParent::CompositorParent(nsIWi
 
   mRootLayerTreeID = AllocateLayerTreeId();
 
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
   }
 
-  if (gfxPrefs::AsyncPanZoomEnabled() &&
-#if !defined(MOZ_B2G) && !defined(MOZ_WIDGET_ANDROID)
-      // For XUL applications (everything but B2G on mobile and desktop, and
-      // Firefox on Android) we only want to use APZ when E10S is enabled. If
-      // we ever get input events off the main thread we can consider relaxing
-      // this requirement.
-      mozilla::BrowserTabsRemoteAutostart() &&
-#endif
+  // The Compositor uses the APZ pref directly since it needs to know whether
+  // to attempt to create the APZ machinery at all.
+  if (gfxPlatform::AsyncPanZoomEnabled() &&
       (aWidget->WindowType() == eWindowType_toplevel || aWidget->WindowType() == eWindowType_child)) {
     mApzcTreeManager = new APZCTreeManager();
   }
 
   if (UseVsyncComposition()) {
     NS_WARNING("Enabling vsync compositor");
     mCompositorScheduler = new CompositorVsyncScheduler(this, aWidget);
   } else {
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2449,17 +2449,17 @@ DetectBadApzWheelInputPrefs()
   }
 
   return badPref;
 }
 
 void
 gfxPlatform::GetApzSupportInfo(mozilla::widget::InfoObject& aObj)
 {
-  if (!gfxPrefs::AsyncPanZoomEnabled()) {
+  if (!gfxPlatform::AsyncPanZoomEnabled()) {
     return;
   }
 
   if (SupportsApzWheelInput()) {
     nsString badPref = DetectBadApzWheelInputPrefs();
 
     aObj.DefineProperty("ApzWheelInput", 1);
     if (badPref.Length()) {
@@ -2467,8 +2467,22 @@ gfxPlatform::GetApzSupportInfo(mozilla::
     }
   }
 
   if (SupportsApzTouchInput()) {
     aObj.DefineProperty("ApzTouchInput", 1);
   }
 }
 
+/*static*/ bool
+gfxPlatform::AsyncPanZoomEnabled()
+{
+#if !defined(MOZ_B2G) && !defined(MOZ_WIDGET_ANDROID)
+  // For XUL applications (everything but B2G on mobile and desktop, and
+  // Firefox on Android) we only want to use APZ when E10S is enabled. If
+  // we ever get input events off the main thread we can consider relaxing
+  // this requirement.
+  if (!BrowserTabsRemoteAutostart()) {
+    return false;
+  }
+#endif
+  return gfxPrefs::AsyncPanZoomEnabledDoNotUseDirectly();
+}
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -284,16 +284,18 @@ public:
 
     virtual bool UseAcceleratedSkiaCanvas();
     virtual void InitializeSkiaCacheLimits();
 
     /// These should be used instead of directly accessing the preference,
     /// as different platforms may override the behaviour.
     virtual bool UseProgressivePaint() { return gfxPrefs::ProgressivePaintDoNotUseDirectly(); }
 
+    static bool AsyncPanZoomEnabled();
+
     void GetAzureBackendInfo(mozilla::widget::InfoObject &aObj) {
       aObj.DefineProperty("AzureCanvasBackend", GetBackendName(mPreferredCanvasBackend));
       aObj.DefineProperty("AzureSkiaAccelerated", UseAcceleratedSkiaCanvas());
       aObj.DefineProperty("AzureFallbackCanvasBackend", GetBackendName(mFallbackCanvasBackend));
       aObj.DefineProperty("AzureContentBackend", GetBackendName(mContentBackend));
     }
     void GetApzSupportInfo(mozilla::widget::InfoObject& aObj);
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -271,17 +271,17 @@ private:
   DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit",    ImageMTDecodingLimit, int32_t, -1);
   DECL_GFX_PREF(Live, "image.single-color-optimization.enabled", ImageSingleColorOptimizationEnabled, bool, true);
 
   DECL_GFX_PREF(Once, "layers.acceleration.disabled",          LayersAccelerationDisabled, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps",          LayersDrawFPS, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram",  FPSPrintHistogram, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
   DECL_GFX_PREF(Once, "layers.acceleration.force-enabled",     LayersAccelerationForceEnabled, bool, false);
-  DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled",         AsyncPanZoomEnabled, bool, false);
+  DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled",         AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false);
   DECL_GFX_PREF(Once, "layers.async-video.enabled",            AsyncVideoEnabled, bool, true);
   DECL_GFX_PREF(Once, "layers.async-video-oop.enabled",        AsyncVideoOOPEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.bench.enabled",                  LayersBenchEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.bufferrotation.enabled",         BufferRotationEnabled, bool, true);
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   // If MOZ_GFX_OPTIMIZE_MOBILE is defined, we force component alpha off
   // and ignore the preference.
--- a/image/ImageFactory.cpp
+++ b/image/ImageFactory.cpp
@@ -42,21 +42,26 @@ ShouldDownscaleDuringDecode(const nsCStr
 
 static uint32_t
 ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
 {
   nsresult rv;
 
   // We default to the static globals.
   bool isDiscardable = gfxPrefs::ImageMemDiscardable();
-  bool doDecodeOnlyOnDraw = gfxPrefs::ImageDecodeOnlyOnDrawEnabled() &&
-                            gfxPrefs::AsyncPanZoomEnabled();
   bool doDecodeImmediately = gfxPrefs::ImageDecodeImmediatelyEnabled();
   bool doDownscaleDuringDecode = gfxPrefs::ImageDownscaleDuringDecodeEnabled();
 
+  // We use the compositor APZ pref here since we don't have a widget to test.
+  // It's safe since this is an optimization, and the only platform
+  // ImageDecodeOnlyOnDraw is disabled on is B2G (where APZ is enabled in all
+  // widgets anyway).
+  bool doDecodeOnlyOnDraw = gfxPrefs::ImageDecodeOnlyOnDrawEnabled() &&
+                            gfxPrefs::AsyncPanZoomEnabledDoNotUseDirectly();
+
   // We want UI to be as snappy as possible and not to flicker. Disable
   // discarding and decode-only-on-draw for chrome URLS.
   bool isChrome = false;
   rv = uri->SchemeIs("chrome", &isChrome);
   if (NS_SUCCEEDED(rv) && isChrome) {
     isDiscardable = doDecodeOnlyOnDraw = false;
   }
 
--- a/layout/base/AccessibleCaretEventHub.cpp
+++ b/layout/base/AccessibleCaretEventHub.cpp
@@ -3,16 +3,17 @@
 /* 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 "AccessibleCaretEventHub.h"
 
 #include "AccessibleCaretLogger.h"
 #include "AccessibleCaretManager.h"
+#include "Layers.h"
 #include "gfxPrefs.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "nsDocShell.h"
 #include "nsFocusManager.h"
 #include "nsFrameSelection.h"
 #include "nsITimer.h"
@@ -406,17 +407,17 @@ AccessibleCaretEventHub::Init(nsIPresShe
   MOZ_ASSERT(presContext, "PresContext should be given in PresShell::Init()");
 
   nsIDocShell* docShell = presContext->GetDocShell();
   if (!docShell) {
     return;
   }
 
 #if defined(MOZ_WIDGET_GONK)
-  mUseAsyncPanZoom = gfxPrefs::AsyncPanZoomEnabled();
+  mUseAsyncPanZoom = mPresShell->AsyncPanZoomEnabled();
 #endif
 
   docShell->AddWeakReflowObserver(this);
   docShell->AddWeakScrollObserver(this);
 
   mDocShell = static_cast<nsDocShell*>(docShell);
 
   mLongTapInjectorTimer = do_CreateInstance("@mozilla.org/timer;1");
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -4880,17 +4880,17 @@ FrameLayerBuilder::BuildContainerLayerFo
     (aManager->GetUserData(&gLayerManagerUserData));
 
   nsIntRect pixBounds;
   nscoord appUnitsPerDevPixel;
   bool flattenToSingleLayer = false;
   if ((aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) &&
       mRetainingManager &&
       mRetainingManager->ShouldAvoidComponentAlphaLayers() &&
-      !gfxPrefs::AsyncPanZoomEnabled())
+      !nsLayoutUtils::AsyncPanZoomEnabled(aContainerFrame))
   {
     flattenToSingleLayer = true;
   }
 
   nscolor backgroundColor = NS_RGBA(0,0,0,0);
   if (aFlags & CONTAINER_ALLOW_PULL_BACKGROUND_COLOR) {
     backgroundColor = aParameters.mBackgroundColor;
   }
@@ -4912,17 +4912,17 @@ FrameLayerBuilder::BuildContainerLayerFo
     appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel();
     state.Finish(&flags, data, pixBounds, aChildren, hasComponentAlphaChildren);
 
     if (hasComponentAlphaChildren &&
         mRetainingManager &&
         mRetainingManager->ShouldAvoidComponentAlphaLayers() &&
         containerLayer->HasMultipleChildren() &&
         !flattenToSingleLayer &&
-        !gfxPrefs::AsyncPanZoomEnabled())
+        !nsLayoutUtils::AsyncPanZoomEnabled(aContainerFrame))
     {
       // Since we don't want any component alpha layers on BasicLayers, we repeat
       // the layer building process with this explicitely forced off.
       // We restore the previous FrameLayerBuilder state since the first set
       // of layer building will have changed it.
       flattenToSingleLayer = true;
       data->mDisplayItems.EnumerateEntries(RestoreDisplayItemData,
                                            &mContainerLayerGeneration);
--- a/layout/base/SelectionCarets.cpp
+++ b/layout/base/SelectionCarets.cpp
@@ -28,16 +28,17 @@
 #include "nsView.h"
 #include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ScrollViewChangeEvent.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/TreeWalker.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TouchEvents.h"
+#include "Layers.h"
 #include "TouchCaret.h"
 #include "nsFrameSelection.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static PRLogModuleInfo* gSelectionCaretsLog;
 static const char* kSelectionCaretsLogModuleName = "SelectionCarets";
@@ -111,17 +112,17 @@ SelectionCarets::Init()
   MOZ_ASSERT(presContext, "PresContext should be given in PresShell::Init()");
 
   nsIDocShell* docShell = presContext->GetDocShell();
   if (!docShell) {
     return;
   }
 
 #if defined(MOZ_WIDGET_GONK)
-  mUseAsyncPanZoom = gfxPrefs::AsyncPanZoomEnabled();
+  mUseAsyncPanZoom = mPresShell->AsyncPanZoomEnabled();
 #endif
 
   docShell->AddWeakReflowObserver(this);
   docShell->AddWeakScrollObserver(this);
 
   mDocShell = static_cast<nsDocShell*>(docShell);
 }
 
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -630,17 +630,18 @@ nsDisplayListBuilder::nsDisplayListBuild
       mSyncDecodeImages(false),
       mIsPaintingToWindow(false),
       mIsCompositingCheap(false),
       mContainsPluginItem(false),
       mAncestorHasApzAwareEventHandler(false),
       mHaveScrollableDisplayPort(false),
       mWindowDraggingAllowed(false),
       mIsBuildingForPopup(nsLayoutUtils::IsPopup(aReferenceFrame)),
-      mForceLayerForScrollParent(false)
+      mForceLayerForScrollParent(false),
+      mAsyncPanZoomEnabled(nsLayoutUtils::AsyncPanZoomEnabled(aReferenceFrame))
 {
   MOZ_COUNT_CTOR(nsDisplayListBuilder);
   PL_InitArenaPool(&mPool, "displayListArena", 1024,
                    std::max(NS_ALIGNMENT_OF(void*),NS_ALIGNMENT_OF(double))-1);
   RecomputeCurrentAnimatedGeometryRoot();
 
   nsPresContext* pc = aReferenceFrame->PresContext();
   nsIPresShell *shell = pc->PresShell();
@@ -1548,17 +1549,17 @@ already_AddRefed<LayerManager> nsDisplay
   // using XUL windows, there is no root scrollframe, and without explicitly
   // creating metrics there will be no guaranteed top-level APZC.
   bool addMetrics = gfxPrefs::LayoutUseContainersForRootFrames() ||
       (XRE_IsParentProcess() && !presShell->GetRootScrollFrame());
 
   // Add metrics if there are none in the layer tree with the id (create an id
   // if there isn't one already) of the root scroll frame/root content.
   bool ensureMetricsForRootId =
-    gfxPrefs::AsyncPanZoomEnabled() &&
+    nsLayoutUtils::AsyncPanZoomEnabled(frame) &&
     !gfxPrefs::LayoutUseContainersForRootFrames() &&
     aBuilder->IsPaintingToWindow() &&
     !presContext->GetParentPresContext();
 
   nsIContent* content = nullptr;
   nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
   if (rootScrollFrame) {
     content = rootScrollFrame->GetContent();
@@ -2328,17 +2329,17 @@ nsDisplayBackgroundImage::IsSingleFixedP
   return true;
 }
 
 bool
 nsDisplayBackgroundImage::ShouldFixToViewport(LayerManager* aManager)
 {
   // APZ doesn't (yet) know how to scroll the visible region for these type of
   // items, so don't layerize them if it's enabled.
-  if (nsLayoutUtils::UsesAsyncScrolling() ||
+  if (nsLayoutUtils::UsesAsyncScrolling(mFrame) ||
       (aManager && aManager->ShouldAvoidComponentAlphaLayers())) {
     return false;
   }
 
   // Put background-attachment:fixed background images in their own
   // compositing layer, unless we have APZ enabled
   return mBackgroundStyle->mLayers[mLayer].mAttachment == NS_STYLE_BG_ATTACHMENT_FIXED &&
          !mBackgroundStyle->mLayers[mLayer].mImage.IsEmpty();
@@ -4270,17 +4271,17 @@ nsDisplaySubDocument::ComputeVisibility(
     childVisibleRegion.GetBounds().Intersect(mList.GetBounds(aBuilder));
   bool visible = mList.ComputeVisibilityForSublist(
     aBuilder, &childVisibleRegion, boundedRect,
     usingDisplayPort ? mFrame : nullptr);
 
   // If APZ is enabled then don't allow this computation to influence
   // aVisibleRegion, on the assumption that the layer can be asynchronously
   // scrolled so we'll definitely need all the content under it.
-  if (!nsLayoutUtils::UsesAsyncScrolling()) {
+  if (!nsLayoutUtils::UsesAsyncScrolling(mFrame)) {
     bool snap;
     nsRect bounds = GetBounds(aBuilder, &snap);
     nsRegion removed;
     removed.Sub(bounds, childVisibleRegion);
 
     aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed);
   }
 
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -345,18 +345,18 @@ public:
   {
     mLayerEventRegions = aItem;
   }
   bool IsBuildingLayerEventRegions()
   {
     if (mMode == PAINTING) {
       // Note: this is the only place that gets to query LayoutEventRegionsEnabled
       // 'directly' - other code should call this function.
-      return (gfxPrefs::LayoutEventRegionsEnabledDoNotUseDirectly() ||
-              gfxPrefs::AsyncPanZoomEnabled());
+      return gfxPrefs::LayoutEventRegionsEnabledDoNotUseDirectly() ||
+             mAsyncPanZoomEnabled;
     }
     return false;
   }
   bool IsInsidePointerEventsNoneDoc()
   {
     return CurrentPresShellState()->mInsidePointerEventsNoneDoc;
   }
 
@@ -991,16 +991,17 @@ private:
   bool                           mAncestorHasApzAwareEventHandler;
   // True when the first async-scrollable scroll frame for which we build a
   // display list has a display port. An async-scrollable scroll frame is one
   // which WantsAsyncScroll().
   bool                           mHaveScrollableDisplayPort;
   bool                           mWindowDraggingAllowed;
   bool                           mIsBuildingForPopup;
   bool                           mForceLayerForScrollParent;
+  bool                           mAsyncPanZoomEnabled;
 };
 
 class nsDisplayItem;
 class nsDisplayList;
 /**
  * nsDisplayItems are put in singly-linked lists rooted in an nsDisplayList.
  * nsDisplayItemLink holds the link. The lists are linked from lowest to
  * highest in z-order.
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1364,16 +1364,22 @@ public:
 
   /**
    * Get the layer manager for the widget of the root view, if it has
    * one.
    */
   virtual LayerManager* GetLayerManager() = 0;
 
   /**
+   * Return true iff there is a widget rendering this presShell and that
+   * widget is APZ-enabled.
+   */
+  virtual bool AsyncPanZoomEnabled() = 0;
+
+  /**
    * Track whether we're ignoring viewport scrolling for the purposes
    * of painting.  If we are ignoring, then layers aren't clipped to
    * the CSS viewport and scrollbars aren't drawn.
    */
   virtual void SetIgnoreViewportScrolling(bool aIgnore) = 0;
   bool IgnoringViewportScrolling() const
   { return mRenderFlags & STATE_IGNORING_VIEWPORT_SCROLLING; }
 
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -810,16 +810,44 @@ ApplyRectMultiplier(nsRect aRect, float 
   float newWidth = aRect.width * aMultiplier;
   float newHeight = aRect.height * aMultiplier;
   float newX = aRect.x - ((newWidth - aRect.width) / 2.0f);
   float newY = aRect.y - ((newHeight - aRect.height) / 2.0f);
   // Rounding doesn't matter too much here, do a round-in
   return nsRect(ceil(newX), ceil(newY), floor(newWidth), floor(newHeight));
 }
 
+bool
+nsLayoutUtils::UsesAsyncScrolling(nsIFrame* aFrame)
+{
+#ifdef MOZ_WIDGET_ANDROID
+  // We always have async scrolling for android
+  return true;
+#endif
+
+  return AsyncPanZoomEnabled(aFrame);
+}
+
+bool
+nsLayoutUtils::AsyncPanZoomEnabled(nsIFrame* aFrame)
+{
+  // We use this as a shortcut, since if the compositor will never use APZ,
+  // no widget will either.
+  if (!gfxPrefs::AsyncPanZoomEnabledDoNotUseDirectly()) {
+    return false;
+  }
+
+  nsIFrame *frame = nsLayoutUtils::GetDisplayRootFrame(aFrame);
+  nsIWidget* widget = frame->GetNearestWidget();
+  if (!widget) {
+    return false;
+  }
+  return widget->AsyncPanZoomEnabled();
+}
+
 // Return the maximum displayport size, based on the LayerManager's maximum
 // supported texture size. The result is in app units.
 static nscoord
 GetMaxDisplayPortSize(nsIContent* aContent)
 {
   MOZ_ASSERT(!gfxPrefs::LayersTilesEnabled(), "Do not clamp displayports if tiling is enabled");
 
   nsIFrame* frame = aContent->GetPrimaryFrame();
@@ -1088,19 +1116,22 @@ nsLayoutUtils::SetDisplayPortMargins(nsI
     return false;
   }
 
   aContent->SetProperty(nsGkAtoms::DisplayPortMargins,
                         new DisplayPortMarginsPropertyData(
                             aMargins, aPriority),
                         nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
 
-  if (nsLayoutUtils::UsesAsyncScrolling() && gfxPrefs::LayoutUseContainersForRootFrames()) {
+  if (gfxPrefs::LayoutUseContainersForRootFrames()) {
     nsIFrame* rootScrollFrame = aPresShell->GetRootScrollFrame();
-    if (rootScrollFrame && aContent == rootScrollFrame->GetContent()) {
+    if (rootScrollFrame &&
+        aContent == rootScrollFrame->GetContent() &&
+        nsLayoutUtils::UsesAsyncScrolling(rootScrollFrame))
+    {
       // We are setting a root displayport for a document.
       // If we have APZ, then set a special flag on the pres shell so
       // that we don't get scrollbars drawn.
       aPresShell->SetIgnoreViewportScrolling(true);
     }
   }
 
   if (aRepaintMode == RepaintMode::Repaint) {
@@ -2982,17 +3013,17 @@ nsLayoutUtils::GetOrMaybeCreateDisplayPo
 
   // We perform an optimization where we ensure that at least one
   // async-scrollable frame (i.e. one that WantsAsyncScroll()) has a displayport.
   // If that's not the case yet, and we are async-scrollable, we will get a
   // displayport.
   // Note: we only do this in processes where we do subframe scrolling to
   //       begin with (i.e., not in the parent process on B2G).
   if (aBuilder.IsPaintingToWindow() &&
-      gfxPrefs::AsyncPanZoomEnabled() &&
+      nsLayoutUtils::AsyncPanZoomEnabled(aScrollFrame) &&
       !aBuilder.HaveScrollableDisplayPort() &&
       scrollableFrame->WantAsyncScroll()) {
 
     // If we don't already have a displayport, calculate and set one.
     if (!haveDisplayPort) {
       CalculateAndSetDisplayPortMargins(scrollableFrame, nsLayoutUtils::RepaintMode::DoNotRepaint);
       haveDisplayPort = GetDisplayPort(content, aOutDisplayport);
       NS_ASSERTION(haveDisplayPort, "should have a displayport after having just set it");
@@ -7833,27 +7864,16 @@ nsLayoutUtils::CalculateExpandedScrollab
   if (scrollableRect.height < compSize.height) {
     scrollableRect.y = std::max(0,
                                 scrollableRect.y - (compSize.height - scrollableRect.height));
     scrollableRect.height = compSize.height;
   }
   return scrollableRect;
 }
 
-/* static */ bool
-nsLayoutUtils::UsesAsyncScrolling()
-{
-#ifdef MOZ_WIDGET_ANDROID
-  // We always have async scrolling for android
-  return true;
-#endif
-
-  return gfxPrefs::AsyncPanZoomEnabled();
-}
-
 /* static */ void
 nsLayoutUtils::DoLogTestDataForPaint(LayerManager* aManager,
                                      ViewID aScrollId,
                                      const std::string& aKey,
                                      const std::string& aValue)
 {
   if (aManager->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
     static_cast<ClientLayerManager*>(aManager)->LogTestDataForCurrentPaint(aScrollId, aKey, aValue);
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2524,20 +2524,26 @@ public:
  /**
   * Calculate the expanded scrollable rect for a frame. See FrameMetrics.h for
   * defintion of expanded scrollable rect.
   */
   static nsRect
   CalculateExpandedScrollableRect(nsIFrame* aFrame);
 
   /**
-   * Returns true if we're using asynchronous scrolling (either through
-   * APZ or the android frontend).
+   * Returns true if the widget owning the given frame uses asynchronous
+   * scrolling.
    */
-  static bool UsesAsyncScrolling();
+  static bool UsesAsyncScrolling(nsIFrame* aFrame);
+
+  /**
+   * Returns true if the widget owning the given frame has builtin APZ support
+   * enabled.
+   */
+  static bool AsyncPanZoomEnabled(nsIFrame* aFrame);
 
   /**
    * Log a key/value pair for APZ testing during a paint.
    * @param aManager   The data will be written to the APZTestData associated 
    *                   with this layer manager.
    * @param aScrollId Identifies the scroll frame to which the data pertains.
    * @param aKey The key under which to log the data.
    * @param aValue The value of the data to be logged.
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5553,16 +5553,28 @@ LayerManager* PresShell::GetLayerManager
   if (rootView) {
     if (nsIWidget* widget = rootView->GetWidget()) {
       return widget->GetLayerManager();
     }
   }
   return nullptr;
 }
 
+bool PresShell::AsyncPanZoomEnabled()
+{
+  NS_ASSERTION(mViewManager, "Should have view manager");
+  nsView* rootView = mViewManager->GetRootView();
+  if (rootView) {
+    if (nsIWidget* widget = rootView->GetWidget()) {
+      return widget->AsyncPanZoomEnabled();
+    }
+  }
+  return false;
+}
+
 void PresShell::SetIgnoreViewportScrolling(bool aIgnore)
 {
   if (IgnoringViewportScrolling() == aIgnore) {
     return;
   }
   RenderingState state(this);
   state.mRenderFlags = ChangeFlag(state.mRenderFlags, aIgnore,
                                   STATE_IGNORING_VIEWPORT_SCROLLING);
@@ -11048,20 +11060,20 @@ nsIPresShell::RecomputeFontSizeInflation
   if ((FontSizeInflationEmPerLine() == 0 &&
       FontSizeInflationMinTwips() == 0) || mPresContext->IsChrome()) {
     mFontSizeInflationEnabled = false;
     return;
   }
 
   // Force-enabling font inflation always trumps the heuristics here.
   if (!FontSizeInflationForceEnabled()) {
-    if (TabChild::GetFrom(this)) {
+    if (TabChild* tab = TabChild::GetFrom(this)) {
       // We're in a child process.  Cancel inflation if we're not
       // async-pan zoomed.
-      if (!gfxPrefs::AsyncPanZoomEnabled()) {
+      if (!tab->AsyncPanZoomEnabled()) {
         mFontSizeInflationEnabled = false;
         return;
       }
     } else if (XRE_GetProcessType() == GeckoProcessType_Default) {
       // We're in the master process.  Cancel inflation if it's been
       // explicitly disabled.
       if (FontSizeInflationDisabledInMasterProcess()) {
         mFontSizeInflationEnabled = false;
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -193,16 +193,18 @@ public:
   RenderSelection(nsISelection* aSelection,
                   nsIntPoint& aPoint,
                   nsIntRect* aScreenRect) override;
 
   virtual already_AddRefed<nsPIDOMWindow> GetRootWindow() override;
 
   virtual LayerManager* GetLayerManager() override;
 
+  virtual bool AsyncPanZoomEnabled() override;
+
   virtual void SetIgnoreViewportScrolling(bool aIgnore) override;
 
   virtual nsresult SetResolution(float aResolution) override {
     return SetResolutionImpl(aResolution, /* aScaleToResolution = */ false);
   }
   virtual nsresult SetResolutionAndScaleTo(float aResolution) override {
     return SetResolutionImpl(aResolution, /* aScaleToResolution = */ true);
   }
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1821,17 +1821,17 @@ ScrollFrameHelper::ScrollFrameHelper(nsC
   if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
     mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
   }
 
   EnsureImageVisPrefsCached();
 
   if (IsAlwaysActive() &&
       gfxPrefs::LayersTilesEnabled() &&
-      !nsLayoutUtils::UsesAsyncScrolling() &&
+      !nsLayoutUtils::UsesAsyncScrolling(mOuter) &&
       mOuter->GetContent()) {
     // If we have tiling but no APZ, then set a 0-margin display port on
     // active scroll containers so that we paint by whole tile increments
     // when scrolling.
     nsLayoutUtils::SetDisplayPortMargins(mOuter->GetContent(),
                                          mOuter->PresContext()->PresShell(),
                                          ScreenMargin(),
                                          0,
@@ -2025,17 +2025,17 @@ ScrollFrameHelper::ScrollToWithOrigin(ns
         currentVelocity.height = sv.y;
         if (mAsyncScroll) {
           if (mAsyncScroll->mIsSmoothScroll) {
             currentVelocity = mAsyncScroll->VelocityAt(now);
           }
           mAsyncScroll = nullptr;
         }
 
-        if (gfxPrefs::AsyncPanZoomEnabled()) {
+        if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter)) {
           // The animation will be handled in the compositor, pass the
           // information needed to start the animation and skip the main-thread
           // animation for this scroll.
           mLastSmoothScrollOrigin = aOrigin;
           mScrollGeneration = ++sScrollGenerationCounter;
 
           if (!nsLayoutUtils::GetDisplayPort(mOuter->GetContent())) {
             // If this frame doesn't have a displayport then there won't be an
@@ -2909,17 +2909,17 @@ ScrollFrameHelper::BuildDisplayList(nsDi
   // If the element is marked 'scrollgrab', also force building of a layer
   // so that APZ can implement scroll grabbing.
   mShouldBuildScrollableLayer = usingDisplayport || nsContentUtils::HasScrollgrab(mOuter->GetContent());
   bool shouldBuildLayer = false;
   if (mShouldBuildScrollableLayer) {
     shouldBuildLayer = true;
   } else {
     shouldBuildLayer =
-      gfxPrefs::AsyncPanZoomEnabled() &&
+      nsLayoutUtils::AsyncPanZoomEnabled(mOuter) &&
       WantAsyncScroll() &&
       // If we are using containers for root frames, and we are the root
       // scroll frame for the display root, then we don't need a scroll
       // info layer. nsDisplayList::PaintForFrame already calls
       // ComputeFrameMetrics for us.
       (!(gfxPrefs::LayoutUseContainersForRootFrames() && mIsRoot) ||
        (aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext()));
   }
@@ -3080,17 +3080,17 @@ ScrollFrameHelper::ComputeFrameMetrics(L
 
     if (mAncestorClip && mAncestorClip->HasClip()) {
       clip = mAncestorClip->GetClipRect().Intersect(clip);
     }
 
     parentLayerClip = Some(clip);
   }
 
-  bool thisScrollFrameUsesAsyncScrolling = nsLayoutUtils::UsesAsyncScrolling();
+  bool thisScrollFrameUsesAsyncScrolling = nsLayoutUtils::UsesAsyncScrolling(mOuter);
 #if defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_ANDROID_APZ)
   // Android without apzc (aka the java pan zoom code) only uses async scrolling
   // for the root scroll frame of the root content document.
   if (!isRoot) {
     thisScrollFrameUsesAsyncScrolling = false;
   }
 #endif
   if (!thisScrollFrameUsesAsyncScrolling) {
@@ -3398,17 +3398,18 @@ ScrollFrameHelper::ScrollBy(nsIntPoint a
     nsPoint clampAmount = newPos - mDestination;
     float appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
     *aOverflow = nsIntPoint(
         NSAppUnitsToIntPixels(clampAmount.x, appUnitsPerDevPixel),
         NSAppUnitsToIntPixels(clampAmount.y, appUnitsPerDevPixel));
   }
 
   if (aUnit == nsIScrollableFrame::DEVICE_PIXELS &&
-      !gfxPrefs::AsyncPanZoomEnabled()) {
+      !nsLayoutUtils::AsyncPanZoomEnabled(mOuter))
+  {
     // When APZ is disabled, we must track the velocity
     // on the main thread; otherwise, the APZC will manage this.
     mVelocityQueue.Sample(GetScrollPosition());
   }
 }
 
 void
 ScrollFrameHelper::ScrollSnap(nsIScrollableFrame::ScrollMode aMode)
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -294,25 +294,29 @@ private:
 RenderFrameParent::RenderFrameParent(nsFrameLoader* aFrameLoader,
                                      TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                      uint64_t* aId,
                                      bool* aSuccess)
   : mLayersId(0)
   , mFrameLoader(aFrameLoader)
   , mFrameLoaderDestroyed(false)
   , mBackgroundColor(gfxRGBA(1, 1, 1))
+  , mAsyncPanZoomEnabled(false)
 {
   *aSuccess = false;
   if (!mFrameLoader) {
     return;
   }
 
   *aId = 0;
 
   nsRefPtr<LayerManager> lm = GetFrom(mFrameLoader);
+
+  mAsyncPanZoomEnabled = lm && lm->AsyncPanZoomEnabled();
+
   // Perhaps the document containing this frame currently has no presentation?
   if (lm && lm->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
     *aTextureFactoryIdentifier =
       static_cast<ClientLayerManager*>(lm.get())->GetTextureFactoryIdentifier();
   } else {
     *aTextureFactoryIdentifier = TextureFactoryIdentifier();
   }
 
@@ -320,17 +324,17 @@ RenderFrameParent::RenderFrameParent(nsF
     // Our remote frame will push layers updates to the compositor,
     // and we'll keep an indirect reference to that tree.
     *aId = mLayersId = CompositorParent::AllocateLayerTreeId();
     if (lm && lm->GetBackendType() == LayersBackend::LAYERS_CLIENT) {
       ClientLayerManager *clientManager =
         static_cast<ClientLayerManager*>(lm.get());
       clientManager->GetRemoteRenderer()->SendNotifyChildCreated(mLayersId);
     }
-    if (gfxPrefs::AsyncPanZoomEnabled()) {
+    if (mAsyncPanZoomEnabled) {
       mContentController = new RemoteContentController(this);
       CompositorParent::SetControllerForLayerTree(mLayersId, mContentController);
     }
   } else if (XRE_GetProcessType() == GeckoProcessType_Content) {
     ContentChild::GetSingleton()->SendAllocateLayerTreeId(aId);
     mLayersId = *aId;
     CompositorChild::Get()->SendNotifyChildCreated(mLayersId);
   }
@@ -341,17 +345,17 @@ RenderFrameParent::RenderFrameParent(nsF
 
 APZCTreeManager*
 RenderFrameParent::GetApzcTreeManager()
 {
   // We can't get a ref to the APZCTreeManager until after the child is
   // created and the static getter knows which CompositorParent is
   // instantiated with this layers ID. That's why try to fetch it when
   // we first need it and cache the result.
-  if (!mApzcTreeManager && gfxPrefs::AsyncPanZoomEnabled()) {
+  if (!mApzcTreeManager && mAsyncPanZoomEnabled) {
     mApzcTreeManager = CompositorParent::GetAPZCTreeManager(mLayersId);
   }
   return mApzcTreeManager.get();
 }
 
 RenderFrameParent::~RenderFrameParent()
 {}
 
--- a/layout/ipc/RenderFrameParent.h
+++ b/layout/ipc/RenderFrameParent.h
@@ -146,16 +146,18 @@ private:
   // 
   // It's possible for mFrameLoader==null and
   // mFrameLoaderDestroyed==false.
   bool mFrameLoaderDestroyed;
   // this is gfxRGBA because that's what ColorLayer wants.
   gfxRGBA mBackgroundColor;
 
   nsRegion mTouchRegion;
+
+  bool mAsyncPanZoomEnabled;
 };
 
 } // namespace layout
 } // namespace mozilla
 
 /**
  * A DisplayRemote exists solely to graft a child process's shadow
  * layer tree (for a given RenderFrameParent) into its parent
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -740,17 +740,17 @@ function BuildConditionSandbox(aURL) {
     };
 
     // Tests shouldn't care about this except for when they need to
     // crash the content process
     sandbox.browserIsRemote = gBrowserIsRemote;
     sandbox.Mulet = gB2GisMulet;
 
     try {
-        sandbox.asyncPanZoom = prefs.getBoolPref("layers.async-pan-zoom.enabled");
+        sandbox.asyncPanZoom = gContainingWindow.document.docShell.asyncPanZoomEnabled;
     } catch (e) {
         sandbox.asyncPanZoom = false;
     }
 
     if (!gDumpedConditionSandbox) {
         dump("REFTEST INFO | Dumping JSON representation of sandbox \n");
         dump("REFTEST INFO | " + JSON.stringify(CU.waiveXrays(sandbox)) + " \n");
         gDumpedConditionSandbox = true;
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -339,17 +339,17 @@ PuppetWidget::DispatchInputEvent(WidgetI
   }
 
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus
 PuppetWidget::DispatchAPZAwareEvent(WidgetInputEvent* aEvent)
 {
-  if (!gfxPrefs::AsyncPanZoomEnabled()) {
+  if (!AsyncPanZoomEnabled()) {
     nsEventStatus status = nsEventStatus_eIgnore;
     DispatchEvent(aEvent, status);
     return status;
   }
 
   if (!mTabChild) {
     return nsEventStatus_eIgnore;
   }
@@ -478,16 +478,22 @@ void
 PuppetWidget::SetConfirmedTargetAPZC(uint64_t aInputBlockId,
                                      const nsTArray<ScrollableLayerGuid>& aTargets) const
 {
   if (mTabChild) {
     mTabChild->SendSetTargetAPZC(aInputBlockId, aTargets);
   }
 }
 
+bool
+PuppetWidget::AsyncPanZoomEnabled() const
+{
+  return mTabChild && mTabChild->AsyncPanZoomEnabled();
+}
+
 NS_IMETHODIMP_(bool)
 PuppetWidget::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
                                       const mozilla::WidgetKeyboardEvent& aEvent,
                                       DoCommandCallback aCallback,
                                       void* aCallbackData)
 {
   // B2G doesn't have native key bindings.
 #ifdef MOZ_WIDGET_GONK
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -130,16 +130,17 @@ public:
 
   void InitEvent(WidgetGUIEvent& aEvent, nsIntPoint* aPoint = nullptr);
 
   NS_IMETHOD DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus) override;
   nsEventStatus DispatchAPZAwareEvent(WidgetInputEvent* aEvent) override;
   nsEventStatus DispatchInputEvent(WidgetInputEvent* aEvent) override;
   void SetConfirmedTargetAPZC(uint64_t aInputBlockId,
                               const nsTArray<ScrollableLayerGuid>& aTargets) const override;
+  bool AsyncPanZoomEnabled() const override;
 
   NS_IMETHOD CaptureRollupEvents(nsIRollupListener* aListener,
                                  bool aDoCapture) override
   { return NS_ERROR_UNEXPECTED; }
 
   NS_IMETHOD_(bool)
   ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
                           const mozilla::WidgetKeyboardEvent& aEvent,
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1005,16 +1005,22 @@ nsBaseWidget::SetConfirmedTargetAPZC(uin
 {
   // Need to specifically bind this since it's overloaded.
   void (APZCTreeManager::*setTargetApzcFunc)(uint64_t, const nsTArray<ScrollableLayerGuid>&)
           = &APZCTreeManager::SetTargetAPZC;
   APZThreadUtils::RunOnControllerThread(NewRunnableMethod(
     mAPZC.get(), setTargetApzcFunc, aInputBlockId, mozilla::Move(aTargets)));
 }
 
+bool
+nsBaseWidget::AsyncPanZoomEnabled() const
+{
+  return !!mAPZC;
+}
+
 nsEventStatus
 nsBaseWidget::ProcessUntransformedAPZEvent(WidgetInputEvent* aEvent,
                                            const ScrollableLayerGuid& aGuid,
                                            uint64_t aInputBlockId,
                                            nsEventStatus aApzResponse)
 {
   MOZ_ASSERT(NS_IsMainThread());
   InputAPZContext context(aGuid, aInputBlockId, aApzResponse);
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -238,16 +238,18 @@ public:
   nsEventStatus DispatchInputEvent(mozilla::WidgetInputEvent* aEvent) override;
 
   // Dispatch an event that must be first be routed through APZ.
   nsEventStatus DispatchAPZAwareEvent(mozilla::WidgetInputEvent* aEvent) override;
 
   void SetConfirmedTargetAPZC(uint64_t aInputBlockId,
                               const nsTArray<ScrollableLayerGuid>& aTargets) const override;
 
+  bool AsyncPanZoomEnabled() const override;
+
   void NotifyWindowDestroyed();
   void NotifySizeMoveDone();
   void NotifyWindowMoved(int32_t aX, int32_t aY);
 
   // Register plugin windows for remote updates from the compositor
   virtual void RegisterPluginWindowForRemoteUpdates() override;
   virtual void UnregisterPluginWindowForRemoteUpdates() override;
 
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1816,16 +1816,21 @@ class nsIWidget : public nsISupports {
     /**
      * Confirm an APZ-aware event target. This should be used when APZ will
      * not need a layers update to process the event.
      */
     virtual void SetConfirmedTargetAPZC(uint64_t aInputBlockId,
                                         const nsTArray<mozilla::layers::ScrollableLayerGuid>& aTargets) const = 0;
 
     /**
+     * Returns true if APZ is in use, false otherwise.
+     */
+    virtual bool AsyncPanZoomEnabled() const = 0;
+
+    /**
      * Enables the dropping of files to a widget (XXX this is temporary)
      *
      */
     NS_IMETHOD EnableDragDrop(bool aEnable) = 0;
    
     /**
      * Enables/Disables system mouse capture.
      * @param aCapture true enables mouse capture, false disables mouse capture