Bug 1168182 - Bind wheel event targets to wheel transactions. r=masayuki,smaug

- Create wheel transactions for wheel events handled by APZ.
 - Group wheel events with the current wheel transaction, so that all
   wheel events in a wheel transaction are fired to the same element.
 - Store the current event target for the first event in a wheel
   transaction to be used for subsequent events.
 - Add the dom.event.wheel-event-groups.enabled preference as a feature
   flag for this behavior.

Differential Revision: https://phabricator.services.mozilla.com/D163484
This commit is contained in:
Dan Robertson 2023-03-20 12:19:36 +00:00
Родитель 17db57410f
Коммит 30e2548477
11 изменённых файлов: 219 добавлений и 55 удалений

Просмотреть файл

@ -2745,7 +2745,7 @@ nsIFrame* EventStateManager::ComputeScrollTargetAndMayAdjustWheelEvent(
// out of the frame, or when more than "mousewheel.transaction.timeout"
// milliseconds have passed after the last operation, even if the mouse
// hasn't moved.
nsIFrame* lastScrollFrame = WheelTransaction::GetTargetFrame();
nsIFrame* lastScrollFrame = WheelTransaction::GetScrollTargetFrame();
if (lastScrollFrame) {
nsIScrollableFrame* scrollableFrame =
lastScrollFrame->GetScrollTargetFrame();
@ -2927,7 +2927,9 @@ void EventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame,
MOZ_ASSERT(scrollFrame);
AutoWeakFrame scrollFrameWeak(scrollFrame);
if (!WheelTransaction::WillHandleDefaultAction(aEvent, scrollFrameWeak)) {
AutoWeakFrame eventFrameWeak(mCurrentTarget);
if (!WheelTransaction::WillHandleDefaultAction(aEvent, scrollFrameWeak,
eventFrameWeak)) {
return;
}
@ -3718,6 +3720,14 @@ nsresult EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
case WheelPrefs::ACTION_NONE:
default:
bool allDeltaOverflown = false;
if (wheelEvent->mDeltaX != 0.0 || wheelEvent->mDeltaY != 0.0) {
if (frameToScroll) {
WheelTransaction::WillHandleDefaultAction(
wheelEvent, frameToScroll, mCurrentTarget);
} else {
WheelTransaction::EndTransaction();
}
}
if (wheelEvent->mFlags.mHandledByAPZ) {
if (wheelEvent->mCanTriggerSwipe) {
// For events that can trigger swipes, APZ needs to know whether
@ -5859,6 +5869,7 @@ void EventStateManager::ContentRemoved(Document* aDocument,
IMEStateManager::OnRemoveContent(*presContext,
MOZ_KnownLive(*aContent->AsElement()));
}
WheelTransaction::OnRemoveElement(aContent);
}
// inform the focus manager that the content is being removed. If this

Просмотреть файл

@ -111,7 +111,8 @@ WheelHandlingUtils::GetDisregardedWheelScrollDirection(const nsIFrame* aFrame) {
/* mozilla::WheelTransaction */
/******************************************************************/
AutoWeakFrame WheelTransaction::sTargetFrame(nullptr);
AutoWeakFrame WheelTransaction::sScrollTargetFrame(nullptr);
AutoWeakFrame WheelTransaction::sEventTargetFrame(nullptr);
uint32_t WheelTransaction::sTime = 0;
uint32_t WheelTransaction::sMouseMoved = 0;
nsITimer* WheelTransaction::sTimer = nullptr;
@ -128,13 +129,28 @@ bool WheelTransaction::OutOfTime(uint32_t aBaseTime, uint32_t aThreshold) {
void WheelTransaction::OwnScrollbars(bool aOwn) { sOwnScrollbars = aOwn; }
/* static */
void WheelTransaction::BeginTransaction(nsIFrame* aTargetFrame,
void WheelTransaction::BeginTransaction(nsIFrame* aScrollTargetFrame,
nsIFrame* aEventTargetFrame,
const WidgetWheelEvent* aEvent) {
NS_ASSERTION(!sTargetFrame, "previous transaction is not finished!");
NS_ASSERTION(!sScrollTargetFrame && !sEventTargetFrame,
"previous transaction is not finished!");
MOZ_ASSERT(aEvent->mMessage == eWheel,
"Transaction must be started with a wheel event");
ScrollbarsForWheel::OwnWheelTransaction(false);
sTargetFrame = aTargetFrame;
sScrollTargetFrame = aScrollTargetFrame;
// Only set the static event target if wheel event groups are enabled.
if (StaticPrefs::dom_event_wheel_event_groups_enabled()) {
// Set a static event target for the wheel transaction. This will be used
// to override the event target frame when computing the event target from
// input coordinates. When this preference is not set or there is no stored
// event target for the current wheel transaction, the event target will
// not be overridden by the current wheel transaction, but will be computed
// from the input coordinates.
sEventTargetFrame = aEventTargetFrame;
}
sScrollSeriesCounter = 0;
if (!UpdateTransaction(aEvent)) {
NS_ERROR("BeginTransaction is called even cannot scroll the frame");
@ -144,7 +160,7 @@ void WheelTransaction::BeginTransaction(nsIFrame* aTargetFrame,
/* static */
bool WheelTransaction::UpdateTransaction(const WidgetWheelEvent* aEvent) {
nsIFrame* scrollToFrame = GetTargetFrame();
nsIFrame* scrollToFrame = GetScrollTargetFrame();
nsIScrollableFrame* scrollableFrame = scrollToFrame->GetScrollTargetFrame();
if (scrollableFrame) {
scrollToFrame = do_QueryFrame(scrollableFrame);
@ -188,7 +204,8 @@ void WheelTransaction::EndTransaction() {
if (sTimer) {
sTimer->Cancel();
}
sTargetFrame = nullptr;
sScrollTargetFrame = nullptr;
sEventTargetFrame = nullptr;
sScrollSeriesCounter = 0;
if (sOwnScrollbars) {
sOwnScrollbars = false;
@ -199,13 +216,16 @@ void WheelTransaction::EndTransaction() {
/* static */
bool WheelTransaction::WillHandleDefaultAction(
WidgetWheelEvent* aWheelEvent, AutoWeakFrame& aTargetWeakFrame) {
nsIFrame* lastTargetFrame = GetTargetFrame();
WidgetWheelEvent* aWheelEvent, AutoWeakFrame& aScrollTargetWeakFrame,
AutoWeakFrame& aEventTargetWeakFrame) {
nsIFrame* lastTargetFrame = GetScrollTargetFrame();
if (!lastTargetFrame) {
BeginTransaction(aTargetWeakFrame.GetFrame(), aWheelEvent);
} else if (lastTargetFrame != aTargetWeakFrame.GetFrame()) {
BeginTransaction(aScrollTargetWeakFrame.GetFrame(),
aEventTargetWeakFrame.GetFrame(), aWheelEvent);
} else if (lastTargetFrame != aScrollTargetWeakFrame.GetFrame()) {
EndTransaction();
BeginTransaction(aTargetWeakFrame.GetFrame(), aWheelEvent);
BeginTransaction(aScrollTargetWeakFrame.GetFrame(),
aEventTargetWeakFrame.GetFrame(), aWheelEvent);
} else {
UpdateTransaction(aWheelEvent);
}
@ -214,7 +234,7 @@ bool WheelTransaction::WillHandleDefaultAction(
// UpdateTransaction() fires MozMouseScrollFailed event which is for
// automated testing. In the event handler, the target frame might be
// destroyed. Then, the caller shouldn't try to handle the default action.
if (!aTargetWeakFrame.IsAlive()) {
if (!aScrollTargetWeakFrame.IsAlive()) {
EndTransaction();
return false;
}
@ -224,7 +244,7 @@ bool WheelTransaction::WillHandleDefaultAction(
/* static */
void WheelTransaction::OnEvent(WidgetEvent* aEvent) {
if (!sTargetFrame) {
if (!sScrollTargetFrame) {
return;
}
@ -255,13 +275,17 @@ void WheelTransaction::OnEvent(WidgetEvent* aEvent) {
// terminate the scrollwheel transaction.
LayoutDeviceIntPoint pt = GetScreenPoint(mouseEvent);
auto r = LayoutDeviceIntRect::FromAppUnitsToNearest(
sTargetFrame->GetScreenRectInAppUnits(),
sTargetFrame->PresContext()->AppUnitsPerDevPixel());
sScrollTargetFrame->GetScreenRectInAppUnits(),
sScrollTargetFrame->PresContext()->AppUnitsPerDevPixel());
if (!r.Contains(pt)) {
EndTransaction();
return;
}
// For mouse move events where the wheel transaction is still valid, the
// stored event target should be reset.
sEventTargetFrame = nullptr;
// If the cursor is moving inside the frame, and it is less than
// ignoremovedelay milliseconds since the last scroll operation, ignore
// the mouse move; otherwise, record the current mouse move time to be
@ -291,35 +315,55 @@ void WheelTransaction::OnEvent(WidgetEvent* aEvent) {
}
}
/* static */
void WheelTransaction::OnRemoveElement(nsIContent* aContent) {
// If dom.event.wheel-event-groups.enabled is not set or we have no current
// wheel event transaction there is no internal state to be updated.
if (!sEventTargetFrame) {
return;
}
if (sEventTargetFrame->GetContent() == aContent) {
// Only invalidate the wheel transaction event target frame when the
// remove target is the event target of the wheel event group. The
// scroll target frame of the wheel event group may still be valid.
//
// With the stored event target unset, the target for any following
// events will be the frame found using the input coordinates.
sEventTargetFrame = nullptr;
}
}
/* static */
void WheelTransaction::Shutdown() { NS_IF_RELEASE(sTimer); }
/* static */
void WheelTransaction::OnFailToScrollTarget() {
MOZ_ASSERT(sTargetFrame, "We don't have mouse scrolling transaction");
MOZ_ASSERT(sScrollTargetFrame, "We don't have mouse scrolling transaction");
if (StaticPrefs::test_mousescroll()) {
// This event is used for automated tests, see bug 442774.
nsContentUtils::DispatchEventOnlyToChrome(
sTargetFrame->GetContent()->OwnerDoc(), sTargetFrame->GetContent(),
u"MozMouseScrollFailed"_ns, CanBubble::eYes, Cancelable::eYes);
sScrollTargetFrame->GetContent()->OwnerDoc(),
sScrollTargetFrame->GetContent(), u"MozMouseScrollFailed"_ns,
CanBubble::eYes, Cancelable::eYes);
}
// The target frame might be destroyed in the event handler, at that time,
// we need to finish the current transaction
if (!sTargetFrame) {
if (!sScrollTargetFrame) {
EndTransaction();
}
}
/* static */
void WheelTransaction::OnTimeout(nsITimer* aTimer, void* aClosure) {
if (!sTargetFrame) {
if (!sScrollTargetFrame) {
// The transaction target was destroyed already
EndTransaction();
return;
}
// Store the sTargetFrame, the variable becomes null in EndTransaction.
nsIFrame* frame = sTargetFrame;
// Store the sScrollTargetFrame, the variable becomes null in EndTransaction.
nsIFrame* frame = sScrollTargetFrame;
// We need to finish current transaction before DOM event firing. Because
// the next DOM event might create strange situation for us.
MayEndTransaction();
@ -388,7 +432,7 @@ double WheelTransaction::ComputeAcceleratedWheelDelta(double aDelta,
/* static */
DeltaValues WheelTransaction::OverrideSystemScrollSpeed(
WidgetWheelEvent* aEvent) {
MOZ_ASSERT(sTargetFrame, "We don't have mouse scrolling transaction");
MOZ_ASSERT(sScrollTargetFrame, "We don't have mouse scrolling transaction");
// If the event doesn't scroll to both X and Y, we don't need to do anything
// here.
@ -446,7 +490,7 @@ void ScrollbarsForWheel::SetActiveScrollTarget(
/* static */
void ScrollbarsForWheel::MayInactivate() {
if (!sOwnWheelTransaction && WheelTransaction::GetTargetFrame()) {
if (!sOwnWheelTransaction && WheelTransaction::GetScrollTargetFrame()) {
WheelTransaction::OwnScrollbars(true);
} else {
Inactivate();

Просмотреть файл

@ -118,23 +118,40 @@ class ScrollbarsForWheel {
class WheelTransaction {
public:
static nsIFrame* GetTargetFrame() { return sTargetFrame; }
/**
* Get the target scroll frame for this wheel transaction. This should
* the the scrollable fame that will scroll for all wheel events in
* this wheel transaction.
*/
static nsIFrame* GetScrollTargetFrame() { return sScrollTargetFrame; }
/*
* The event target to use for all wheel events in this wheel transaction.
* This should be the event target for all wheel events in this wheel
* transaction. Note that this frame will likely be a child of the
* scrollable frame.
*/
static nsIFrame* GetEventTargetFrame() { return sEventTargetFrame; }
static void EndTransaction();
/**
* WillHandleDefaultAction() is called before handling aWheelEvent on
* aTargetFrame.
* aScrollTargetWeakFrame given the event target aEventTargetWeakFrame.
*
* @return false if the caller cannot continue to handle the default
* action. Otherwise, true.
*/
static bool WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent,
AutoWeakFrame& aTargetWeakFrame);
AutoWeakFrame& aScrollTargetWeakFrame,
AutoWeakFrame& aEventTargetWeakFrame);
static bool WillHandleDefaultAction(WidgetWheelEvent* aWheelEvent,
nsIFrame* aTargetFrame) {
AutoWeakFrame targetWeakFrame(aTargetFrame);
return WillHandleDefaultAction(aWheelEvent, targetWeakFrame);
nsIFrame* aScrollTargetFrame,
nsIFrame* aEventTargetFrame) {
AutoWeakFrame scrollTargetWeakFrame(aScrollTargetFrame);
AutoWeakFrame eventTargetWeakFrame(aEventTargetFrame);
return WillHandleDefaultAction(aWheelEvent, scrollTargetWeakFrame,
eventTargetWeakFrame);
}
static void OnEvent(WidgetEvent* aEvent);
static void OnRemoveElement(nsIContent* aContent);
static void Shutdown();
static void OwnScrollbars(bool aOwn);
@ -142,7 +159,8 @@ class WheelTransaction {
static DeltaValues AccelerateWheelDelta(WidgetWheelEvent* aEvent);
protected:
static void BeginTransaction(nsIFrame* aTargetFrame,
static void BeginTransaction(nsIFrame* aScrollTargetFrame,
nsIFrame* aEventTargetFrame,
const WidgetWheelEvent* aEvent);
// Be careful, UpdateTransaction may fire a DOM event, therefore, the target
// frame might be destroyed in the event handler.
@ -157,7 +175,23 @@ class WheelTransaction {
static double ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor);
static bool OutOfTime(uint32_t aBaseTime, uint32_t aThreshold);
static AutoWeakFrame sTargetFrame;
/**
* The scrollable element the current wheel event group is bound to.
*/
static AutoWeakFrame sScrollTargetFrame;
/**
* The initial target of the first wheel event in the wheel event group.
* This frame is typically a child of the scrollable element. The wheel
* event should target the topmost-event-target. For a wheel event
* group, we'll use this target for the entire group.
*
* See https://w3c.github.io/uievents/#topmost-event-target and
* https://w3c.github.io/uievents/#event-type-wheel for details.
*
* Note: this is only populated if dom.event.wheel-event-groups.enabled is
* set.
*/
static AutoWeakFrame sEventTargetFrame;
static uint32_t sTime; // in milliseconds
static uint32_t sMouseMoved; // in milliseconds
static nsITimer* sTimer;

Просмотреть файл

@ -2,9 +2,10 @@
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1013412
https://bugzilla.mozilla.org/show_bug.cgi?id=1168182
-->
<head>
<title>Test for Bug 1013412</title>
<title>Test for Bug 1013412 and 1168182</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
@ -44,9 +45,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1013412
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1013412">Mozilla Bug 1013412</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1168182">Mozilla Bug 1168182</a>
<p id="display"></p>
<div id="content">
<p>Scrolling the page should be async, but scrolling over the dark circle should not scroll the page and instead rotate the white ball.</p>
<p>Scrolling the page should be async and scrolling over the dark circle should scroll the page and avoid rotating the white ball.</p>
<div id="scroller">
<div id="scrollbox">
<div id="circle"></div>
@ -56,8 +58,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1013412
<pre id="test">
<script type="application/javascript">
/** Test for Bug 1013412 **/
var rotation = 0;
var rotationAdjusted = false;
@ -80,7 +80,8 @@ document.getElementById("scrollbox").addEventListener("wheel", function (e) {
var iteration = 0;
function runTest() {
var content = document.getElementById('content');
if (iteration < 300) { // enough iterations that we would scroll to the bottom of 'content'
// enough iterations that we would scroll to the bottom of 'content'
if (iteration < 600 && content.scrollTop != content.scrollTopMax) {
iteration++;
sendWheelAndPaint(content, 100, 10,
{ deltaMode: WheelEvent.DOM_DELTA_LINE,
@ -89,8 +90,8 @@ function runTest() {
return;
}
var scrollbox = document.getElementById('scrollbox');
is(content.scrollTop < content.scrollTopMax, true, "We should not have scrolled to the bottom of the scrollframe");
is(rotationAdjusted, true, "The rotation should have been adjusted");
is(content.scrollTop, content.scrollTopMax, "We should have scrolled to the bottom of the scrollframe");
is(rotationAdjusted, false, "The rotation should not have been adjusted");
SimpleTest.finish();
}
@ -98,7 +99,11 @@ function startTest() {
// If we allow smooth scrolling the "smooth" scrolling may cause the page to
// glide past the scrollbox (which is supposed to stop the scrolling) and so
// we might end up at the bottom of the page.
SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false], ["test.events.async.enabled", true]]}, runTest);
SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false],
["test.events.async.enabled", true],
["mousewheel.transaction.timeout", 100000],
["dom.event.wheel-event-groups.enabled", true]]},
runTest);
}
SimpleTest.waitForExplicitFinish();

Просмотреть файл

@ -2,9 +2,10 @@
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1013412
https://bugzilla.mozilla.org/show_bug.cgi?id=1168182
-->
<head>
<title>Test for Bug 1013412</title>
<title>Test for Bug 1013412 and 1168182</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="/tests/SimpleTest/paint_listener.js"></script>
@ -45,7 +46,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1013412
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1161206">Mozilla Bug 1161206</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1013412">Mozilla Bug 1013412</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1168182">Mozilla Bug 1168182</a>
<p id="display"></p>
<div id="content">
<p>Scrolling the page should be async, but scrolling over the dark circle should not scroll the page and instead rotate the white ball.</p>
@ -79,12 +81,13 @@ document.getElementById("scrollbox").addEventListener("wheel", function(e) {
async function test() {
var content = document.getElementById("content");
for (let i = 0; i < 300; i++) { // enough iterations that we would scroll to the bottom of 'content'
// enough iterations that we would scroll to the bottom of 'content'
for (let i = 0; i < 600 && content.scrollTop != content.scrollTopMax; i++) {
await promiseNativeWheelAndWaitForWheelEvent(content, 100, 150, 0, -5);
}
is(content.scrollTop > 0, true, "We should have scrolled down somewhat");
is(content.scrollTop < content.scrollTopMax, true, "We should not have scrolled to the bottom of the scrollframe");
is(rotationAdjusted, true, "The rotation should have been adjusted");
is(content.scrollTop, content.scrollTopMax, "We should have scrolled to the bottom of the scrollframe");
is(rotationAdjusted, false, "The rotation should not have been adjusted");
}
SimpleTest.waitForExplicitFinish();
@ -92,7 +95,9 @@ SimpleTest.waitForExplicitFinish();
// If we allow smooth scrolling the "smooth" scrolling may cause the page to
// glide past the scrollbox (which is supposed to stop the scrolling) and so
// we might end up at the bottom of the page.
pushPrefs([["general.smoothScroll", false]])
pushPrefs([["general.smoothScroll", false],
["mousewheel.transaction.timeout", 100000],
["dom.event.wheel-event-groups", true]])
.then(waitUntilApzStable)
.then(test)
.then(SimpleTest.finish, SimpleTest.finishWithFailure);

Просмотреть файл

@ -48,12 +48,19 @@ async function scrollWheelOver(element, deltaY) {
async function test() {
var outer = document.getElementById("outer-frame");
var inner = document.getElementById("inner-frame");
var innerContent = document.getElementById("inner-content");
// Register a wheel event listener that records the target of
// the last wheel event, so that we can make assertions about it.
var lastWheelTarget;
var wheelTargetRecorder = function(e) { lastWheelTarget = e.target; };
let lastWheelTarget;
let firstWheelTarget;
let wheelEventOccurred = false;
var wheelTargetRecorder = function(e) {
if (!wheelEventOccurred) {
firstWheelTarget = e.target;
wheelEventOccurred = true;
}
lastWheelTarget = e.target;
};
window.addEventListener("wheel", wheelTargetRecorder);
// Scroll |outer| to the bottom.
@ -61,8 +68,8 @@ async function test() {
await scrollWheelOver(outer, -10);
}
// Verify that this has brought |inner| under the wheel.
is(lastWheelTarget, innerContent, "'inner-content' should have been brought under the wheel");
is(lastWheelTarget, firstWheelTarget,
"target " + lastWheelTarget.id + " should be " + lastWheelTarget.id);
window.removeEventListener("wheel", wheelTargetRecorder);
// Immediately after, scroll it back up a bit.
@ -129,7 +136,9 @@ SimpleTest.waitForExplicitFinish();
// inputs since this test is specifically testing things related to wheel
// transactions.
pushPrefs([["general.smoothScroll", false],
["apz.test.mac.synth_wheel_input", true]])
["apz.test.mac.synth_wheel_input", true],
["mousewheel.transaction.timeout", 1500],
["dom.event.wheel-event-groups", true]])
.then(waitUntilApzStable)
.then(test)
.then(SimpleTest.finish, SimpleTest.finishWithFailure);

Просмотреть файл

@ -7099,6 +7099,11 @@ nsresult PresShell::EventHandler::HandleEventUsingCoordinates(
return NS_OK;
}
// Wheel events only apply to elements. If this is a wheel event, attempt to
// update the event target from the current wheel transaction before we
// compute the element from the target frame.
eventTargetData.UpdateWheelEventTarget(aGUIEvent);
if (!eventTargetData.ComputeElementFromFrame(aGUIEvent)) {
return NS_OK;
}
@ -11813,6 +11818,34 @@ bool PresShell::EventHandler::EventTargetData::ComputeElementFromFrame(
return !!mContent;
}
void PresShell::EventHandler::EventTargetData::UpdateWheelEventTarget(
WidgetGUIEvent* aGUIEvent) {
MOZ_ASSERT(aGUIEvent);
if (aGUIEvent->mMessage != eWheel) {
return;
}
// If dom.event.wheel-event-groups.enabled is not set or the stored
// event target is removed, we will not get a event target frame from the
// wheel transaction here.
nsIFrame* groupFrame = WheelTransaction::GetEventTargetFrame();
if (!groupFrame) {
return;
}
// If the browsing context is no longer the same as the context of the
// current wheel transaction, do not override the event target.
if (!groupFrame->PresContext() || !groupFrame->PresShell() ||
groupFrame->PresContext() != GetPresContext()) {
return;
}
// If dom.event.wheel-event-groups.enabled is set and whe have a stored
// event target from the wheel transaction, override the event target.
SetFrameAndComputePresShellAndContent(groupFrame, aGUIEvent);
}
void PresShell::EventHandler::EventTargetData::UpdateTouchEventTarget(
WidgetGUIEvent* aGUIEvent) {
MOZ_ASSERT(aGUIEvent);

Просмотреть файл

@ -2209,6 +2209,16 @@ class PresShell final : public nsStubDocumentObserver,
*/
void UpdateTouchEventTarget(WidgetGUIEvent* aGUIEvent);
/**
* UpdateWheelEventTarget() updates mFrame, mPresShell, and mContent if
* aGUIEvent is a wheel event and aGUIEvent should be grouped with prior
* wheel events.
*
* @param aGUIEvent The handled event. If it's not a wheel event,
* this method does nothing.
*/
void UpdateWheelEventTarget(WidgetGUIEvent* aGUIEvent);
RefPtr<PresShell> mPresShell;
nsIFrame* mFrame = nullptr;
nsCOMPtr<nsIContent> mContent;

Просмотреть файл

@ -2513,6 +2513,13 @@
value: @IS_NOT_NIGHTLY_BUILD@
mirror: always
# Whether wheel event target's should be grouped. When enabled, all wheel
# events that occur in a given wheel transaction have the same event target.
- name: dom.event.wheel-event-groups.enabled
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: always
# Whether WheelEvent should return pixels instead of lines for
# WheelEvent.deltaX/Y/Z, when deltaMode hasn't been checked.
#

Просмотреть файл

@ -1,2 +1,2 @@
prefs: [apz.scrollend-event.content.enabled:true]
prefs: [apz.scrollend-event.content.enabled:true, dom.event.wheel-event-groups.enabled:true, mousewheel.transaction.timeout:500]
lsan-allowed: [Alloc, MakeUnique, Malloc, Realloc, XPCNativeInterface::NewInstance, XPCNativeSet::NewInstance, XPCNativeSet::NewInstanceMutate, XPCWrappedNative::GetNewOrUsed, XPCWrappedNativeProto::GetNewOrUsed, mozilla::dom::WebExtensionInit::Init, mozilla::extensions::MatchPatternCore::MatchPatternCore, mozilla::extensions::MatchPatternSet::Constructor, mozilla::extensions::MatchPatternSet::GetPatterns, mozilla::extensions::ParseGlobs, mozilla::extensions::PermittedSchemes, mozilla::extensions::WebExtensionPolicy::Constructor, mozilla::extensions::WebExtensionPolicy::WebExtensionPolicy, mozilla::extensions::WebExtensionPolicyCore::WebExtensionPolicyCore, mozilla::net::nsStandardURL::TemplatedMutator, nsDynamicAtom::Create, nsJARURI::Mutator::SetSpecBaseCharset]

Просмотреть файл

@ -277,7 +277,13 @@ async function prepareRunningTests()
function* testBody()
{
yield* testRichListbox("richlistbox");
// Perform a mousedown to ensure the wheel transaction from the previous test
// does not impact the next test.
synthesizeMouse(document.scrollingElement, 0, 0, {type: "mousedown"}, window);
yield* testArrowScrollbox("hscrollbox");
synthesizeMouse(document.scrollingElement, -1, -1, {type: "mousedown"}, window);
yield* testArrowScrollbox("vscrollbox");
}