Add scroll listeners to dispatch-to-content regions. (bug 1013432 part 5, r=tn)

--HG--
extra : rebase_source : babd42d9bdae722173821e800bc595fe662ee8f1
This commit is contained in:
David Anderson 2014-12-09 02:38:23 -08:00
parent 1c62410221
commit fae21c73e9
12 changed files with 324 additions and 9 deletions

View File

@ -566,7 +566,9 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
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),

View File

@ -410,6 +410,9 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
if (elm->MayHaveTouchEventListener()) {
window->SetHasTouchEventListeners();
}
if (elm->MayHaveScrollWheelEventListener()) {
window->SetHasScrollWheelEventListeners();
}
if (elm->MayHaveMouseEnterLeaveEventListener()) {
window->SetHasMouseEnterLeaveEventListeners();
}

View File

@ -443,6 +443,29 @@ public:
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
@ -791,6 +814,7 @@ protected:
bool mMayHavePaintEventListener;
bool mMayHaveTouchEventListener;
bool mMayHaveTouchCaret;
bool mMayHaveScrollWheelEventListener;
bool mMayHaveMouseEnterLeaveEventListener;
bool mMayHavePointerEnterLeaveEventListener;

View File

@ -31,6 +31,7 @@
#include "nsDOMCID.h"
#include "nsError.h"
#include "nsGkAtoms.h"
#include "nsHtml5Atoms.h"
#include "nsIContent.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIDocument.h"
@ -97,6 +98,7 @@ EventListenerManager::EventListenerManager(EventTarget* aTarget)
, mMayHaveCapturingListeners(false)
, mMayHaveSystemGroupListeners(false)
, mMayHaveTouchEventListener(false)
, mMayHaveScrollWheelEventListener(false)
, mMayHaveMouseEnterLeaveEventListener(false)
, mMayHavePointerEnterLeaveEventListener(false)
, mClearingListeners(false)
@ -338,6 +340,16 @@ EventListenerManager::AddEventListenerInternal(
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 ||

View File

@ -394,6 +394,12 @@ public:
*/
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; }
@ -544,6 +550,7 @@ protected:
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;

View File

@ -530,7 +530,7 @@ 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.

View File

@ -614,7 +614,7 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent,
result = mInputQueue->ReceiveInputEvent(
apzc,
/* aTargetConfirmed = */ hitResult,
/* aTargetConfirmed = */ hitResult == ApzcHitRegion,
wheelInput, aOutInputBlockId);
// Update the out-parameters so they are what the caller expects.

View File

@ -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

View File

@ -558,6 +558,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mIsCompositingCheap(false),
mContainsPluginItem(false),
mAncestorHasTouchEventHandler(false),
mAncestorHasScrollEventHandler(false),
mHaveScrollableDisplayPort(false)
{
MOZ_COUNT_CTOR(nsDisplayListBuilder);
@ -772,7 +773,7 @@ nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame,
if (document) {
nsCOMPtr<nsPIDOMWindow> innerWin(document->GetInnerWindow());
if (innerWin) {
metrics.SetMayHaveTouchListeners(innerWin->HasTouchEventListeners());
metrics.SetMayHaveTouchListeners(innerWin->HasApzAwareEventListeners());
}
}
metrics.SetMayHaveTouchCaret(presShell->MayHaveTouchCaret());
@ -2982,7 +2983,9 @@ nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
} else {
mHitRegion.Or(mHitRegion, borderBox);
}
if (aBuilder->GetAncestorHasTouchEventHandler()) {
if (aBuilder->GetAncestorHasTouchEventHandler() ||
aBuilder->GetAncestorHasScrollEventHandler())
{
mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox);
}
}

View File

@ -351,6 +351,11 @@ public:
{
mAncestorHasTouchEventHandler = aValue;
}
bool GetAncestorHasScrollEventHandler() { return mAncestorHasScrollEventHandler; }
void SetAncestorHasScrollEventHandler(bool aValue)
{
mAncestorHasScrollEventHandler = aValue;
}
bool HaveScrollableDisplayPort() const { return mHaveScrollableDisplayPort; }
void SetHaveScrollableDisplayPort() { mHaveScrollableDisplayPort = true; }
@ -561,7 +566,8 @@ public:
mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
mPrevDirtyRect(aBuilder->mDirtyRect),
mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext),
mPrevAncestorHasTouchEventHandler(aBuilder->mAncestorHasTouchEventHandler)
mPrevAncestorHasTouchEventHandler(aBuilder->mAncestorHasTouchEventHandler),
mPrevAncestorHasScrollEventHandler(aBuilder->mAncestorHasScrollEventHandler)
{
if (aForChild->IsTransformed()) {
aBuilder->mCurrentOffsetToReferenceFrame = nsPoint();
@ -607,6 +613,7 @@ public:
mBuilder->mDirtyRect = mPrevDirtyRect;
mBuilder->mIsAtRootOfPseudoStackingContext = mPrevIsAtRootOfPseudoStackingContext;
mBuilder->mAncestorHasTouchEventHandler = mPrevAncestorHasTouchEventHandler;
mBuilder->mAncestorHasScrollEventHandler = mPrevAncestorHasScrollEventHandler;
mBuilder->mCurrentAnimatedGeometryRoot = mPrevAnimatedGeometryRoot;
}
private:
@ -619,6 +626,7 @@ public:
nsRect mPrevDirtyRect;
bool mPrevIsAtRootOfPseudoStackingContext;
bool mPrevAncestorHasTouchEventHandler;
bool mPrevAncestorHasScrollEventHandler;
};
/**
@ -868,6 +876,7 @@ private:
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().

View File

@ -44,6 +44,7 @@
#include "nsISelectionPrivate.h"
#include "nsFrameSelection.h"
#include "nsGkAtoms.h"
#include "nsHtml5Atoms.h"
#include "nsCSSAnonBoxes.h"
#include "nsFrameTraversal.h"
@ -1910,7 +1911,7 @@ public:
};
static void
CheckForTouchEventHandler(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
{
nsIContent* content = aFrame->GetContent();
if (!content) {
@ -1924,6 +1925,12 @@ CheckForTouchEventHandler(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
elm->HasListenersFor(nsGkAtoms::ontouchmove)) {
aBuilder->SetAncestorHasTouchEventHandler(true);
}
if (elm->HasListenersFor(nsGkAtoms::onwheel) ||
elm->HasListenersFor(nsGkAtoms::onDOMMouseScroll) ||
elm->HasListenersFor(nsHtml5Atoms::onmousewheel))
{
aBuilder->SetAncestorHasScrollEventHandler(true);
}
}
void
@ -2027,7 +2034,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
nsDisplayListBuilder::AutoInTransformSetter
inTransformSetter(aBuilder, inTransform);
CheckForTouchEventHandler(aBuilder, this);
CheckForApzAwareEventHandlers(aBuilder, this);
nsRect clipPropClip;
if (ApplyClipPropClipping(aBuilder, this, disp, &clipPropClip,
@ -2374,7 +2381,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
nsDisplayListBuilder::AutoBuildingDisplayList
buildingForChild(aBuilder, child, dirty, pseudoStackingContext);
DisplayListClipState::AutoClipMultiple clipState(aBuilder);
CheckForTouchEventHandler(aBuilder, child);
CheckForApzAwareEventHandlers(aBuilder, child);
if (savedOutOfFlowData) {
clipState.SetClipForContainingBlockDescendants(

View File

@ -478,6 +478,7 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
: aBuilder->GetCurrentScrollParentId());
aBuilder->SetAncestorHasTouchEventHandler(false);
aBuilder->SetAncestorHasScrollEventHandler(false);
subdocRootFrame->
BuildDisplayListForStackingContext(aBuilder, dirty, &childItems);
}