Bug 1749825 - Make it possible for privileged script to opt elements into mousewheel autodir/honour root behaviour. r=botond

Differential Revision: https://phabricator.services.mozilla.com/D135914
This commit is contained in:
Mike Conley 2022-01-21 16:12:48 +00:00
parent 1556e8b1ef
commit 038eb21cc2
9 changed files with 183 additions and 12 deletions

View File

@ -427,6 +427,16 @@ nsDOMWindowUtils::GetViewportFitInfo(nsAString& aViewportFit) {
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::SetMousewheelAutodir(Element* aElement, bool aEnabled,
bool aHonourRoot) {
aElement->SetProperty(nsGkAtoms::forceMousewheelAutodir,
reinterpret_cast<void*>(aEnabled));
aElement->SetProperty(nsGkAtoms::forceMousewheelAutodirHonourRoot,
reinterpret_cast<void*>(aHonourRoot));
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::SetDisplayPortForElement(float aXPx, float aYPx,
float aWidthPx, float aHeightPx,

View File

@ -2399,6 +2399,104 @@ function doTestAutoDirScroll2(aSettings, aAutoDirTrait,
shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
adjusted: true,
expected: kAdjustedForLeft.result,
cleanup (cb) {
SpecialPowers.pushPrefEnv({"set":
[["mousewheel.autodir.enabled",
false]]},
cb);
} },
// Tests: Test that autodir scrolling can be force-enabled using windowUtils.
// This only tests vertical wheel scrolls being adjusted to be
// horizontal, rather than re-testing all autodir behaviours just for
// this way of enabling it.
// Results: Vertical wheel scrolls are adjusted to be horizontal whereas the
// horizontal wheel scrolls are unadjusted.
// Reason: Auto-dir adjustment applies to a target if the target overflows
// in only one direction and the direction is orthogonal to the
// wheel and deltaZ is zero.
{ description: "force-enabled auto-dir scroll to " + kAdjustedForDown.desc +
"(originally bottom) by pixel scroll even if lineOrPageDelta is 0, " +
"no vertical scrollbar",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
adjusted: true,
expected: kAdjustedForDown.result,
prepare (cb) {
gScrollableElement.style.overflowX = "auto";
gScrollableElement.style.overflowY = "hidden";
resetScrollPosition(gScrollableElement);
winUtils.setMousewheelAutodir(gScrollableElement, true, kHonoursRoot)
cb();
} },
{ description: "force-enabled auto-dir scroll to " + kAdjustedForDown.desc +
"(originally bottom) by pixel scroll when lineOrPageDelta is 1, " +
"no vertical scrollbar",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
adjusted: true,
expected: kAdjustedForDown.result },
{ description: "force-enabled auto-dir scroll to " + kAdjustedForUp.desc +
"(originally top) by pixel scroll even if lineOrPageDelta is 0, " +
"no vertical scrollbar",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
adjusted: true,
expected: kAdjustedForUp.result },
{ description: "force-enabled auto-dir scroll to " + kAdjustedForUp.desc +
"(originally top) by pixel scroll when lineOrPageDelta is -1, " +
"no vertical scrollbar",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
adjusted: true,
expected: kAdjustedForUp.result },
{ description: "force-enabled auto-dir scroll to right by pixel scroll even if lineOrPageDelta is 0, " +
"no vertical scrollbar",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
adjusted: false,
expected: kScrollRight },
{ description: "force-enabled auto-dir scroll to right by pixel scroll when lineOrPageDelta is 1, " +
"no vertical scrollbar",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
adjusted: false,
expected: kScrollRight },
{ description: "force-enabled auto-dir scroll to left by pixel scroll even if lineOrPageDelta is 0, " +
"no vertical scrollbar",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
adjusted: false,
expected: kScrollLeft },
{ description: "force-enabled auto-dir scroll to left by pixel scroll when lineOrPageDelta is -1, " +
"no vertical scrollbar",
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, osKey: false },
adjusted: false,
expected: kScrollLeft,
cleanup (cb) {
gScrollableElement.style.position = "static";
gScrollableElement.style.top = "auto";
@ -2406,11 +2504,9 @@ function doTestAutoDirScroll2(aSettings, aAutoDirTrait,
gScrollableElement.style.overflow = "auto";
Object.assign(document.body.style, kOldStyleForRoot);
Object.assign(gScrollableElement.style, kOldStyleForTarget);
SpecialPowers.pushPrefEnv({"set":
[["mousewheel.autodir.enabled",
false]]},
cb);
} },
winUtils.setMousewheelAutodir(gScrollableElement, false, false);
cb();
} },
];
let styleDescForRoot = "";

View File

@ -126,6 +126,17 @@ interface nsIDOMWindowUtils : nsISupports {
*/
void getContentViewerSize(out uint32_t aDisplayWidth, out uint32_t aDisplayHeight);
/**
* For any scrollable element, this allows you to override the default
* scroll behaviour and force autodir (which allows a mousewheel to
* horizontally scroll regions that only scroll on that one axis).
*
* See the documentation for mousewheel.autodir.enabled and
* mousewheel.autodir.honourroot for a more thorough explanation of
* what these behaviours do.
*/
void setMousewheelAutodir(in Element aElement, in boolean aEnabled, in boolean aHonourRoot);
/**
* For any scrollable element, this allows you to override the
* visible region and draw more than what is visible, which is

View File

@ -806,6 +806,8 @@ struct ScrollMetadata {
mIsRDMTouchSimulationActive(false),
mDidContentGetPainted(true),
mPrefersReducedMotion(false),
mForceMousewheelAutodir(false),
mForceMousewheelAutodirHonourRoot(false),
mOverscrollBehavior() {}
bool operator==(const ScrollMetadata& aOther) const {
@ -823,6 +825,9 @@ struct ScrollMetadata {
mIsRDMTouchSimulationActive == aOther.mIsRDMTouchSimulationActive &&
mDidContentGetPainted == aOther.mDidContentGetPainted &&
mPrefersReducedMotion == aOther.mPrefersReducedMotion &&
mForceMousewheelAutodir == aOther.mForceMousewheelAutodir &&
mForceMousewheelAutodirHonourRoot ==
aOther.mForceMousewheelAutodirHonourRoot &&
mDisregardedDirection == aOther.mDisregardedDirection &&
mOverscrollBehavior == aOther.mOverscrollBehavior &&
mScrollUpdates == aOther.mScrollUpdates;
@ -897,6 +902,18 @@ struct ScrollMetadata {
void SetPrefersReducedMotion(bool aValue) { mPrefersReducedMotion = aValue; }
bool PrefersReducedMotion() const { return mPrefersReducedMotion; }
void SetForceMousewheelAutodir(bool aValue) {
mForceMousewheelAutodir = aValue;
}
bool ForceMousewheelAutodir() const { return mForceMousewheelAutodir; }
void SetForceMousewheelAutodirHonourRoot(bool aValue) {
mForceMousewheelAutodirHonourRoot = aValue;
}
bool ForceMousewheelAutodirHonourRoot() const {
return mForceMousewheelAutodirHonourRoot;
}
bool DidContentGetPainted() const { return mDidContentGetPainted; }
private:
@ -1007,6 +1024,11 @@ struct ScrollMetadata {
// media query).
bool mPrefersReducedMotion : 1;
// Whether privileged code has requested that autodir behaviour be
// enabled for the scroll frame.
bool mForceMousewheelAutodir : 1;
bool mForceMousewheelAutodirHonourRoot : 1;
// The disregarded direction means the direction which is disregarded anyway,
// even if the scroll frame overflows in that direction and the direction is
// specified as scrollable. This could happen in some scenarios, for instance,

View File

@ -2128,12 +2128,13 @@ bool AsyncPanZoomController::CanScroll(const InputData& aEvent) const {
// to checking if it is scrollable without adjusting its delta.
// 2. For a non-auto-dir scroll, simply check if it is scrollable without
// adjusting its delta.
if (scrollWheelInput.IsAutoDir()) {
if (scrollWheelInput.IsAutoDir(mScrollMetadata.ForceMousewheelAutodir())) {
RecursiveMutexAutoLock lock(mRecursiveMutex);
auto deltaX = scrollWheelInput.mDeltaX;
auto deltaY = scrollWheelInput.mDeltaY;
bool isRTL =
IsContentOfHonouredTargetRightToLeft(scrollWheelInput.HonoursRoot());
IsContentOfHonouredTargetRightToLeft(scrollWheelInput.HonoursRoot(
mScrollMetadata.ForceMousewheelAutodirHonourRoot()));
APZAutoDirWheelDeltaAdjuster adjuster(deltaX, deltaY, mX, mY, isRTL);
if (adjuster.ShouldBeAdjusted()) {
// If we detect that the delta values should be adjusted for an auto-dir
@ -2288,11 +2289,12 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(
auto deltaX = aEvent.mDeltaX;
auto deltaY = aEvent.mDeltaY;
ParentLayerPoint delta;
if (aEvent.IsAutoDir()) {
if (aEvent.IsAutoDir(mScrollMetadata.ForceMousewheelAutodir())) {
// It's an auto-dir scroll, so check if its delta should be adjusted, if so,
// adjust it.
RecursiveMutexAutoLock lock(mRecursiveMutex);
bool isRTL = IsContentOfHonouredTargetRightToLeft(aEvent.HonoursRoot());
bool isRTL = IsContentOfHonouredTargetRightToLeft(
aEvent.HonoursRoot(mScrollMetadata.ForceMousewheelAutodirHonourRoot()));
APZAutoDirWheelDeltaAdjuster adjuster(deltaX, deltaY, mX, mY, isRTL);
if (adjuster.ShouldBeAdjusted()) {
adjuster.Adjust();
@ -5048,6 +5050,10 @@ void AsyncPanZoomController::NotifyLayersUpdated(
aScrollMetadata.GetIsRDMTouchSimulationActive());
mScrollMetadata.SetPrefersReducedMotion(
aScrollMetadata.PrefersReducedMotion());
mScrollMetadata.SetForceMousewheelAutodir(
aScrollMetadata.ForceMousewheelAutodir());
mScrollMetadata.SetForceMousewheelAutodirHonourRoot(
aScrollMetadata.ForceMousewheelAutodirHonourRoot());
mScrollMetadata.SetDisregardedDirection(
aScrollMetadata.GetDisregardedDirection());
mScrollMetadata.SetOverscrollBehavior(

View File

@ -434,6 +434,8 @@ struct ParamTraits<mozilla::layers::ScrollMetadata>
WriteParam(aMsg, aParam.mIsRDMTouchSimulationActive);
WriteParam(aMsg, aParam.mDidContentGetPainted);
WriteParam(aMsg, aParam.mPrefersReducedMotion);
WriteParam(aMsg, aParam.mForceMousewheelAutodir);
WriteParam(aMsg, aParam.mForceMousewheelAutodirHonourRoot);
WriteParam(aMsg, aParam.mDisregardedDirection);
WriteParam(aMsg, aParam.mOverscrollBehavior);
WriteParam(aMsg, aParam.mScrollUpdates);
@ -474,6 +476,11 @@ struct ParamTraits<mozilla::layers::ScrollMetadata>
&paramType::SetDidContentGetPainted) &&
ReadBoolForBitfield(aMsg, aIter, aResult,
&paramType::SetPrefersReducedMotion) &&
ReadBoolForBitfield(aMsg, aIter, aResult,
&paramType::SetForceMousewheelAutodir) &&
ReadBoolForBitfield(
aMsg, aIter, aResult,
&paramType::SetForceMousewheelAutodirHonourRoot) &&
ReadParam(aMsg, aIter, &aResult->mDisregardedDirection) &&
ReadParam(aMsg, aIter, &aResult->mOverscrollBehavior) &&
ReadParam(aMsg, aIter, &aResult->mScrollUpdates);

View File

@ -8570,6 +8570,18 @@ ScrollMetadata nsLayoutUtils::ComputeScrollMetadata(
}
}
// Note: GetProperty() will return nullptr both in the case where
// the property hasn't been set, and in the case where the property
// has been set to false (in which case the property value is
// `reinterpret_cast<void*>(false)` which is nullptr.
if (aContent->GetProperty(nsGkAtoms::forceMousewheelAutodir)) {
metadata.SetForceMousewheelAutodir(true);
}
if (aContent->GetProperty(nsGkAtoms::forceMousewheelAutodirHonourRoot)) {
metadata.SetForceMousewheelAutodirHonourRoot(true);
}
if (IsAPZTestLoggingEnabled()) {
LogTestDataForPaint(aLayerManager, scrollId, "displayport",
metrics.GetDisplayPort());

View File

@ -698,7 +698,11 @@ class ScrollWheelInput : public InputData {
// The following two functions are for auto-dir scrolling. For detailed
// information on auto-dir, @see mozilla::WheelDeltaAdjustmentStrategy
bool IsAutoDir() const {
bool IsAutoDir(bool aForce = false) const {
if (aForce) {
return true;
}
switch (mWheelDeltaAdjustmentStrategy) {
case WheelDeltaAdjustmentStrategy::eAutoDir:
case WheelDeltaAdjustmentStrategy::eAutoDirWithRootHonour:
@ -717,9 +721,10 @@ class ScrollWheelInput : public InputData {
// not an auto-dir scroll.
// For detailed information on auto-dir,
// @see mozilla::WheelDeltaAdjustmentStrategy
bool HonoursRoot() const {
bool HonoursRoot(bool aForce = false) const {
return WheelDeltaAdjustmentStrategy::eAutoDirWithRootHonour ==
mWheelDeltaAdjustmentStrategy;
mWheelDeltaAdjustmentStrategy ||
aForce;
}
// Warning, this class is serialized and sent over IPC. Any change to its

View File

@ -2202,6 +2202,8 @@ STATIC_ATOMS = [
Atom("DisplayPortMargins", "_displayportmargins"),
Atom("DisplayPortBase", "_displayportbase"),
Atom("MinimalDisplayPort", "_minimaldisplayport"),
Atom("forceMousewheelAutodir", "_force_mousewheel_autodir"),
Atom("forceMousewheelAutodirHonourRoot", "_force_mousewheel_autodir_honourroot"),
Atom("forcemessagemanager", "forcemessagemanager"),
Atom("initialBrowsingContextGroupId", "initialBrowsingContextGroupId"),
Atom("initiallyactive", "initiallyactive"),