Bug 1678505 - Add overscroll-behavior and scrollable directions into APZHandledResult. r=botond
Differential Revision: https://phabricator.services.mozilla.com/D103419
This commit is contained in:
@@ -387,12 +387,12 @@ class CompositableHandle final {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
MOZ_DEFINE_ENUM_CLASS_WITH_BASE(ScrollDirection, uint32_t, (
|
MOZ_DEFINE_ENUM_CLASS_WITH_BASE(ScrollDirection, uint8_t, (
|
||||||
eVertical,
|
eVertical,
|
||||||
eHorizontal
|
eHorizontal
|
||||||
));
|
));
|
||||||
|
|
||||||
typedef EnumSet<ScrollDirection> ScrollDirections;
|
using ScrollDirections = EnumSet<ScrollDirection, uint8_t>;
|
||||||
|
|
||||||
constexpr ScrollDirections EitherScrollDirection(ScrollDirection::eVertical,ScrollDirection::eHorizontal);
|
constexpr ScrollDirections EitherScrollDirection(ScrollDirection::eVertical,ScrollDirection::eHorizontal);
|
||||||
constexpr ScrollDirections HorizontalScrollDirection(ScrollDirection::eHorizontal);
|
constexpr ScrollDirections HorizontalScrollDirection(ScrollDirection::eHorizontal);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "mozilla/EventForwards.h" // for WidgetInputEvent, nsEventStatus
|
#include "mozilla/EventForwards.h" // for WidgetInputEvent, nsEventStatus
|
||||||
#include "mozilla/layers/APZPublicUtils.h" // for APZWheelAction
|
#include "mozilla/layers/APZPublicUtils.h" // for APZWheelAction
|
||||||
|
#include "mozilla/layers/LayersTypes.h" // for ScrollDirections
|
||||||
#include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid
|
#include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid
|
||||||
#include "Units.h" // for LayoutDeviceIntPoint
|
#include "Units.h" // for LayoutDeviceIntPoint
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ class InputBlockState;
|
|||||||
struct ScrollableLayerGuid;
|
struct ScrollableLayerGuid;
|
||||||
struct TargetConfirmationFlags;
|
struct TargetConfirmationFlags;
|
||||||
|
|
||||||
enum class APZHandledResult : uint8_t {
|
enum class APZHandledPlace : uint8_t {
|
||||||
Unhandled = 0, // we know for sure that the event will not be handled
|
Unhandled = 0, // we know for sure that the event will not be handled
|
||||||
// by either the root APZC or others
|
// by either the root APZC or others
|
||||||
HandledByRoot = 1, // we know for sure that the event will be handled
|
HandledByRoot = 1, // we know for sure that the event will be handled
|
||||||
@@ -36,6 +37,33 @@ enum class APZHandledResult : uint8_t {
|
|||||||
Last = Invalid
|
Last = Invalid
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct APZHandledResult {
|
||||||
|
APZHandledPlace mPlace = APZHandledPlace::Invalid;
|
||||||
|
SideBits mScrollableDirections = SideBits::eNone;
|
||||||
|
ScrollDirections mOverscrollDirections = ScrollDirections();
|
||||||
|
|
||||||
|
APZHandledResult() = default;
|
||||||
|
APZHandledResult(APZHandledPlace aPlace,
|
||||||
|
const AsyncPanZoomController* aTarget);
|
||||||
|
APZHandledResult(APZHandledPlace aPlace, SideBits aScrollableDirections,
|
||||||
|
ScrollDirections aOverscrollDirections)
|
||||||
|
: mPlace(aPlace),
|
||||||
|
mScrollableDirections(aScrollableDirections),
|
||||||
|
mOverscrollDirections(aOverscrollDirections) {}
|
||||||
|
|
||||||
|
bool IsHandledByContent() const {
|
||||||
|
return mPlace == APZHandledPlace::HandledByContent;
|
||||||
|
}
|
||||||
|
bool IsHandledByRoot() const {
|
||||||
|
return mPlace == APZHandledPlace::HandledByRoot;
|
||||||
|
}
|
||||||
|
bool operator==(const APZHandledResult& aOther) const {
|
||||||
|
return mPlace == aOther.mPlace &&
|
||||||
|
mScrollableDirections == aOther.mScrollableDirections &&
|
||||||
|
mOverscrollDirections == aOther.mOverscrollDirections;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the outcome of APZ receiving and processing an input event.
|
* Represents the outcome of APZ receiving and processing an input event.
|
||||||
* This is returned from APZInputBridge::ReceiveInputEvent() and related APIs.
|
* This is returned from APZInputBridge::ReceiveInputEvent() and related APIs.
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ enum ZoomToRectBehavior : uint32_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class AsyncDragMetrics;
|
class AsyncDragMetrics;
|
||||||
enum class APZHandledResult : uint8_t;
|
struct APZHandledResult;
|
||||||
|
|
||||||
class IAPZCTreeManager {
|
class IAPZCTreeManager {
|
||||||
NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(IAPZCTreeManager)
|
NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(IAPZCTreeManager)
|
||||||
@@ -149,7 +149,7 @@ class IAPZCTreeManager {
|
|||||||
* the APZCTreeManager.
|
* the APZCTreeManager.
|
||||||
*/
|
*/
|
||||||
using InputBlockCallback = std::function<void(
|
using InputBlockCallback = std::function<void(
|
||||||
uint64_t aInputBlockId, APZHandledResult aHandledResult)>;
|
uint64_t aInputBlockId, const APZHandledResult& aHandledResult)>;
|
||||||
virtual void AddInputBlockCallback(uint64_t aInputBlockId,
|
virtual void AddInputBlockCallback(uint64_t aInputBlockId,
|
||||||
InputBlockCallback&& aCallback) = 0;
|
InputBlockCallback&& aCallback) = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -38,13 +38,15 @@ APZEventResult::APZEventResult(
|
|||||||
// If the initial target is not the root, this will definitely not be
|
// If the initial target is not the root, this will definitely not be
|
||||||
// handled by the root. (The confirmed target is either the initial
|
// handled by the root. (The confirmed target is either the initial
|
||||||
// target, or a descendant.)
|
// target, or a descendant.)
|
||||||
return Some(APZHandledResult::HandledByContent);
|
return Some(
|
||||||
|
APZHandledResult{APZHandledPlace::HandledByContent, aInitialTarget});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aFlags.mDispatchToContent) {
|
if (!aFlags.mDispatchToContent) {
|
||||||
// If the initial target is the root and we don't need to dispatch to
|
// If the initial target is the root and we don't need to dispatch to
|
||||||
// content, the event will definitely be handled by the root.
|
// content, the event will definitely be handled by the root.
|
||||||
return Some(APZHandledResult::HandledByRoot);
|
return Some(
|
||||||
|
APZHandledResult{APZHandledPlace::HandledByRoot, aInitialTarget});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we're not sure.
|
// Otherwise, we're not sure.
|
||||||
@@ -62,8 +64,9 @@ void APZEventResult::SetStatusAsConsumeDoDefault(
|
|||||||
const RefPtr<AsyncPanZoomController>& aTarget) {
|
const RefPtr<AsyncPanZoomController>& aTarget) {
|
||||||
mStatus = nsEventStatus_eConsumeDoDefault;
|
mStatus = nsEventStatus_eConsumeDoDefault;
|
||||||
mHandledResult =
|
mHandledResult =
|
||||||
Some(aTarget->IsRootContent() ? APZHandledResult::HandledByRoot
|
Some(aTarget && aTarget->IsRootContent()
|
||||||
: APZHandledResult::HandledByContent);
|
? APZHandledResult{APZHandledPlace::HandledByRoot, aTarget}
|
||||||
|
: APZHandledResult{APZHandledPlace::HandledByContent, aTarget});
|
||||||
}
|
}
|
||||||
|
|
||||||
void APZEventResult::SetStatusAsConsumeDoDefaultWithTargetConfirmationFlags(
|
void APZEventResult::SetStatusAsConsumeDoDefaultWithTargetConfirmationFlags(
|
||||||
@@ -84,9 +87,10 @@ void APZEventResult::SetStatusAsConsumeDoDefaultWithTargetConfirmationFlags(
|
|||||||
// mDispatchToContent, we need to change it to Nothing() so that
|
// mDispatchToContent, we need to change it to Nothing() so that
|
||||||
// GeckoView can properly wait for results from the content on the
|
// GeckoView can properly wait for results from the content on the
|
||||||
// main-thread.
|
// main-thread.
|
||||||
mHandledResult = aFlags.mDispatchToContent
|
mHandledResult =
|
||||||
|
aFlags.mDispatchToContent
|
||||||
? Nothing()
|
? Nothing()
|
||||||
: Some(APZHandledResult::HandledByRoot);
|
: Some(APZHandledResult{APZHandledPlace::HandledByRoot, &aTarget});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,21 +256,94 @@ APZEventResult APZInputBridge::ReceiveInputEvent(WidgetInputEvent& aEvent) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
APZHandledResult::APZHandledResult(APZHandledPlace aPlace,
|
||||||
|
const AsyncPanZoomController* aTarget)
|
||||||
|
: mPlace(aPlace) {
|
||||||
|
MOZ_ASSERT(aTarget);
|
||||||
|
switch (aPlace) {
|
||||||
|
case APZHandledPlace::Unhandled:
|
||||||
|
break;
|
||||||
|
case APZHandledPlace::HandledByContent:
|
||||||
|
if (aTarget) {
|
||||||
|
mScrollableDirections = aTarget->ScrollableDirections();
|
||||||
|
mOverscrollDirections = aTarget->GetAllowedHandoffDirections();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case APZHandledPlace::HandledByRoot: {
|
||||||
|
// The only way we can have mPlace == HandledByRoot but target is not the
|
||||||
|
// root, is if scroll is handed off immediately from target to the root
|
||||||
|
// because target and its ancestors (other than the root) do not have a
|
||||||
|
// scroll range. Therefore, it's the scroll directions of the root which
|
||||||
|
// are relevant.
|
||||||
|
const AsyncPanZoomController* target = aTarget;
|
||||||
|
while (target && !target->IsRootContent()) {
|
||||||
|
target = target->GetParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(target && target->IsRootContent());
|
||||||
|
if (target) {
|
||||||
|
mScrollableDirections = target->ScrollableDirections();
|
||||||
|
mOverscrollDirections = target->GetAllowedHandoffDirections();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
MOZ_ASSERT_UNREACHABLE("Invalid APZHandledPlace");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& aOut, const SideBits& aSideBits) {
|
||||||
|
if ((aSideBits & SideBits::eAll) == SideBits::eAll) {
|
||||||
|
aOut << "all";
|
||||||
|
} else {
|
||||||
|
AutoTArray<nsCString, 4> strings;
|
||||||
|
if (aSideBits & SideBits::eTop) {
|
||||||
|
strings.AppendElement("top"_ns);
|
||||||
|
}
|
||||||
|
if (aSideBits & SideBits::eRight) {
|
||||||
|
strings.AppendElement("right"_ns);
|
||||||
|
}
|
||||||
|
if (aSideBits & SideBits::eBottom) {
|
||||||
|
strings.AppendElement("bottom"_ns);
|
||||||
|
}
|
||||||
|
if (aSideBits & SideBits::eLeft) {
|
||||||
|
strings.AppendElement("left"_ns);
|
||||||
|
}
|
||||||
|
aOut << strings;
|
||||||
|
}
|
||||||
|
return aOut;
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& aOut,
|
std::ostream& operator<<(std::ostream& aOut,
|
||||||
const APZHandledResult& aHandledResult) {
|
const ScrollDirections& aScrollDirections) {
|
||||||
switch (aHandledResult) {
|
if (aScrollDirections.contains(EitherScrollDirection)) {
|
||||||
case APZHandledResult::Unhandled:
|
aOut << "either";
|
||||||
|
} else if (aScrollDirections.contains(HorizontalScrollDirection)) {
|
||||||
|
aOut << "horizontal";
|
||||||
|
} else if (aScrollDirections.contains(VerticalScrollDirection)) {
|
||||||
|
aOut << "vertical";
|
||||||
|
} else {
|
||||||
|
aOut << "none";
|
||||||
|
}
|
||||||
|
return aOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& aOut,
|
||||||
|
const APZHandledPlace& aHandledPlace) {
|
||||||
|
switch (aHandledPlace) {
|
||||||
|
case APZHandledPlace::Unhandled:
|
||||||
aOut << "unhandled";
|
aOut << "unhandled";
|
||||||
break;
|
break;
|
||||||
case APZHandledResult::HandledByRoot: {
|
case APZHandledPlace::HandledByRoot: {
|
||||||
aOut << "handled-by-root";
|
aOut << "handled-by-root";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case APZHandledResult::HandledByContent: {
|
case APZHandledPlace::HandledByContent: {
|
||||||
aOut << "handled-by-content";
|
aOut << "handled-by-content";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case APZHandledResult::Invalid: {
|
case APZHandledPlace::Invalid: {
|
||||||
aOut << "INVALID";
|
aOut << "INVALID";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -274,5 +351,13 @@ std::ostream& operator<<(std::ostream& aOut,
|
|||||||
return aOut;
|
return aOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& aOut,
|
||||||
|
const APZHandledResult& aHandledResult) {
|
||||||
|
aOut << "handled: " << aHandledResult.mPlace << ", ";
|
||||||
|
aOut << "scrollable: " << aHandledResult.mScrollableDirections << ", ";
|
||||||
|
aOut << "overscroll: " << aHandledResult.mOverscrollDirections << std::endl;
|
||||||
|
return aOut;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace layers
|
} // namespace layers
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|||||||
@@ -874,21 +874,21 @@ static APZHandledResult GetHandledResultFor(
|
|||||||
const AsyncPanZoomController* aApzc,
|
const AsyncPanZoomController* aApzc,
|
||||||
const InputBlockState& aCurrentInputBlock) {
|
const InputBlockState& aCurrentInputBlock) {
|
||||||
if (aCurrentInputBlock.ShouldDropEvents()) {
|
if (aCurrentInputBlock.ShouldDropEvents()) {
|
||||||
return APZHandledResult::HandledByContent;
|
return APZHandledResult{APZHandledPlace::HandledByContent, aApzc};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aApzc && aApzc->IsRootContent()) {
|
if (aApzc && aApzc->IsRootContent()) {
|
||||||
return aApzc->CanVerticalScrollWithDynamicToolbar()
|
return aApzc->CanVerticalScrollWithDynamicToolbar()
|
||||||
? APZHandledResult::HandledByRoot
|
? APZHandledResult{APZHandledPlace::HandledByRoot, aApzc}
|
||||||
: APZHandledResult::Unhandled;
|
: APZHandledResult{APZHandledPlace::Unhandled, aApzc};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return `HandledByRoot` if scroll positions in all relevant APZC are at the
|
// Return `HandledByRoot` if scroll positions in all relevant APZC are at the
|
||||||
// bottom edge and if there are contents covered by the dynamic toolbar.
|
// bottom edge and if there are contents covered by the dynamic toolbar.
|
||||||
return aApzc && aCurrentInputBlock.GetOverscrollHandoffChain()
|
return aApzc && aCurrentInputBlock.GetOverscrollHandoffChain()
|
||||||
->ScrollingDownWillMoveDynamicToolbar(aApzc)
|
->ScrollingDownWillMoveDynamicToolbar(aApzc)
|
||||||
? APZHandledResult::HandledByRoot
|
? APZHandledResult{APZHandledPlace::HandledByRoot, aApzc}
|
||||||
: APZHandledResult::HandledByContent;
|
: APZHandledResult{APZHandledPlace::HandledByContent, aApzc};
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputQueue::ProcessQueue() {
|
void InputQueue::ProcessQueue() {
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ class PinchGestureBlockState;
|
|||||||
class KeyboardBlockState;
|
class KeyboardBlockState;
|
||||||
class AsyncDragMetrics;
|
class AsyncDragMetrics;
|
||||||
class QueuedInput;
|
class QueuedInput;
|
||||||
enum class APZHandledResult : uint8_t;
|
|
||||||
struct APZEventResult;
|
struct APZEventResult;
|
||||||
|
struct APZHandledResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class stores incoming input events, associated with "input blocks",
|
* This class stores incoming input events, associated with "input blocks",
|
||||||
|
|||||||
@@ -408,8 +408,8 @@ class APZCTesterBase : public ::testing::Test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <class InputReceiver>
|
template <class InputReceiver>
|
||||||
void Tap(const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
|
APZEventResult Tap(const RefPtr<InputReceiver>& aTarget,
|
||||||
TimeDuration aTapLength,
|
const ScreenIntPoint& aPoint, TimeDuration aTapLength,
|
||||||
nsEventStatus (*aOutEventStatuses)[2] = nullptr,
|
nsEventStatus (*aOutEventStatuses)[2] = nullptr,
|
||||||
uint64_t* aOutInputBlockId = nullptr);
|
uint64_t* aOutInputBlockId = nullptr);
|
||||||
|
|
||||||
@@ -503,30 +503,32 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(APZCTesterBase::PanOptions)
|
|||||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(APZCTesterBase::PinchOptions)
|
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(APZCTesterBase::PinchOptions)
|
||||||
|
|
||||||
template <class InputReceiver>
|
template <class InputReceiver>
|
||||||
void APZCTesterBase::Tap(const RefPtr<InputReceiver>& aTarget,
|
APZEventResult APZCTesterBase::Tap(const RefPtr<InputReceiver>& aTarget,
|
||||||
const ScreenIntPoint& aPoint, TimeDuration aTapLength,
|
const ScreenIntPoint& aPoint,
|
||||||
|
TimeDuration aTapLength,
|
||||||
nsEventStatus (*aOutEventStatuses)[2],
|
nsEventStatus (*aOutEventStatuses)[2],
|
||||||
uint64_t* aOutInputBlockId) {
|
uint64_t* aOutInputBlockId) {
|
||||||
APZEventResult result = TouchDown(aTarget, aPoint, mcc->Time());
|
APZEventResult touchDownResult = TouchDown(aTarget, aPoint, mcc->Time());
|
||||||
if (aOutEventStatuses) {
|
if (aOutEventStatuses) {
|
||||||
(*aOutEventStatuses)[0] = result.GetStatus();
|
(*aOutEventStatuses)[0] = touchDownResult.GetStatus();
|
||||||
}
|
}
|
||||||
if (aOutInputBlockId) {
|
if (aOutInputBlockId) {
|
||||||
*aOutInputBlockId = result.mInputBlockId;
|
*aOutInputBlockId = touchDownResult.mInputBlockId;
|
||||||
}
|
}
|
||||||
mcc->AdvanceBy(aTapLength);
|
mcc->AdvanceBy(aTapLength);
|
||||||
|
|
||||||
// If touch-action is enabled then simulate the allowed touch behaviour
|
// If touch-action is enabled then simulate the allowed touch behaviour
|
||||||
// notification that the main thread is supposed to deliver.
|
// notification that the main thread is supposed to deliver.
|
||||||
if (StaticPrefs::layout_css_touch_action_enabled() &&
|
if (StaticPrefs::layout_css_touch_action_enabled() &&
|
||||||
result.GetStatus() != nsEventStatus_eConsumeNoDefault) {
|
touchDownResult.GetStatus() != nsEventStatus_eConsumeNoDefault) {
|
||||||
SetDefaultAllowedTouchBehavior(aTarget, result.mInputBlockId);
|
SetDefaultAllowedTouchBehavior(aTarget, touchDownResult.mInputBlockId);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = TouchUp(aTarget, aPoint, mcc->Time());
|
APZEventResult touchUpResult = TouchUp(aTarget, aPoint, mcc->Time());
|
||||||
if (aOutEventStatuses) {
|
if (aOutEventStatuses) {
|
||||||
(*aOutEventStatuses)[1] = result.GetStatus();
|
(*aOutEventStatuses)[1] = touchUpResult.GetStatus();
|
||||||
}
|
}
|
||||||
|
return touchDownResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class InputReceiver>
|
template <class InputReceiver>
|
||||||
|
|||||||
@@ -311,101 +311,3 @@ TEST_F(APZEventRegionsTester, Bug1117712) {
|
|||||||
targets.AppendElement(apzc2->GetGuid());
|
targets.AppendElement(apzc2->GetGuid());
|
||||||
manager->SetTargetAPZC(inputBlockId, targets);
|
manager->SetTargetAPZC(inputBlockId, targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that APZEventResult::GetHandledResult() is correctly
|
|
||||||
// populated.
|
|
||||||
TEST_F(APZEventRegionsTesterLayersOnly, HandledByRootApzcFlag) {
|
|
||||||
// Create simple layer tree containing a dispatch-to-content region
|
|
||||||
// that covers part but not all of its area.
|
|
||||||
const char* layerTreeSyntax = "c";
|
|
||||||
nsIntRegion layerVisibleRegions[] = {
|
|
||||||
nsIntRegion(IntRect(0, 0, 100, 100)),
|
|
||||||
};
|
|
||||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm,
|
|
||||||
layers);
|
|
||||||
SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
|
|
||||||
CSSRect(0, 0, 100, 200));
|
|
||||||
ModifyFrameMetrics(root, [](ScrollMetadata& sm, FrameMetrics& metrics) {
|
|
||||||
metrics.SetIsRootContent(true);
|
|
||||||
});
|
|
||||||
// away from the scrolling container layer.
|
|
||||||
EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100)));
|
|
||||||
// bottom half is dispatch-to-content
|
|
||||||
regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 50, 100, 50));
|
|
||||||
root->SetEventRegions(regions);
|
|
||||||
registration =
|
|
||||||
MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc);
|
|
||||||
UpdateHitTestingTree();
|
|
||||||
|
|
||||||
// Tap the top half and check that we report that the event was
|
|
||||||
// handled by the root APZC.
|
|
||||||
APZEventResult result =
|
|
||||||
TouchDown(manager, ScreenIntPoint(50, 25), mcc->Time());
|
|
||||||
TouchUp(manager, ScreenIntPoint(50, 25), mcc->Time());
|
|
||||||
EXPECT_EQ(result.GetHandledResult(), Some(APZHandledResult::HandledByRoot));
|
|
||||||
|
|
||||||
// Tap the bottom half and check that we report that we're not
|
|
||||||
// sure whether the event was handled by the root APZC.
|
|
||||||
result = TouchDown(manager, ScreenIntPoint(50, 75), mcc->Time());
|
|
||||||
TouchUp(manager, ScreenIntPoint(50, 75), mcc->Time());
|
|
||||||
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
|
||||||
|
|
||||||
// Register an input block callback that will tell us the
|
|
||||||
// delayed answer.
|
|
||||||
APZHandledResult delayedAnswer = APZHandledResult::Invalid;
|
|
||||||
manager->AddInputBlockCallback(result.mInputBlockId,
|
|
||||||
[&](uint64_t id, APZHandledResult answer) {
|
|
||||||
EXPECT_EQ(id, result.mInputBlockId);
|
|
||||||
delayedAnswer = answer;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Send APZ the relevant notifications to allow it to process the
|
|
||||||
// input block.
|
|
||||||
manager->SetAllowedTouchBehavior(result.mInputBlockId,
|
|
||||||
{AllowedTouchBehavior::VERTICAL_PAN});
|
|
||||||
manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
|
|
||||||
manager->ContentReceivedInputBlock(result.mInputBlockId,
|
|
||||||
/*preventDefault=*/false);
|
|
||||||
|
|
||||||
// Check that we received the delayed answer and it is what we expect.
|
|
||||||
EXPECT_EQ(delayedAnswer, APZHandledResult::HandledByRoot);
|
|
||||||
|
|
||||||
// Now repeat the tap on the bottom half, but simulate a prevent-default.
|
|
||||||
// This time, we expect a delayed answer of `HandledByContent`.
|
|
||||||
result = TouchDown(manager, ScreenIntPoint(50, 75), mcc->Time());
|
|
||||||
TouchUp(manager, ScreenIntPoint(50, 75), mcc->Time());
|
|
||||||
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
|
||||||
manager->AddInputBlockCallback(result.mInputBlockId,
|
|
||||||
[&](uint64_t id, APZHandledResult answer) {
|
|
||||||
EXPECT_EQ(id, result.mInputBlockId);
|
|
||||||
delayedAnswer = answer;
|
|
||||||
});
|
|
||||||
manager->SetAllowedTouchBehavior(result.mInputBlockId,
|
|
||||||
{AllowedTouchBehavior::VERTICAL_PAN});
|
|
||||||
manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
|
|
||||||
manager->ContentReceivedInputBlock(result.mInputBlockId,
|
|
||||||
/*preventDefault=*/true);
|
|
||||||
EXPECT_EQ(delayedAnswer, APZHandledResult::HandledByContent);
|
|
||||||
|
|
||||||
// Shrink the scrollable area, now it's no longer scrollable.
|
|
||||||
ModifyFrameMetrics(root, [](ScrollMetadata& sm, FrameMetrics& metrics) {
|
|
||||||
metrics.SetScrollableRect(CSSRect(0, 0, 100, 100));
|
|
||||||
});
|
|
||||||
UpdateHitTestingTree();
|
|
||||||
// Now repeat the tap on the bottom half with an event handler.
|
|
||||||
// This time, we expect a delayed answer of `Unhandled`.
|
|
||||||
result = TouchDown(manager, ScreenIntPoint(50, 75), mcc->Time());
|
|
||||||
TouchUp(manager, ScreenIntPoint(50, 75), mcc->Time());
|
|
||||||
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
|
||||||
manager->AddInputBlockCallback(result.mInputBlockId,
|
|
||||||
[&](uint64_t id, APZHandledResult answer) {
|
|
||||||
EXPECT_EQ(id, result.mInputBlockId);
|
|
||||||
delayedAnswer = answer;
|
|
||||||
});
|
|
||||||
manager->SetAllowedTouchBehavior(result.mInputBlockId,
|
|
||||||
{AllowedTouchBehavior::VERTICAL_PAN});
|
|
||||||
manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
|
|
||||||
manager->ContentReceivedInputBlock(result.mInputBlockId,
|
|
||||||
/*preventDefault=*/false);
|
|
||||||
EXPECT_EQ(delayedAnswer, APZHandledResult::Unhandled);
|
|
||||||
}
|
|
||||||
|
|||||||
435
gfx/layers/apz/test/gtest/TestEventResult.cpp
Normal file
435
gfx/layers/apz/test/gtest/TestEventResult.cpp
Normal file
@@ -0,0 +1,435 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "APZCTreeManagerTester.h"
|
||||||
|
#include "APZTestCommon.h"
|
||||||
|
#include "InputUtils.h"
|
||||||
|
#include "mozilla/layers/LayersTypes.h"
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
class APZEventResultTester : public APZCTreeManagerTester {
|
||||||
|
protected:
|
||||||
|
UniquePtr<ScopedLayerTreeRegistration> registration;
|
||||||
|
|
||||||
|
void UpdateOverscrollBehavior(OverscrollBehavior aX, OverscrollBehavior aY) {
|
||||||
|
ModifyFrameMetrics(root, [aX, aY](ScrollMetadata& sm, FrameMetrics& _) {
|
||||||
|
OverscrollBehaviorInfo overscroll;
|
||||||
|
overscroll.mBehaviorX = aX;
|
||||||
|
overscroll.mBehaviorY = aY;
|
||||||
|
sm.SetOverscrollBehavior(overscroll);
|
||||||
|
});
|
||||||
|
UpdateHitTestingTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetScrollOffsetOnMainThread(const CSSPoint& aPoint) {
|
||||||
|
RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root);
|
||||||
|
|
||||||
|
ScrollMetadata metadata = apzc->GetScrollMetadata();
|
||||||
|
metadata.GetMetrics().SetLayoutScrollOffset(aPoint);
|
||||||
|
nsTArray<ScrollPositionUpdate> scrollUpdates;
|
||||||
|
scrollUpdates.AppendElement(ScrollPositionUpdate::NewScroll(
|
||||||
|
ScrollOrigin::Other, CSSPoint::ToAppUnits(aPoint)));
|
||||||
|
metadata.SetScrollUpdates(scrollUpdates);
|
||||||
|
metadata.GetMetrics().SetScrollGeneration(
|
||||||
|
scrollUpdates.LastElement().GetGeneration());
|
||||||
|
apzc->NotifyLayersUpdated(metadata, /*aIsFirstPaint=*/false,
|
||||||
|
/*aThisLayerTreeUpdated=*/true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateScrollableRootLayer() {
|
||||||
|
const char* layerTreeSyntax = "c";
|
||||||
|
nsIntRegion layerVisibleRegions[] = {
|
||||||
|
nsIntRegion(IntRect(0, 0, 100, 100)),
|
||||||
|
};
|
||||||
|
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm,
|
||||||
|
layers);
|
||||||
|
SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
|
||||||
|
CSSRect(0, 0, 200, 200));
|
||||||
|
ModifyFrameMetrics(root, [](ScrollMetadata& sm, FrameMetrics& metrics) {
|
||||||
|
metrics.SetIsRootContent(true);
|
||||||
|
});
|
||||||
|
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0},
|
||||||
|
root, mcc);
|
||||||
|
UpdateHitTestingTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class PreventDefaultFlag { No, Yes };
|
||||||
|
std::tuple<APZEventResult, APZHandledResult> TapDispatchToContent(
|
||||||
|
const ScreenIntPoint& aPoint, PreventDefaultFlag aPreventDefaultFlag) {
|
||||||
|
APZEventResult result =
|
||||||
|
Tap(manager, aPoint, TimeDuration::FromMilliseconds(100));
|
||||||
|
|
||||||
|
APZHandledResult delayedAnswer{APZHandledPlace::Invalid, SideBits::eNone,
|
||||||
|
ScrollDirections()};
|
||||||
|
manager->AddInputBlockCallback(
|
||||||
|
result.mInputBlockId, [&](uint64_t id, const APZHandledResult& answer) {
|
||||||
|
EXPECT_EQ(id, result.mInputBlockId);
|
||||||
|
delayedAnswer = answer;
|
||||||
|
});
|
||||||
|
manager->SetAllowedTouchBehavior(result.mInputBlockId,
|
||||||
|
{AllowedTouchBehavior::VERTICAL_PAN});
|
||||||
|
manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
|
||||||
|
manager->ContentReceivedInputBlock(
|
||||||
|
result.mInputBlockId, aPreventDefaultFlag == PreventDefaultFlag::Yes);
|
||||||
|
return {result, delayedAnswer};
|
||||||
|
}
|
||||||
|
|
||||||
|
void OverscrollDirectionsWithEventHandlerTest(
|
||||||
|
PreventDefaultFlag aPreventDefaultFlag) {
|
||||||
|
EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100)));
|
||||||
|
regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
|
||||||
|
root->SetEventRegions(regions);
|
||||||
|
UpdateHitTestingTree();
|
||||||
|
|
||||||
|
APZHandledPlace expectedPlace =
|
||||||
|
aPreventDefaultFlag == PreventDefaultFlag::No
|
||||||
|
? APZHandledPlace::HandledByRoot
|
||||||
|
: APZHandledPlace::HandledByContent;
|
||||||
|
{
|
||||||
|
auto [result, delayedHandledResult] =
|
||||||
|
TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
EXPECT_EQ(
|
||||||
|
delayedHandledResult,
|
||||||
|
(APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
|
||||||
|
EitherScrollDirection}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// overscroll-behavior: contain, contain.
|
||||||
|
UpdateOverscrollBehavior(OverscrollBehavior::Contain,
|
||||||
|
OverscrollBehavior::Contain);
|
||||||
|
{
|
||||||
|
auto [result, delayedHandledResult] =
|
||||||
|
TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
EXPECT_EQ(
|
||||||
|
delayedHandledResult,
|
||||||
|
(APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
|
||||||
|
ScrollDirections()}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// overscroll-behavior: none, none.
|
||||||
|
UpdateOverscrollBehavior(OverscrollBehavior::None,
|
||||||
|
OverscrollBehavior::None);
|
||||||
|
{
|
||||||
|
auto [result, delayedHandledResult] =
|
||||||
|
TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
EXPECT_EQ(
|
||||||
|
delayedHandledResult,
|
||||||
|
(APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
|
||||||
|
ScrollDirections()}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// overscroll-behavior: auto, none.
|
||||||
|
UpdateOverscrollBehavior(OverscrollBehavior::Auto,
|
||||||
|
OverscrollBehavior::None);
|
||||||
|
{
|
||||||
|
auto [result, delayedHandledResult] =
|
||||||
|
TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
EXPECT_EQ(
|
||||||
|
delayedHandledResult,
|
||||||
|
(APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
|
||||||
|
HorizontalScrollDirection}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// overscroll-behavior: none, auto.
|
||||||
|
UpdateOverscrollBehavior(OverscrollBehavior::None,
|
||||||
|
OverscrollBehavior::Auto);
|
||||||
|
{
|
||||||
|
auto [result, delayedHandledResult] =
|
||||||
|
TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
EXPECT_EQ(
|
||||||
|
delayedHandledResult,
|
||||||
|
(APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
|
||||||
|
VerticalScrollDirection}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollableDirectionsWithEventHandlerTest(
|
||||||
|
PreventDefaultFlag aPreventDefaultFlag) {
|
||||||
|
EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100)));
|
||||||
|
regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
|
||||||
|
root->SetEventRegions(regions);
|
||||||
|
UpdateHitTestingTree();
|
||||||
|
|
||||||
|
APZHandledPlace expectedPlace =
|
||||||
|
aPreventDefaultFlag == PreventDefaultFlag::No
|
||||||
|
? APZHandledPlace::HandledByRoot
|
||||||
|
: APZHandledPlace::HandledByContent;
|
||||||
|
{
|
||||||
|
auto [result, delayedHandledResult] =
|
||||||
|
TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
EXPECT_EQ(
|
||||||
|
delayedHandledResult,
|
||||||
|
(APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
|
||||||
|
EitherScrollDirection}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// scroll down a bit.
|
||||||
|
SetScrollOffsetOnMainThread(CSSPoint(0, 10));
|
||||||
|
{
|
||||||
|
auto [result, delayedHandledResult] =
|
||||||
|
TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
EXPECT_EQ(delayedHandledResult,
|
||||||
|
(APZHandledResult{
|
||||||
|
expectedPlace,
|
||||||
|
SideBits::eTop | SideBits::eBottom | SideBits::eRight,
|
||||||
|
EitherScrollDirection}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// scroll to the bottom edge
|
||||||
|
SetScrollOffsetOnMainThread(CSSPoint(0, 100));
|
||||||
|
{
|
||||||
|
auto [result, delayedHandledResult] =
|
||||||
|
TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
EXPECT_EQ(
|
||||||
|
delayedHandledResult,
|
||||||
|
(APZHandledResult{expectedPlace, SideBits::eRight | SideBits::eTop,
|
||||||
|
EitherScrollDirection}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// scroll to right a bit.
|
||||||
|
SetScrollOffsetOnMainThread(CSSPoint(10, 100));
|
||||||
|
{
|
||||||
|
auto [result, delayedHandledResult] =
|
||||||
|
TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
EXPECT_EQ(
|
||||||
|
delayedHandledResult,
|
||||||
|
(APZHandledResult{expectedPlace,
|
||||||
|
SideBits::eLeft | SideBits::eRight | SideBits::eTop,
|
||||||
|
EitherScrollDirection}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// scroll to the right edge.
|
||||||
|
SetScrollOffsetOnMainThread(CSSPoint(100, 100));
|
||||||
|
{
|
||||||
|
auto [result, delayedHandledResult] =
|
||||||
|
TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
EXPECT_EQ(
|
||||||
|
delayedHandledResult,
|
||||||
|
(APZHandledResult{expectedPlace, SideBits::eTop | SideBits::eLeft,
|
||||||
|
EitherScrollDirection}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(APZEventResultTester, OverscrollDirections) {
|
||||||
|
CreateScrollableRootLayer();
|
||||||
|
|
||||||
|
TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
|
||||||
|
|
||||||
|
// The default value of overscroll-behavior is auto.
|
||||||
|
APZEventResult result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
|
||||||
|
EXPECT_EQ(result.GetHandledResult()->mOverscrollDirections,
|
||||||
|
EitherScrollDirection);
|
||||||
|
|
||||||
|
// overscroll-behavior: contain, contain.
|
||||||
|
UpdateOverscrollBehavior(OverscrollBehavior::Contain,
|
||||||
|
OverscrollBehavior::Contain);
|
||||||
|
result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
|
||||||
|
EXPECT_EQ(result.GetHandledResult()->mOverscrollDirections,
|
||||||
|
ScrollDirections());
|
||||||
|
|
||||||
|
// overscroll-behavior: none, none.
|
||||||
|
UpdateOverscrollBehavior(OverscrollBehavior::None, OverscrollBehavior::None);
|
||||||
|
result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
|
||||||
|
EXPECT_EQ(result.GetHandledResult()->mOverscrollDirections,
|
||||||
|
ScrollDirections());
|
||||||
|
|
||||||
|
// overscroll-behavior: auto, none.
|
||||||
|
UpdateOverscrollBehavior(OverscrollBehavior::Auto, OverscrollBehavior::None);
|
||||||
|
result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
|
||||||
|
EXPECT_EQ(result.GetHandledResult()->mOverscrollDirections,
|
||||||
|
HorizontalScrollDirection);
|
||||||
|
|
||||||
|
// overscroll-behavior: none, auto.
|
||||||
|
UpdateOverscrollBehavior(OverscrollBehavior::None, OverscrollBehavior::Auto);
|
||||||
|
result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
|
||||||
|
EXPECT_EQ(result.GetHandledResult()->mOverscrollDirections,
|
||||||
|
VerticalScrollDirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(APZEventResultTester, ScrollableDirections) {
|
||||||
|
CreateScrollableRootLayer();
|
||||||
|
|
||||||
|
TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
|
||||||
|
|
||||||
|
APZEventResult result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
|
||||||
|
// scrollable to down/right.
|
||||||
|
EXPECT_EQ(result.GetHandledResult()->mScrollableDirections,
|
||||||
|
SideBits::eBottom | SideBits::eRight);
|
||||||
|
|
||||||
|
// scroll down a bit.
|
||||||
|
SetScrollOffsetOnMainThread(CSSPoint(0, 10));
|
||||||
|
result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
|
||||||
|
// also scrollable toward top.
|
||||||
|
EXPECT_EQ(result.GetHandledResult()->mScrollableDirections,
|
||||||
|
SideBits::eTop | SideBits::eBottom | SideBits::eRight);
|
||||||
|
|
||||||
|
// scroll to the bottom edge
|
||||||
|
SetScrollOffsetOnMainThread(CSSPoint(0, 100));
|
||||||
|
result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
|
||||||
|
EXPECT_EQ(result.GetHandledResult()->mScrollableDirections,
|
||||||
|
SideBits::eRight | SideBits::eTop);
|
||||||
|
|
||||||
|
// scroll to right a bit.
|
||||||
|
SetScrollOffsetOnMainThread(CSSPoint(10, 100));
|
||||||
|
result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
|
||||||
|
EXPECT_EQ(result.GetHandledResult()->mScrollableDirections,
|
||||||
|
SideBits::eLeft | SideBits::eRight | SideBits::eTop);
|
||||||
|
|
||||||
|
// scroll to the right edge.
|
||||||
|
SetScrollOffsetOnMainThread(CSSPoint(100, 100));
|
||||||
|
result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
|
||||||
|
EXPECT_EQ(result.GetHandledResult()->mScrollableDirections,
|
||||||
|
SideBits::eLeft | SideBits::eTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
class APZEventResultTesterLayersOnly : public APZEventResultTester {
|
||||||
|
public:
|
||||||
|
APZEventResultTesterLayersOnly() { mLayersOnly = true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(APZEventResultTesterLayersOnly, OverscrollDirectionsWithEventHandler) {
|
||||||
|
CreateScrollableRootLayer();
|
||||||
|
|
||||||
|
OverscrollDirectionsWithEventHandlerTest(PreventDefaultFlag::No);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(APZEventResultTesterLayersOnly,
|
||||||
|
OverscrollDirectionsWithPreventDefaultEventHandler) {
|
||||||
|
CreateScrollableRootLayer();
|
||||||
|
|
||||||
|
OverscrollDirectionsWithEventHandlerTest(PreventDefaultFlag::Yes);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(APZEventResultTesterLayersOnly, ScrollableDirectionsWithEventHandler) {
|
||||||
|
CreateScrollableRootLayer();
|
||||||
|
|
||||||
|
ScrollableDirectionsWithEventHandlerTest(PreventDefaultFlag::No);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(APZEventResultTesterLayersOnly,
|
||||||
|
ScrollableDirectionsWithPreventDefaultEventHandler) {
|
||||||
|
CreateScrollableRootLayer();
|
||||||
|
|
||||||
|
ScrollableDirectionsWithEventHandlerTest(PreventDefaultFlag::Yes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that APZEventResult::GetHandledResult() is correctly
|
||||||
|
// populated.
|
||||||
|
TEST_F(APZEventResultTesterLayersOnly, HandledByRootApzcFlag) {
|
||||||
|
// Create simple layer tree containing a dispatch-to-content region
|
||||||
|
// that covers part but not all of its area.
|
||||||
|
const char* layerTreeSyntax = "c";
|
||||||
|
nsIntRegion layerVisibleRegions[] = {
|
||||||
|
nsIntRegion(IntRect(0, 0, 100, 100)),
|
||||||
|
};
|
||||||
|
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm,
|
||||||
|
layers);
|
||||||
|
SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
|
||||||
|
CSSRect(0, 0, 100, 200));
|
||||||
|
ModifyFrameMetrics(root, [](ScrollMetadata& sm, FrameMetrics& metrics) {
|
||||||
|
metrics.SetIsRootContent(true);
|
||||||
|
});
|
||||||
|
// away from the scrolling container layer.
|
||||||
|
EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100)));
|
||||||
|
// bottom half is dispatch-to-content
|
||||||
|
regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 50, 100, 50));
|
||||||
|
root->SetEventRegions(regions);
|
||||||
|
registration =
|
||||||
|
MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc);
|
||||||
|
UpdateHitTestingTree();
|
||||||
|
|
||||||
|
// Tap the top half and check that we report that the event was
|
||||||
|
// handled by the root APZC.
|
||||||
|
APZEventResult result =
|
||||||
|
TouchDown(manager, ScreenIntPoint(50, 25), mcc->Time());
|
||||||
|
TouchUp(manager, ScreenIntPoint(50, 25), mcc->Time());
|
||||||
|
EXPECT_EQ(result.GetHandledResult(),
|
||||||
|
Some(APZHandledResult{APZHandledPlace::HandledByRoot,
|
||||||
|
SideBits::eBottom, EitherScrollDirection}));
|
||||||
|
|
||||||
|
// Tap the bottom half and check that we report that we're not
|
||||||
|
// sure whether the event was handled by the root APZC.
|
||||||
|
result = TouchDown(manager, ScreenIntPoint(50, 75), mcc->Time());
|
||||||
|
TouchUp(manager, ScreenIntPoint(50, 75), mcc->Time());
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
|
||||||
|
// Register an input block callback that will tell us the
|
||||||
|
// delayed answer.
|
||||||
|
APZHandledResult delayedAnswer{APZHandledPlace::Invalid, SideBits::eNone,
|
||||||
|
ScrollDirections()};
|
||||||
|
manager->AddInputBlockCallback(
|
||||||
|
result.mInputBlockId, [&](uint64_t id, const APZHandledResult& answer) {
|
||||||
|
EXPECT_EQ(id, result.mInputBlockId);
|
||||||
|
delayedAnswer = answer;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send APZ the relevant notifications to allow it to process the
|
||||||
|
// input block.
|
||||||
|
manager->SetAllowedTouchBehavior(result.mInputBlockId,
|
||||||
|
{AllowedTouchBehavior::VERTICAL_PAN});
|
||||||
|
manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
|
||||||
|
manager->ContentReceivedInputBlock(result.mInputBlockId,
|
||||||
|
/*aPreventDefault=*/false);
|
||||||
|
|
||||||
|
// Check that we received the delayed answer and it is what we expect.
|
||||||
|
EXPECT_EQ(delayedAnswer,
|
||||||
|
(APZHandledResult{APZHandledPlace::HandledByRoot, SideBits::eBottom,
|
||||||
|
EitherScrollDirection}));
|
||||||
|
|
||||||
|
// Now repeat the tap on the bottom half, but simulate a prevent-default.
|
||||||
|
// This time, we expect a delayed answer of `HandledByContent`.
|
||||||
|
result = TouchDown(manager, ScreenIntPoint(50, 75), mcc->Time());
|
||||||
|
TouchUp(manager, ScreenIntPoint(50, 75), mcc->Time());
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
manager->AddInputBlockCallback(
|
||||||
|
result.mInputBlockId, [&](uint64_t id, const APZHandledResult& answer) {
|
||||||
|
EXPECT_EQ(id, result.mInputBlockId);
|
||||||
|
delayedAnswer = answer;
|
||||||
|
});
|
||||||
|
manager->SetAllowedTouchBehavior(result.mInputBlockId,
|
||||||
|
{AllowedTouchBehavior::VERTICAL_PAN});
|
||||||
|
manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
|
||||||
|
manager->ContentReceivedInputBlock(result.mInputBlockId,
|
||||||
|
/*aPreventDefault=*/true);
|
||||||
|
EXPECT_EQ(delayedAnswer,
|
||||||
|
(APZHandledResult{APZHandledPlace::HandledByContent,
|
||||||
|
SideBits::eBottom, EitherScrollDirection}));
|
||||||
|
|
||||||
|
// Shrink the scrollable area, now it's no longer scrollable.
|
||||||
|
ModifyFrameMetrics(root, [](ScrollMetadata& sm, FrameMetrics& metrics) {
|
||||||
|
metrics.SetScrollableRect(CSSRect(0, 0, 100, 100));
|
||||||
|
});
|
||||||
|
UpdateHitTestingTree();
|
||||||
|
// Now repeat the tap on the bottom half with an event handler.
|
||||||
|
// This time, we expect a delayed answer of `Unhandled`.
|
||||||
|
result = TouchDown(manager, ScreenIntPoint(50, 75), mcc->Time());
|
||||||
|
TouchUp(manager, ScreenIntPoint(50, 75), mcc->Time());
|
||||||
|
EXPECT_EQ(result.GetHandledResult(), Nothing());
|
||||||
|
manager->AddInputBlockCallback(
|
||||||
|
result.mInputBlockId, [&](uint64_t id, const APZHandledResult& answer) {
|
||||||
|
EXPECT_EQ(id, result.mInputBlockId);
|
||||||
|
delayedAnswer = answer;
|
||||||
|
});
|
||||||
|
manager->SetAllowedTouchBehavior(result.mInputBlockId,
|
||||||
|
{AllowedTouchBehavior::VERTICAL_PAN});
|
||||||
|
manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
|
||||||
|
manager->ContentReceivedInputBlock(result.mInputBlockId,
|
||||||
|
/*aPreventDefault=*/false);
|
||||||
|
EXPECT_EQ(delayedAnswer,
|
||||||
|
(APZHandledResult{APZHandledPlace::Unhandled, SideBits::eNone,
|
||||||
|
ScrollDirections()}));
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
"TestBasic.cpp",
|
"TestBasic.cpp",
|
||||||
"TestEventRegions.cpp",
|
"TestEventRegions.cpp",
|
||||||
|
"TestEventResult.cpp",
|
||||||
"TestFlingAcceleration.cpp",
|
"TestFlingAcceleration.cpp",
|
||||||
"TestGestureDetector.cpp",
|
"TestGestureDetector.cpp",
|
||||||
"TestHitTesting.cpp",
|
"TestHitTesting.cpp",
|
||||||
|
|||||||
@@ -577,11 +577,48 @@ struct ParamTraits<nsEventStatus>
|
|||||||
nsEventStatus_eSentinel> {};
|
nsEventStatus_eSentinel> {};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct ParamTraits<mozilla::layers::APZHandledResult>
|
struct ParamTraits<mozilla::layers::APZHandledPlace>
|
||||||
: public ContiguousEnumSerializer<
|
: public ContiguousEnumSerializer<
|
||||||
mozilla::layers::APZHandledResult,
|
mozilla::layers::APZHandledPlace,
|
||||||
mozilla::layers::APZHandledResult::Unhandled,
|
mozilla::layers::APZHandledPlace::Unhandled,
|
||||||
mozilla::layers::APZHandledResult::Last> {};
|
mozilla::layers::APZHandledPlace::Last> {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct ParamTraits<mozilla::layers::ScrollDirections> {
|
||||||
|
typedef mozilla::layers::ScrollDirections paramType;
|
||||||
|
|
||||||
|
static void Write(Message* aMsg, const paramType& aParam) {
|
||||||
|
WriteParam(aMsg, aParam.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Read(const Message* aMsg, PickleIterator* aIter,
|
||||||
|
paramType* aResult) {
|
||||||
|
uint8_t value;
|
||||||
|
if (!ReadParam(aMsg, aIter, &value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
aResult->deserialize(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct ParamTraits<mozilla::layers::APZHandledResult> {
|
||||||
|
typedef mozilla::layers::APZHandledResult paramType;
|
||||||
|
|
||||||
|
static void Write(Message* aMsg, const paramType& aParam) {
|
||||||
|
WriteParam(aMsg, aParam.mPlace);
|
||||||
|
WriteParam(aMsg, aParam.mScrollableDirections);
|
||||||
|
WriteParam(aMsg, aParam.mOverscrollDirections);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Read(const Message* aMsg, PickleIterator* aIter,
|
||||||
|
paramType* aResult) {
|
||||||
|
return (ReadParam(aMsg, aIter, &aResult->mPlace) &&
|
||||||
|
ReadParam(aMsg, aIter, &aResult->mScrollableDirections) &&
|
||||||
|
ReadParam(aMsg, aIter, &aResult->mOverscrollDirections));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct ParamTraits<mozilla::layers::APZEventResult> {
|
struct ParamTraits<mozilla::layers::APZEventResult> {
|
||||||
|
|||||||
@@ -405,8 +405,7 @@ class NPZCSupport final
|
|||||||
case nsEventStatus_eIgnore:
|
case nsEventStatus_eIgnore:
|
||||||
return INPUT_RESULT_UNHANDLED;
|
return INPUT_RESULT_UNHANDLED;
|
||||||
case nsEventStatus_eConsumeDoDefault:
|
case nsEventStatus_eConsumeDoDefault:
|
||||||
return (result.GetHandledResult() ==
|
return result.GetHandledResult()->IsHandledByRoot()
|
||||||
Some(APZHandledResult::HandledByRoot))
|
|
||||||
? INPUT_RESULT_HANDLED
|
? INPUT_RESULT_HANDLED
|
||||||
: INPUT_RESULT_HANDLED_CONTENT;
|
: INPUT_RESULT_HANDLED_CONTENT;
|
||||||
default:
|
default:
|
||||||
@@ -459,14 +458,14 @@ class NPZCSupport final
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int32_t ConvertAPZHandledResult(APZHandledResult aHandledResult) {
|
static int32_t ConvertAPZHandledResult(APZHandledResult aHandledResult) {
|
||||||
switch (aHandledResult) {
|
switch (aHandledResult.mPlace) {
|
||||||
case APZHandledResult::Unhandled:
|
case APZHandledPlace::Unhandled:
|
||||||
return INPUT_RESULT_UNHANDLED;
|
return INPUT_RESULT_UNHANDLED;
|
||||||
case APZHandledResult::HandledByRoot:
|
case APZHandledPlace::HandledByRoot:
|
||||||
return INPUT_RESULT_HANDLED;
|
return INPUT_RESULT_HANDLED;
|
||||||
case APZHandledResult::HandledByContent:
|
case APZHandledPlace::HandledByContent:
|
||||||
return INPUT_RESULT_HANDLED_CONTENT;
|
return INPUT_RESULT_HANDLED_CONTENT;
|
||||||
case APZHandledResult::Invalid:
|
case APZHandledPlace::Invalid:
|
||||||
MOZ_ASSERT_UNREACHABLE("The handled result should NOT be Invalid");
|
MOZ_ASSERT_UNREACHABLE("The handled result should NOT be Invalid");
|
||||||
return INPUT_RESULT_UNHANDLED;
|
return INPUT_RESULT_UNHANDLED;
|
||||||
}
|
}
|
||||||
@@ -546,8 +545,7 @@ class NPZCSupport final
|
|||||||
case nsEventStatus_eIgnore:
|
case nsEventStatus_eIgnore:
|
||||||
return INPUT_RESULT_UNHANDLED;
|
return INPUT_RESULT_UNHANDLED;
|
||||||
case nsEventStatus_eConsumeDoDefault:
|
case nsEventStatus_eConsumeDoDefault:
|
||||||
return (result.GetHandledResult() ==
|
return result.GetHandledResult()->IsHandledByRoot()
|
||||||
Some(APZHandledResult::HandledByRoot))
|
|
||||||
? INPUT_RESULT_HANDLED
|
? INPUT_RESULT_HANDLED
|
||||||
: INPUT_RESULT_HANDLED_CONTENT;
|
: INPUT_RESULT_HANDLED_CONTENT;
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user