Add scroll listeners to dispatch-to-content regions. (bug 1013432 part 5, r=tn)
authorDavid Anderson <danderson@mozilla.com>
Tue, 09 Dec 2014 02:38:23 -0800
changeset 244668 89858cf2820485c236d33ef45b3cf9f8570d29fd
parent 244667 d0bef02fe97707ade1d10fd3bf6b80ed78b7132a
child 244669 5493ccef57ca405f9d9ee3956ea80d693dbcf2d3
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1013432
milestone37.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
Add scroll listeners to dispatch-to-content regions. (bug 1013432 part 5, r=tn)
dom/base/nsGlobalWindow.cpp
dom/base/nsNodeUtils.cpp
dom/base/nsPIDOMWindow.h
dom/events/EventListenerManager.cpp
dom/events/EventListenerManager.h
gfx/layers/FrameMetrics.h
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/InputBlockState.h.orig
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/generic/nsFrame.cpp
layout/generic/nsSubDocumentFrame.cpp
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -561,17 +561,19 @@ nsTimeout::HasRefCntOne()
   return mRefCnt.get() == 1;
 }
 
 nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
 : mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0),
   mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false),
   mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
   mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
-  mMayHaveTouchCaret(false), mMayHaveMouseEnterLeaveEventListener(false),
+  mMayHaveTouchCaret(false),
+  mMayHaveScrollWheelEventListener(false),
+  mMayHaveMouseEnterLeaveEventListener(false),
   mMayHavePointerEnterLeaveEventListener(false),
   mIsModalContentWindow(false),
   mIsActive(false), mIsBackground(false),
   mAudioMuted(false), mAudioVolume(1.0),
   mInnerWindow(nullptr), mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false),
   mMarkedCCGeneration(0), mSendAfterRemotePaint(false)
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -405,16 +405,19 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
         if (elm) {
           window->SetMutationListeners(elm->MutationListenerBits());
           if (elm->MayHavePaintEventListener()) {
             window->SetHasPaintEventListeners();
           }
           if (elm->MayHaveTouchEventListener()) {
             window->SetHasTouchEventListeners();
           }
+          if (elm->MayHaveScrollWheelEventListener()) {
+            window->SetHasScrollWheelEventListeners();
+          }
           if (elm->MayHaveMouseEnterLeaveEventListener()) {
             window->SetHasMouseEnterLeaveEventListeners();
           }
           if (elm->MayHavePointerEnterLeaveEventListener()) {
             window->SetHasPointerEnterLeaveEventListeners();
           }
         }
       }
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -438,16 +438,39 @@ public:
     MaybeUpdateTouchState();
   }
 
   bool HasTouchEventListeners()
   {
     return mMayHaveTouchEventListener;
   }
 
+  /**
+   * Call this to indicate that some node (this window, its document,
+   * or content in that document) has a scroll wheel event listener.
+   */
+  void SetHasScrollWheelEventListeners()
+  {
+    mMayHaveScrollWheelEventListener = true;
+  }
+
+  bool HasScrollWheelEventListeners()
+  {
+    return mMayHaveScrollWheelEventListener;
+  }
+
+  /**
+   * Returns whether or not any event listeners are present that APZ must be
+   * aware of.
+   */
+  bool HasApzAwareEventListeners()
+  {
+    return HasTouchEventListeners() || HasScrollWheelEventListeners();
+  }
+
    /**
    * Will be called when touch caret visibility has changed. mMayHaveTouchCaret
    * is set if that some node (this window, its document, or content in that
    * document) has a visible touch caret.
    */
   void SetMayHaveTouchCaret(bool aSetValue)
   {
     mMayHaveTouchCaret = aSetValue;
@@ -786,16 +809,17 @@ protected:
   uint32_t               mMutationBits;
 
   bool                   mIsDocumentLoaded;
   bool                   mIsHandlingResizeEvent;
   bool                   mIsInnerWindow;
   bool                   mMayHavePaintEventListener;
   bool                   mMayHaveTouchEventListener;
   bool                   mMayHaveTouchCaret;
+  bool                   mMayHaveScrollWheelEventListener;
   bool                   mMayHaveMouseEnterLeaveEventListener;
   bool                   mMayHavePointerEnterLeaveEventListener;
 
   // This variable is used on both inner and outer windows (and they
   // should match).
   bool                   mIsModalContentWindow;
 
   // Tracks activation state that's used for :-moz-window-inactive.
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -26,16 +26,17 @@
 #include "EventListenerService.h"
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsDOMCID.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
+#include "nsHtml5Atoms.h"
 #include "nsIContent.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocument.h"
 #include "nsIDOMEventListener.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsISupports.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
@@ -92,16 +93,17 @@ MutationBitForEventType(uint32_t aEventT
 uint32_t EventListenerManager::sMainThreadCreatedCount = 0;
 
 EventListenerManager::EventListenerManager(EventTarget* aTarget)
   : mMayHavePaintEventListener(false)
   , mMayHaveMutationListeners(false)
   , mMayHaveCapturingListeners(false)
   , mMayHaveSystemGroupListeners(false)
   , mMayHaveTouchEventListener(false)
+  , mMayHaveScrollWheelEventListener(false)
   , mMayHaveMouseEnterLeaveEventListener(false)
   , mMayHavePointerEnterLeaveEventListener(false)
   , mClearingListeners(false)
   , mIsMainThreadELM(NS_IsMainThread())
   , mNoListenerForEvent(0)
   , mTarget(aTarget)
 {
   NS_ASSERTION(aTarget, "unexpected null pointer");
@@ -333,16 +335,26 @@ EventListenerManager::AddEventListenerIn
              aTypeAtom == nsGkAtoms::ontouchcancel) {
     mMayHaveTouchEventListener = true;
     nsPIDOMWindow* window = GetInnerWindowForTarget();
     // we don't want touchevent listeners added by scrollbars to flip this flag
     // so we ignore listeners created with system event flag
     if (window && !aFlags.mInSystemGroup) {
       window->SetHasTouchEventListeners();
     }
+  } else if (aTypeAtom == nsGkAtoms::onwheel ||
+             aTypeAtom == nsGkAtoms::onDOMMouseScroll ||
+             aTypeAtom == nsHtml5Atoms::onmousewheel) {
+    mMayHaveScrollWheelEventListener = true;
+    nsPIDOMWindow* window = GetInnerWindowForTarget();
+    // we don't want touchevent listeners added by scrollbars to flip this flag
+    // so we ignore listeners created with system event flag
+    if (window && !aFlags.mInSystemGroup) {
+      window->SetHasScrollWheelEventListeners();
+    }
   } else if (aType >= NS_POINTER_EVENT_START && aType <= NS_POINTER_LOST_CAPTURE) {
     nsPIDOMWindow* window = GetInnerWindowForTarget();
     if (aTypeAtom == nsGkAtoms::onpointerenter ||
         aTypeAtom == nsGkAtoms::onpointerleave) {
       mMayHavePointerEnterLeaveEventListener = true;
       if (window) {
 #ifdef DEBUG
         nsCOMPtr<nsIDocument> d = window->GetExtantDoc();
--- a/dom/events/EventListenerManager.h
+++ b/dom/events/EventListenerManager.h
@@ -389,16 +389,22 @@ public:
   bool MayHavePaintEventListener() { return mMayHavePaintEventListener; }
 
   /**
    * Returns true if there may be a touch event listener registered,
    * false if there definitely isn't.
    */
   bool MayHaveTouchEventListener() { return mMayHaveTouchEventListener; }
 
+  /**
+   * Returns true if there may be a scroll wheel listener registered,
+   * false if there definitely isn't.
+   */
+  bool MayHaveScrollWheelEventListener() { return mMayHaveScrollWheelEventListener; }
+
   bool MayHaveMouseEnterLeaveEventListener() { return mMayHaveMouseEnterLeaveEventListener; }
   bool MayHavePointerEnterLeaveEventListener() { return mMayHavePointerEnterLeaveEventListener; }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
   uint32_t ListenerCount() const
   {
     return mListeners.Length();
@@ -539,16 +545,17 @@ protected:
   already_AddRefed<nsIScriptGlobalObject>
   GetScriptGlobalAndDocument(nsIDocument** aDoc);
 
   uint32_t mMayHavePaintEventListener : 1;
   uint32_t mMayHaveMutationListeners : 1;
   uint32_t mMayHaveCapturingListeners : 1;
   uint32_t mMayHaveSystemGroupListeners : 1;
   uint32_t mMayHaveTouchEventListener : 1;
+  uint32_t mMayHaveScrollWheelEventListener : 1;
   uint32_t mMayHaveMouseEnterLeaveEventListener : 1;
   uint32_t mMayHavePointerEnterLeaveEventListener : 1;
   uint32_t mClearingListeners : 1;
   uint32_t mIsMainThreadELM : 1;
   uint32_t mNoListenerForEvent : 23;
 
   nsAutoTObserverArray<Listener, 2> mListeners;
   dom::EventTarget* mTarget;  // WEAK
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -525,17 +525,17 @@ public:
   {
     mLineScrollAmount = size;
   }
 
 private:
   // New fields from now on should be made private and old fields should
   // be refactored to be private.
 
-  // Whether or not this frame may have a touch listeners.
+  // Whether or not this frame may have touch or scroll wheel listeners.
   bool mMayHaveTouchListeners;
 
   // Whether or not this frame may have a touch caret.
   bool mMayHaveTouchCaret;
 
   // Whether or not this is the root scroll frame for the root content document.
   bool mIsRoot;
 
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -609,17 +609,17 @@ APZCTreeManager::ReceiveInputEvent(Input
         MOZ_ASSERT(hitResult == ApzcHitRegion || hitResult == ApzcContentRegion);
 
         transformToApzc = GetScreenToApzcTransform(apzc);
         wheelInput.mLocalOrigin =
           TransformTo<ParentLayerPixel>(transformToApzc, wheelInput.mOrigin);
 
         result = mInputQueue->ReceiveInputEvent(
           apzc,
-          /* aTargetConfirmed = */ hitResult,
+          /* aTargetConfirmed = */ hitResult == ApzcHitRegion,
           wheelInput, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
         Matrix4x4 transformToGecko = transformToApzc * GetApzcToGeckoTransform(apzc);
         wheelInput.mOrigin =
           TransformTo<ScreenPixel>(transformToGecko, wheelInput.mLocalOrigin);
       }
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/InputBlockState.h.orig
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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 mozilla_layers_InputBlockState_h
+#define mozilla_layers_InputBlockState_h
+
+#include "nsTArray.h"                       // for nsTArray
+#include "InputData.h"                      // for MultiTouchInput
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+namespace layers {
+
+class AsyncPanZoomController;
+class OverscrollHandoffChain;
+class CancelableBlockState;
+class TouchBlockState;
+class WheelBlockState;
+
+/**
+ * A base class that stores state common to various input blocks.
+ * Currently, it just stores the overscroll handoff chain.
+ */
+class InputBlockState
+{
+public:
+  static const uint64_t NO_BLOCK_ID = 0;
+
+  explicit InputBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
+                           bool aTargetConfirmed);
+  virtual ~InputBlockState()
+  {}
+
+  bool SetConfirmedTargetApzc(const nsRefPtr<AsyncPanZoomController>& aTargetApzc);
+  const nsRefPtr<AsyncPanZoomController>& GetTargetApzc() const;
+  const nsRefPtr<const OverscrollHandoffChain>& GetOverscrollHandoffChain() const;
+  uint64_t GetBlockId() const;
+
+  bool IsTargetConfirmed() const;
+
+private:
+  nsRefPtr<AsyncPanZoomController> mTargetApzc;
+  nsRefPtr<const OverscrollHandoffChain> mOverscrollHandoffChain;
+  bool mTargetConfirmed;
+  const uint64_t mBlockId;
+};
+
+/**
+ * This class represents a set of events that can be cancelled by web content
+ * via event listeners.
+ *
+ * Each cancelable input block can be cancelled by web content, and
+ * this information is stored in the mPreventDefault flag. Because web
+ * content runs on the Gecko main thread, we cannot always wait for web content's
+ * response. Instead, there is a timeout that sets this flag in the case
+ * where web content doesn't respond in time. The mContentResponded
+ * and mContentResponseTimerExpired flags indicate which of these scenarios
+ * occurred.
+ */
+class CancelableBlockState : public InputBlockState
+{
+public:
+  CancelableBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
+                       bool aTargetConfirmed);
+
+  virtual TouchBlockState *AsTouchBlock() {
+    return nullptr;
+  }
+
+  /**
+   * Record whether or not content cancelled this block of events.
+   * @param aPreventDefault true iff the block is cancelled.
+   * @return false if this block has already received a response from
+   *         web content, true if not.
+   */
+  bool SetContentResponse(bool aPreventDefault);
+
+  /**
+   * Record that content didn't respond in time.
+   * @return false if this block already timed out, true if not.
+   */
+  bool TimeoutContentResponse();
+
+  /**
+   * @return true iff web content cancelled this block of events.
+   */
+  bool IsDefaultPrevented() const;
+
+  /**
+   * @return true iff this block has received all the information needed
+   *         to properly dispatch the events in the block.
+   */
+  virtual bool IsReadyForHandling() const;
+
+<<<<<<< HEAD
+private:
+=======
+  /**
+   * Returns whether or not this block has pending events.
+   */
+  virtual bool HasEvents() const = 0;
+
+  /**
+   * Throw away all the events in this input block.
+   */
+  virtual void DropEvents() = 0;
+
+  /**
+   * Process all events given an apzc, leaving ths block depleted.
+   */
+  virtual void HandleEvents(const nsRefPtr<AsyncPanZoomController>& aTarget) = 0;
+
+  /**
+   * Return true if this input block must stay active if it would otherwise
+   * be removed as the last item in the pending queue.
+   */
+  virtual bool MustStayActive() = 0;
+
+  /**
+   * Return a descriptive name for the block kind.
+   */
+  virtual const char* Type() = 0;
+
+ private:
+>>>>>>> 80325be... Refactor InputQueue to hold more than touch events. (bug 1013432 part 2, r=kats)
+  bool mPreventDefault;
+  bool mContentResponded;
+  bool mContentResponseTimerExpired;
+};
+
+/**
+ * This class represents a single touch block. A touch block is
+ * a set of touch events that can be cancelled by web content via
+ * touch event listeners.
+ *
+ * Every touch-start event creates a new touch block. In this case, the
+ * touch block consists of the touch-start, followed by all touch events
+ * up to but not including the next touch-start (except in the case where
+ * a long-tap happens, see below). Note that in particular we cannot know
+ * when a touch block ends until the next one is started. Most touch
+ * blocks are created by receipt of a touch-start event.
+ *
+ * Every long-tap event also creates a new touch block, since it can also
+ * be consumed by web content. In this case, when the long-tap event is
+ * dispatched to web content, a new touch block is started to hold the remaining
+ * touch events, up to but not including the next touch start (or long-tap).
+ *
+ * Additionally, if touch-action is enabled, each touch block should
+ * have a set of allowed touch behavior flags; one for each touch point.
+ * This also requires running code on the Gecko main thread, and so may
+ * be populated with some latency. The mAllowedTouchBehaviorSet and
+ * mAllowedTouchBehaviors variables track this information.
+ */
+class TouchBlockState : public CancelableBlockState
+{
+public:
+  typedef uint32_t TouchBehaviorFlags;
+
+  explicit TouchBlockState(const nsRefPtr<AsyncPanZoomController>& aTargetApzc,
+                           bool aTargetConfirmed);
+
+  TouchBlockState *AsTouchBlock() MOZ_OVERRIDE {
+    return this;
+  }
+
+  /**
+   * Set the allowed touch behavior flags for this block.
+   * @return false if this block already has these flags set, true if not.
+   */
+  bool SetAllowedTouchBehaviors(const nsTArray<TouchBehaviorFlags>& aBehaviors);
+  /**
+   * Copy the allowed touch behavior flags from another block.
+   * @return false if this block already has these flags set, true if not.
+   */
+  bool CopyAllowedTouchBehaviorsFrom(const TouchBlockState& aOther);
+
+  /**
+   * @return true iff this block has received all the information needed
+   *         to properly dispatch the events in the block.
+   */
+  bool IsReadyForHandling() const MOZ_OVERRIDE;
+
+  /**
+   * Set a flag that disables setting the single-tap flag on this block.
+   */
+  void DisallowSingleTap();
+  /**
+   * Set a flag that indicates that this touch block triggered a single tap event.
+   * @return true iff DisallowSingleTap was not previously called.
+   */
+  bool SetSingleTapOccurred();
+  /**
+   * @return true iff SetSingleTapOccurred was previously called on this block.
+   */
+  bool SingleTapOccurred() const;
+
+  /**
+   * Add a new touch event to the queue of events in this input block.
+   */
+  void AddEvent(const MultiTouchInput& aEvent);
+
+  /**
+   * @return false iff touch-action is enabled and the allowed touch behaviors for
+   *         this touch block do not allow pinch-zooming.
+   */
+  bool TouchActionAllowsPinchZoom() const;
+  /**
+   * @return false iff touch-action is enabled and the allowed touch behaviors for
+   *         this touch block do not allow double-tap zooming.
+   */
+  bool TouchActionAllowsDoubleTapZoom() const;
+  /**
+   * @return false iff touch-action is enabled and the allowed touch behaviors for
+   *         the first touch point do not allow panning in the specified direction(s).
+   */
+  bool TouchActionAllowsPanningX() const;
+  bool TouchActionAllowsPanningY() const;
+  bool TouchActionAllowsPanningXY() const;
+
+  bool HasEvents() const MOZ_OVERRIDE;
+  void DropEvents() MOZ_OVERRIDE;
+  void HandleEvents(const nsRefPtr<AsyncPanZoomController>& aTarget) MOZ_OVERRIDE;
+  bool MustStayActive() MOZ_OVERRIDE;
+  const char* Type() MOZ_OVERRIDE;
+
+private:
+  /**
+   * @return the first event in the queue. The event is removed from the queue
+   *         before it is returned.
+   */
+  MultiTouchInput RemoveFirstEvent();
+
+private:
+  nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
+  bool mAllowedTouchBehaviorSet;
+  bool mSingleTapDisallowed;
+  bool mSingleTapOccurred;
+  nsTArray<MultiTouchInput> mEvents;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_InputBlockState_h
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -553,16 +553,17 @@ nsDisplayListBuilder::nsDisplayListBuild
       mAllowMergingAndFlattening(true),
       mWillComputePluginGeometry(false),
       mInTransform(false),
       mSyncDecodeImages(false),
       mIsPaintingToWindow(false),
       mIsCompositingCheap(false),
       mContainsPluginItem(false),
       mAncestorHasTouchEventHandler(false),
+      mAncestorHasScrollEventHandler(false),
       mHaveScrollableDisplayPort(false)
 {
   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();
@@ -767,17 +768,17 @@ nsDisplayScrollLayer::ComputeFrameMetric
                   * layerToParentLayerScale);
 
   if (presShell) {
     nsIDocument* document = nullptr;
     document = presShell->GetDocument();
     if (document) {
       nsCOMPtr<nsPIDOMWindow> innerWin(document->GetInnerWindow());
       if (innerWin) {
-        metrics.SetMayHaveTouchListeners(innerWin->HasTouchEventListeners());
+        metrics.SetMayHaveTouchListeners(innerWin->HasApzAwareEventListeners());
       }
     }
     metrics.SetMayHaveTouchCaret(presShell->MayHaveTouchCaret());
   }
 
   // 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,
@@ -2977,17 +2978,19 @@ nsDisplayLayerEventRegions::AddFrame(nsD
     }
   }
   if (borderBoxHasRoundedCorners ||
       (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
     mMaybeHitRegion.Or(mMaybeHitRegion, borderBox);
   } else {
     mHitRegion.Or(mHitRegion, borderBox);
   }
-  if (aBuilder->GetAncestorHasTouchEventHandler()) {
+  if (aBuilder->GetAncestorHasTouchEventHandler() ||
+      aBuilder->GetAncestorHasScrollEventHandler())
+  {
     mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox);
   }
 }
 
 void
 nsDisplayLayerEventRegions::AddInactiveScrollPort(const nsRect& aRect)
 {
   mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aRect);
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -346,16 +346,21 @@ public:
     return (gfxPrefs::LayoutEventRegionsEnabled() && mMode == PAINTING);
   }
 
   bool GetAncestorHasTouchEventHandler() { return mAncestorHasTouchEventHandler; }
   void SetAncestorHasTouchEventHandler(bool aValue)
   {
     mAncestorHasTouchEventHandler = aValue;
   }
+  bool GetAncestorHasScrollEventHandler() { return mAncestorHasScrollEventHandler; }
+  void SetAncestorHasScrollEventHandler(bool aValue)
+  {
+    mAncestorHasScrollEventHandler = aValue;
+  }
 
   bool HaveScrollableDisplayPort() const { return mHaveScrollableDisplayPort; }
   void SetHaveScrollableDisplayPort() { mHaveScrollableDisplayPort = true; }
 
   bool SetIsCompositingCheap(bool aCompositingCheap) { 
     bool temp = mIsCompositingCheap; 
     mIsCompositingCheap = aCompositingCheap;
     return temp;
@@ -556,17 +561,18 @@ public:
       : mBuilder(aBuilder),
         mPrevFrame(aBuilder->mCurrentFrame),
         mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
         mPrevAnimatedGeometryRoot(mBuilder->mCurrentAnimatedGeometryRoot),
         mPrevLayerEventRegions(aBuilder->mLayerEventRegions),
         mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
         mPrevDirtyRect(aBuilder->mDirtyRect),
         mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext),
-        mPrevAncestorHasTouchEventHandler(aBuilder->mAncestorHasTouchEventHandler)
+        mPrevAncestorHasTouchEventHandler(aBuilder->mAncestorHasTouchEventHandler),
+        mPrevAncestorHasScrollEventHandler(aBuilder->mAncestorHasScrollEventHandler)
     {
       if (aForChild->IsTransformed()) {
         aBuilder->mCurrentOffsetToReferenceFrame = nsPoint();
         aBuilder->mCurrentReferenceFrame = aForChild;
       } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
         aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
       } else {
         aBuilder->mCurrentReferenceFrame =
@@ -602,28 +608,30 @@ public:
     ~AutoBuildingDisplayList() {
       mBuilder->mCurrentFrame = mPrevFrame;
       mBuilder->mCurrentReferenceFrame = mPrevReferenceFrame;
       mBuilder->mLayerEventRegions = mPrevLayerEventRegions;
       mBuilder->mCurrentOffsetToReferenceFrame = mPrevOffset;
       mBuilder->mDirtyRect = mPrevDirtyRect;
       mBuilder->mIsAtRootOfPseudoStackingContext = mPrevIsAtRootOfPseudoStackingContext;
       mBuilder->mAncestorHasTouchEventHandler = mPrevAncestorHasTouchEventHandler;
+      mBuilder->mAncestorHasScrollEventHandler = mPrevAncestorHasScrollEventHandler;
       mBuilder->mCurrentAnimatedGeometryRoot = mPrevAnimatedGeometryRoot;
     }
   private:
     nsDisplayListBuilder* mBuilder;
     const nsIFrame*       mPrevFrame;
     const nsIFrame*       mPrevReferenceFrame;
     nsIFrame*             mPrevAnimatedGeometryRoot;
     nsDisplayLayerEventRegions* mPrevLayerEventRegions;
     nsPoint               mPrevOffset;
     nsRect                mPrevDirtyRect;
     bool                  mPrevIsAtRootOfPseudoStackingContext;
     bool                  mPrevAncestorHasTouchEventHandler;
+    bool                  mPrevAncestorHasScrollEventHandler;
   };
 
   /**
    * A helper class to temporarily set the value of mInTransform.
    */
   class AutoInTransformSetter;
   friend class AutoInTransformSetter;
   class AutoInTransformSetter {
@@ -863,16 +871,17 @@ private:
   // True when we're building a display list that's directly or indirectly
   // under an nsDisplayTransform
   bool                           mInTransform;
   bool                           mSyncDecodeImages;
   bool                           mIsPaintingToWindow;
   bool                           mIsCompositingCheap;
   bool                           mContainsPluginItem;
   bool                           mAncestorHasTouchEventHandler;
+  bool                           mAncestorHasScrollEventHandler;
   // 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;
 };
 
 class nsDisplayItem;
 class nsDisplayList;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -39,16 +39,17 @@
 #include "nsLayoutUtils.h"
 #include "RestyleManager.h"
 
 #include "nsIDOMNode.h"
 #include "nsISelection.h"
 #include "nsISelectionPrivate.h"
 #include "nsFrameSelection.h"
 #include "nsGkAtoms.h"
+#include "nsHtml5Atoms.h"
 #include "nsCSSAnonBoxes.h"
 
 #include "nsFrameTraversal.h"
 #include "nsRange.h"
 #include "nsITextControlFrame.h"
 #include "nsNameSpaceManager.h"
 #include "nsIPercentHeightObserver.h"
 #include "nsStyleStructInlines.h"
@@ -1905,30 +1906,36 @@ public:
   { }
 
   ~AutoSaveRestoreBlendMode() {
     mBuilder.SetContainsBlendModes(mSavedBlendModes);
   }
 };
 
 static void
-CheckForTouchEventHandler(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
+CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
 {
   nsIContent* content = aFrame->GetContent();
   if (!content) {
     return;
   }
   EventListenerManager* elm = nsContentUtils::GetExistingListenerManagerForNode(content);
   if (!elm) {
     return;
   }
   if (elm->HasListenersFor(nsGkAtoms::ontouchstart) ||
       elm->HasListenersFor(nsGkAtoms::ontouchmove)) {
     aBuilder->SetAncestorHasTouchEventHandler(true);
   }
+  if (elm->HasListenersFor(nsGkAtoms::onwheel) ||
+      elm->HasListenersFor(nsGkAtoms::onDOMMouseScroll) ||
+      elm->HasListenersFor(nsHtml5Atoms::onmousewheel))
+  {
+    aBuilder->SetAncestorHasScrollEventHandler(true);
+  }
 }
 
 void
 nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
                                              const nsRect&         aDirtyRect,
                                              nsDisplayList*        aList) {
   if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
     return;
@@ -2022,17 +2029,17 @@ nsIFrame::BuildDisplayListForStackingCon
     clipState.Clear();
   }
 
   nsDisplayListCollection set;
   {
     DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
     nsDisplayListBuilder::AutoInTransformSetter
       inTransformSetter(aBuilder, inTransform);
-    CheckForTouchEventHandler(aBuilder, this);
+    CheckForApzAwareEventHandlers(aBuilder, this);
 
     nsRect clipPropClip;
     if (ApplyClipPropClipping(aBuilder, this, disp, &clipPropClip,
                               nestedClipState)) {
       dirtyRect.IntersectRect(dirtyRect, clipPropClip);
     }
 
     MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect);
@@ -2369,17 +2376,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
     pseudoStackingContext = true;
   }
   NS_ASSERTION(!isStackingContext || pseudoStackingContext,
                "Stacking contexts must also be pseudo-stacking-contexts");
 
   nsDisplayListBuilder::AutoBuildingDisplayList
     buildingForChild(aBuilder, child, dirty, pseudoStackingContext);
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
-  CheckForTouchEventHandler(aBuilder, child);
+  CheckForApzAwareEventHandlers(aBuilder, child);
 
   if (savedOutOfFlowData) {
     clipState.SetClipForContainingBlockDescendants(
       &savedOutOfFlowData->mContainingBlockClip);
   }
 
   // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
   // or overflow:hidden on elements that don't support scrolling (and therefore
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -473,16 +473,17 @@ nsSubDocumentFrame::BuildDisplayList(nsD
       nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
       nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
           aBuilder,
           ignoreViewportScrolling && rootScrollFrame && rootScrollFrame->GetContent()
               ? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent())
               : aBuilder->GetCurrentScrollParentId());
 
       aBuilder->SetAncestorHasTouchEventHandler(false);
+      aBuilder->SetAncestorHasScrollEventHandler(false);
       subdocRootFrame->
         BuildDisplayListForStackingContext(aBuilder, dirty, &childItems);
     }
 
     if (!aBuilder->IsForEventDelivery()) {
       // If we are going to use a displayzoom below then any items we put under
       // it need to have underlying frames from the subdocument. So we need to
       // calculate the bounds based on which frame will be the underlying frame