зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1663000 - Return UNHANDLED even if there's no event handler which uses preventDefault() and the root content is not covered by the dynamic toolbar. r=botond,geckoview-reviewers,snorp
This change has a couple of tests but they are for scrollable/non-scrollable contents, they are not for non-scrollable contents but covered by the dynamic toolbar cases, e.g. 100vh body element. Those cases will be introduced in the subsequent change. Differential Revision: https://phabricator.services.mozilla.com/D93915
This commit is contained in:
Родитель
ea157cca7c
Коммит
9a544e618f
|
@ -20,6 +20,18 @@ namespace layers {
|
|||
class APZInputBridgeParent;
|
||||
struct ScrollableLayerGuid;
|
||||
|
||||
enum class APZHandledResult : uint8_t {
|
||||
Unhandled = 0, // we know for sure that the event will not be handled
|
||||
// by either the root APZC or others
|
||||
HandledByRoot = 1, // we know for sure that the event will be handled
|
||||
// by the root content APZC
|
||||
HandledByContent = 2, // we know for sure it will be handled by a non-root
|
||||
// APZC or by an event listener using preventDefault()
|
||||
// in a document
|
||||
Invalid = 3,
|
||||
Last = Invalid
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the outcome of APZ receiving and processing an input event.
|
||||
* This is returned from APZInputBridge::ReceiveInputEvent() and related APIs.
|
||||
|
@ -65,6 +77,7 @@ struct APZEventResult {
|
|||
* by the root content APZC;
|
||||
* - set to false if we know for sure it will not be;
|
||||
* - left empty if we are unsure.
|
||||
* FIXME: Bug 1674694 - We should use APZHandledResult here.
|
||||
*/
|
||||
Maybe<bool> mHandledByRootApzc;
|
||||
/**
|
||||
|
@ -145,6 +158,9 @@ class APZInputBridge {
|
|||
virtual ~APZInputBridge() = default;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& aOut,
|
||||
const APZHandledResult& aHandledResult);
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ enum ZoomToRectBehavior : uint32_t {
|
|||
};
|
||||
|
||||
class AsyncDragMetrics;
|
||||
enum class APZHandledResult : uint8_t;
|
||||
|
||||
class IAPZCTreeManager {
|
||||
NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(IAPZCTreeManager)
|
||||
|
@ -147,8 +148,8 @@ class IAPZCTreeManager {
|
|||
* This is only implemented when the caller is in the same process as
|
||||
* the APZCTreeManager.
|
||||
*/
|
||||
using InputBlockCallback =
|
||||
std::function<void(uint64_t aInputBlockId, bool aHandledByRootApzc)>;
|
||||
using InputBlockCallback = std::function<void(
|
||||
uint64_t aInputBlockId, APZHandledResult aHandledResult)>;
|
||||
virtual void AddInputBlockCallback(uint64_t aInputBlockId,
|
||||
InputBlockCallback&& aCallback) = 0;
|
||||
|
||||
|
|
|
@ -189,5 +189,27 @@ APZEventResult APZInputBridge::ReceiveInputEvent(WidgetInputEvent& aEvent) {
|
|||
return result;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& aOut,
|
||||
const APZHandledResult& aHandledResult) {
|
||||
switch (aHandledResult) {
|
||||
case APZHandledResult::Unhandled:
|
||||
aOut << "unhandled";
|
||||
break;
|
||||
case APZHandledResult::HandledByRoot: {
|
||||
aOut << "handled-by-root";
|
||||
break;
|
||||
}
|
||||
case APZHandledResult::HandledByContent: {
|
||||
aOut << "handled-by-content";
|
||||
break;
|
||||
}
|
||||
case APZHandledResult::Invalid: {
|
||||
aOut << "INVALID";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return aOut;
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "GestureEventListener.h"
|
||||
#include "InputBlockState.h"
|
||||
#include "mozilla/layers/APZInputBridge.h"
|
||||
#include "mozilla/layers/APZThreadUtils.h"
|
||||
#include "mozilla/ToString.h"
|
||||
#include "OverscrollHandoffState.h"
|
||||
|
@ -854,6 +855,25 @@ void InputQueue::SetAllowedTouchBehavior(
|
|||
}
|
||||
}
|
||||
|
||||
static APZHandledResult GetHandledResultFor(
|
||||
const AsyncPanZoomController* aApzc,
|
||||
const InputBlockState& aCurrentInputBlock) {
|
||||
if (aCurrentInputBlock.ShouldDropEvents()) {
|
||||
return APZHandledResult::HandledByContent;
|
||||
}
|
||||
|
||||
if (aApzc && aApzc->IsRootContent()) {
|
||||
return aApzc->CanScrollDownwardsWithDynamicToolbar()
|
||||
? APZHandledResult::HandledByRoot
|
||||
: APZHandledResult::Unhandled;
|
||||
}
|
||||
|
||||
// FIXME: We need to return `HandledByRoot` if scroll positions in all
|
||||
// relevant APZCs are at the bottom edge and if there are contents covered by
|
||||
// the dynamic toolbar.
|
||||
return APZHandledResult::HandledByContent;
|
||||
}
|
||||
|
||||
void InputQueue::ProcessQueue() {
|
||||
APZThreadUtils::AssertOnControllerThread();
|
||||
|
||||
|
@ -875,9 +895,8 @@ void InputQueue::ProcessQueue() {
|
|||
// input block, invoke it.
|
||||
auto it = mInputBlockCallbacks.find(curBlock->GetBlockId());
|
||||
if (it != mInputBlockCallbacks.end()) {
|
||||
bool handledByRootApzc =
|
||||
!curBlock->ShouldDropEvents() && target && target->IsRootContent();
|
||||
it->second(curBlock->GetBlockId(), handledByRootApzc);
|
||||
APZHandledResult handledResult = GetHandledResultFor(target, *curBlock);
|
||||
it->second(curBlock->GetBlockId(), handledResult);
|
||||
// The callback is one-shot; discard it after calling it.
|
||||
mInputBlockCallbacks.erase(it);
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ class PinchGestureBlockState;
|
|||
class KeyboardBlockState;
|
||||
class AsyncDragMetrics;
|
||||
class QueuedInput;
|
||||
enum class APZHandledResult : uint8_t;
|
||||
|
||||
/**
|
||||
* This class stores incoming input events, associated with "input blocks",
|
||||
|
@ -146,8 +147,8 @@ class InputQueue {
|
|||
|
||||
InputBlockState* GetBlockForId(uint64_t aInputBlockId);
|
||||
|
||||
using InputBlockCallback =
|
||||
std::function<void(uint64_t aInputBlockId, bool aHandledByRootApzc)>;
|
||||
using InputBlockCallback = std::function<void(
|
||||
uint64_t aInputBlockId, APZHandledResult aHandledResult)>;
|
||||
void AddInputBlockCallback(uint64_t aInputBlockId,
|
||||
InputBlockCallback&& aCallback);
|
||||
|
||||
|
|
|
@ -322,7 +322,8 @@ TEST_F(APZEventRegionsTester, HandledByRootApzcFlag) {
|
|||
};
|
||||
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm,
|
||||
layers);
|
||||
SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID);
|
||||
SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
|
||||
CSSRect(0, 0, 100, 200));
|
||||
ModifyFrameMetrics(root, [](ScrollMetadata& sm, FrameMetrics& metrics) {
|
||||
metrics.SetIsRootContent(true);
|
||||
});
|
||||
|
@ -350,11 +351,11 @@ TEST_F(APZEventRegionsTester, HandledByRootApzcFlag) {
|
|||
|
||||
// Register an input block callback that will tell us the
|
||||
// delayed answer.
|
||||
Maybe<bool> delayedAnswer;
|
||||
APZHandledResult delayedAnswer = APZHandledResult::Invalid;
|
||||
manager->AddInputBlockCallback(result.mInputBlockId,
|
||||
[&](uint64_t id, bool answer) {
|
||||
[&](uint64_t id, APZHandledResult answer) {
|
||||
EXPECT_EQ(id, result.mInputBlockId);
|
||||
delayedAnswer = Some(answer);
|
||||
delayedAnswer = answer;
|
||||
});
|
||||
|
||||
// Send APZ the relevant notifications to allow it to process the
|
||||
|
@ -366,22 +367,44 @@ TEST_F(APZEventRegionsTester, HandledByRootApzcFlag) {
|
|||
/*preventDefault=*/false);
|
||||
|
||||
// Check that we received the delayed answer and it is what we expect.
|
||||
EXPECT_EQ(delayedAnswer, Some(true));
|
||||
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 false.
|
||||
// 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.mHandledByRootApzc, Nothing());
|
||||
manager->AddInputBlockCallback(result.mInputBlockId,
|
||||
[&](uint64_t id, bool answer) {
|
||||
[&](uint64_t id, APZHandledResult answer) {
|
||||
EXPECT_EQ(id, result.mInputBlockId);
|
||||
delayedAnswer = Some(answer);
|
||||
delayedAnswer = answer;
|
||||
});
|
||||
manager->SetAllowedTouchBehavior(result.mInputBlockId,
|
||||
{AllowedTouchBehavior::VERTICAL_PAN});
|
||||
manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
|
||||
manager->ContentReceivedInputBlock(result.mInputBlockId,
|
||||
/*preventDefault=*/true);
|
||||
EXPECT_EQ(delayedAnswer, Some(false));
|
||||
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.mHandledByRootApzc, 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);
|
||||
}
|
||||
|
|
|
@ -17,19 +17,19 @@ body {
|
|||
#one {
|
||||
background-color: red;
|
||||
width: 200vw;
|
||||
height: 100vh;
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
#two {
|
||||
background-color: green;
|
||||
width: 200vw;
|
||||
height: 100vh;
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
#three {
|
||||
background-color: blue;
|
||||
width: 200vw;
|
||||
height: 100vh;
|
||||
height: 200vh;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
@ -37,5 +37,10 @@ body {
|
|||
<div id="one"></div>
|
||||
<div id="two"></div>
|
||||
<div id="three"></div>
|
||||
<script>
|
||||
document.getElementById('two').addEventListener('touchstart', e => {
|
||||
console.log('not preventing default');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -277,11 +277,18 @@ class PanZoomControllerTest : BaseSessionTest() {
|
|||
|
||||
// Touch handler without preventDefault
|
||||
value = sessionRule.waitForResult(sendDownEvent(50f, 75f))
|
||||
assertThat("Value should match", value, equalTo(PanZoomController.INPUT_RESULT_HANDLED))
|
||||
// Nothing should have done in the event handler and the content is not scrollable,
|
||||
// thus the input result should be UNHANDLED, i.e. the dynamic toolbar should NOT
|
||||
// move in response to the event.
|
||||
assertThat("Value should match", value, equalTo(PanZoomController.INPUT_RESULT_UNHANDLED))
|
||||
|
||||
// No touch handlers, with scrolling
|
||||
setupScroll()
|
||||
value = sessionRule.waitForResult(sendDownEvent(50f, 50f))
|
||||
value = sessionRule.waitForResult(sendDownEvent(50f, 25f))
|
||||
assertThat("Value should match", value, equalTo(PanZoomController.INPUT_RESULT_HANDLED))
|
||||
|
||||
// Touch handler with scrolling
|
||||
value = sessionRule.waitForResult(sendDownEvent(50f, 75f))
|
||||
assertThat("Value should match", value, equalTo(PanZoomController.INPUT_RESULT_HANDLED))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -427,6 +427,22 @@ class NPZCSupport final
|
|||
return result;
|
||||
}
|
||||
|
||||
static int32_t ConvertAPZHandledResult(APZHandledResult aHandledResult) {
|
||||
switch (aHandledResult) {
|
||||
case APZHandledResult::Unhandled:
|
||||
return INPUT_RESULT_UNHANDLED;
|
||||
case APZHandledResult::HandledByRoot:
|
||||
return INPUT_RESULT_HANDLED;
|
||||
case APZHandledResult::HandledByContent:
|
||||
return INPUT_RESULT_HANDLED_CONTENT;
|
||||
case APZHandledResult::Invalid:
|
||||
MOZ_ASSERT_UNREACHABLE("The handled result should NOT be Invalid");
|
||||
return INPUT_RESULT_UNHANDLED;
|
||||
}
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown handled result");
|
||||
return INPUT_RESULT_UNHANDLED;
|
||||
}
|
||||
|
||||
public:
|
||||
int32_t HandleMouseEvent(int32_t aAction, int64_t aTime, int32_t aMetaState,
|
||||
float aX, float aY, int buttons) {
|
||||
|
@ -733,10 +749,9 @@ class NPZCSupport final
|
|||
controller->AddInputBlockCallback(
|
||||
result.mInputBlockId,
|
||||
[returnResult = java::GeckoResult::GlobalRef(returnResult)](
|
||||
uint64_t aInputBlockId, bool aHandledByRootApzc) {
|
||||
uint64_t aInputBlockId, APZHandledResult aHandledResult) {
|
||||
returnResult->Complete(java::sdk::Integer::ValueOf(
|
||||
aHandledByRootApzc ? INPUT_RESULT_HANDLED
|
||||
: INPUT_RESULT_HANDLED_CONTENT));
|
||||
ConvertAPZHandledResult(aHandledResult)));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче