Bug 1392460 - Add a couple prefs to expose DOM_DELTA_PIXEL rather than DOM_DELTA_LINES to the web. r=masayuki,smaug

And use them to disable DOM_DELTA_LINES on Nightly builds, to see the
impact, since this is pretty hard to measure just with telemetry.

Differential Revision: https://phabricator.services.mozilla.com/D95388
This commit is contained in:
Emilio Cobos Álvarez 2020-11-07 16:12:34 +00:00
Родитель 771dd03229
Коммит 0d187533ef
6 изменённых файлов: 163 добавлений и 35 удалений

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

@ -18,6 +18,11 @@ WheelEvent::WheelEvent(EventTarget* aOwner, nsPresContext* aPresContext,
? aWheelEvent
: new WidgetWheelEvent(false, eVoidEvent, nullptr)),
mAppUnitsPerDevPixel(0) {
if (StaticPrefs::dom_event_wheel_deltaMode_lines_always_disabled()) {
mDeltaModeCheckingState = DeltaModeCheckingState::Unchecked;
}
if (aWheelEvent) {
mEventIsInternal = false;
// If the delta mode is pixel, the WidgetWheelEvent's delta values are in
@ -55,31 +60,55 @@ void WheelEvent::InitWheelEvent(
wheelEvent->mDeltaMode = aDeltaMode;
}
double WheelEvent::DeltaX() {
if (!mAppUnitsPerDevPixel) {
return mEvent->AsWheelEvent()->mDeltaX;
double WheelEvent::ToWebExposedDelta(const WidgetWheelEvent& aWidgetEvent,
double aDelta, CallerType aCallerType) {
if (aCallerType != CallerType::System) {
if (mDeltaModeCheckingState == DeltaModeCheckingState::Unknown) {
mDeltaModeCheckingState = DeltaModeCheckingState::Unchecked;
}
if (mDeltaModeCheckingState == DeltaModeCheckingState::Unchecked &&
aWidgetEvent.mDeltaMode == WheelEvent_Binding::DOM_DELTA_LINE &&
StaticPrefs::dom_event_wheel_deltaMode_lines_disabled()) {
// TODO(emilio, bug 1675949): Consider not using a fixed multiplier here?
return aDelta *
StaticPrefs::dom_event_wheel_deltaMode_lines_to_pixel_scale();
}
}
return mEvent->AsWheelEvent()->mDeltaX * mAppUnitsPerDevPixel /
AppUnitsPerCSSPixel();
if (!mAppUnitsPerDevPixel) {
return aDelta;
}
return aDelta * mAppUnitsPerDevPixel / AppUnitsPerCSSPixel();
}
double WheelEvent::DeltaY() {
if (!mAppUnitsPerDevPixel) {
return mEvent->AsWheelEvent()->mDeltaY;
}
return mEvent->AsWheelEvent()->mDeltaY * mAppUnitsPerDevPixel /
AppUnitsPerCSSPixel();
double WheelEvent::DeltaX(CallerType aCallerType) {
WidgetWheelEvent* ev = mEvent->AsWheelEvent();
return ToWebExposedDelta(*ev, ev->mDeltaX, aCallerType);
}
double WheelEvent::DeltaZ() {
if (!mAppUnitsPerDevPixel) {
return mEvent->AsWheelEvent()->mDeltaZ;
}
return mEvent->AsWheelEvent()->mDeltaZ * mAppUnitsPerDevPixel /
AppUnitsPerCSSPixel();
double WheelEvent::DeltaY(CallerType aCallerType) {
WidgetWheelEvent* ev = mEvent->AsWheelEvent();
return ToWebExposedDelta(*ev, ev->mDeltaY, aCallerType);
}
uint32_t WheelEvent::DeltaMode() { return mEvent->AsWheelEvent()->mDeltaMode; }
double WheelEvent::DeltaZ(CallerType aCallerType) {
WidgetWheelEvent* ev = mEvent->AsWheelEvent();
return ToWebExposedDelta(*ev, ev->mDeltaZ, aCallerType);
}
uint32_t WheelEvent::DeltaMode(CallerType aCallerType) {
uint32_t mode = mEvent->AsWheelEvent()->mDeltaMode;
if (aCallerType != CallerType::System) {
if (mDeltaModeCheckingState == DeltaModeCheckingState::Unknown) {
mDeltaModeCheckingState = DeltaModeCheckingState::Checked;
} else if (mDeltaModeCheckingState == DeltaModeCheckingState::Unchecked &&
mode == WheelEvent_Binding::DOM_DELTA_LINE &&
StaticPrefs::dom_event_wheel_deltaMode_lines_disabled()) {
return WheelEvent_Binding::DOM_DELTA_PIXEL;
}
}
return mode;
}
already_AddRefed<WheelEvent> WheelEvent::Constructor(
const GlobalObject& aGlobal, const nsAString& aType,

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

@ -33,10 +33,10 @@ class WheelEvent : public MouseEvent {
// NOTE: DeltaX(), DeltaY() and DeltaZ() return CSS pixels when deltaMode is
// DOM_DELTA_PIXEL. (The internal event's delta values are device pixels
// if it's dispatched by widget)
double DeltaX();
double DeltaY();
double DeltaZ();
uint32_t DeltaMode();
double DeltaX(CallerType);
double DeltaY(CallerType);
double DeltaZ(CallerType);
uint32_t DeltaMode(CallerType);
void InitWheelEvent(const nsAString& aType, bool aCanBubble, bool aCancelable,
nsGlobalWindowInner* aView, int32_t aDetail,
@ -49,8 +49,26 @@ class WheelEvent : public MouseEvent {
protected:
~WheelEvent() = default;
double ToWebExposedDelta(const WidgetWheelEvent&, double aDelta, CallerType);
private:
int32_t mAppUnitsPerDevPixel;
enum class DeltaModeCheckingState : uint8_t {
// Neither deltaMode nor the delta values have been accessed.
Unknown,
// The delta values have been accessed, without checking deltaMode first.
Unchecked,
// The deltaMode has been checked.
Checked,
};
// For compat reasons, we might expose a DOM_DELTA_LINE event as
// DOM_DELTA_PIXEL instead. Whether we do that depends on whether the event
// has been asked for the deltaMode before the deltas. If it has, we assume
// that the page will correctly handle DOM_DELTA_LINE. This variable tracks
// that state. See bug 1392460.
DeltaModeCheckingState mDeltaModeCheckingState =
DeltaModeCheckingState::Unknown;
};
} // namespace dom

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

@ -2494,6 +2494,26 @@ function* testContinuousTrustedEvents()
horizontal: { expected: true, preventDefault: true, detail: gHorizontalLine },
vertical: { expected: true, preventDefault: true, detail: gLineHeight } },
},
{ description: "modifier key tests without content checking mode (alt, line)",
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
isCustomizedByPrefs: false,
shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
wheel: {
expected: true, preventDefault: false,
skipDeltaModeCheck: true,
deltaX: SpecialPowers.getIntPref("dom.event.wheel-deltaMode-lines-to-pixel-scale"),
deltaY: SpecialPowers.getIntPref("dom.event.wheel-deltaMode-lines-to-pixel-scale"),
deltaZ: SpecialPowers.getIntPref("dom.event.wheel-deltaMode-lines-to-pixel-scale"),
},
DOMMouseScroll: {
horizontal: { expected: true, preventDefault: false, detail: 1 },
vertical: { expected: true, preventDefault: false, detail: 1 } },
MozMousePixelScroll: {
horizontal: { expected: true, preventDefault: true, detail: gHorizontalLine },
vertical: { expected: true, preventDefault: true, detail: gLineHeight } },
},
{ description: "modifier key tests (alt, page)",
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
@ -3058,14 +3078,24 @@ function* testContinuousTrustedEvents()
is(aEvent.target, gScrolledElement,
description + "target was invalid");
is(aEvent.deltaMode, currentWheelEventTest.event.deltaMode,
description + "deltaMode was invalid");
is(aEvent.deltaX, currentWheelEventTest.wheel.deltaX,
description + "deltaX was invalid");
is(aEvent.deltaY, currentWheelEventTest.wheel.deltaY,
description + "deltaY was invalid");
is(aEvent.deltaZ, currentWheelEventTest.wheel.deltaZ,
description + "deltaZ was invalid");
if (!currentWheelEventTest.wheel.skipDeltaModeCheck) {
is(aEvent.deltaMode, currentWheelEventTest.event.deltaMode,
description + "deltaMode was invalid");
}
is(SpecialPowers.wrap(aEvent).deltaMode, currentWheelEventTest.event.deltaMode,
description + "deltaMode is raw value from privileged script");
for (let prop of ["deltaX", "deltaY", "deltaZ"]) {
is(aEvent[prop], currentWheelEventTest.wheel[prop],
description + prop + " was invalid");
if (currentWheelEventTest.wheel.skipDeltaModeCheck) {
is(aEvent.deltaMode, WheelEvent.DOM_DELTA_PIXEL,
description + "deltaMode should become pixels for line scrolling if unchecked by content")
if (aEvent[prop] != 0) {
isnot(aEvent[prop], SpecialPowers.wrap(aEvent)[prop],
description + "should keep returning raw value for privileged script");
}
}
}
is(aEvent.shiftKey, currentWheelEventTest.event.shiftKey,
description + "shiftKey was invalid");
is(aEvent.ctrlKey, currentWheelEventTest.event.ctrlKey,
@ -3225,6 +3255,12 @@ function* testBody()
function runTests()
{
SpecialPowers.pushPrefEnv({"set": [
// FIXME(emilio): This test is broken in HiDPI, unclear if
// MozMousePixelScroll is not properly converting to CSS pixels, or
// whether sendWheelAndWait expectes device rather than CSS pixels, or
// something else.
["layout.css.devPixelsPerPx", 1.0],
["mousewheel.transaction.timeout", 100000],
["mousewheel.default.delta_multiplier_x", 100],
["mousewheel.default.delta_multiplier_y", 100],

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

@ -183,6 +183,8 @@ function* testDeltaMultiplierPrefs()
deltaX: -gHorizontalLine, deltaY: -gLineHeight, deltaZ: -gLineHeight, lineOrPageDeltaX: -1, lineOrPageDeltaY: -1 },
{ deltaMode: WheelEvent.DOM_DELTA_LINE,
deltaX: -1.0, deltaY: -1.0, deltaZ: -1.0, lineOrPageDeltaX: -1, lineOrPageDeltaY: -1 },
{ deltaMode: WheelEvent.DOM_DELTA_LINE, skipDeltaModeCheck: true,
deltaX: -1.0, deltaY: -1.0, deltaZ: -1.0, lineOrPageDeltaX: -1, lineOrPageDeltaY: -1 },
{ deltaMode: WheelEvent.DOM_DELTA_PAGE,
deltaX: -1.0, deltaY: -1.0, deltaZ: -1.0, lineOrPageDeltaX: -1, lineOrPageDeltaY: -1 },
];
@ -222,9 +224,20 @@ function* testDeltaMultiplierPrefs()
break;
}
}
if (currentEvent.skipDeltaModeCheck) {
let linesToPixel = SpecialPowers.getIntPref("dom.event.wheel-deltaMode-lines-to-pixel-scale");
expectedDeltaX *= linesToPixel;
expectedDeltaY *= linesToPixel;
expectedDeltaZ *= linesToPixel;
} else {
is(aEvent.deltaMode, currentEvent.deltaMode, description + "deltaMode (" + currentEvent.deltaMode + ") was invalid");
}
is(aEvent.deltaX, expectedDeltaX, description + "deltaX (" + currentEvent.deltaX + ") was invalid");
is(aEvent.deltaY, expectedDeltaY, description + "deltaY (" + currentEvent.deltaY + ") was invalid");
is(aEvent.deltaZ, expectedDeltaZ, description + "deltaZ (" + currentEvent.deltaZ + ") was invalid");
if (currentEvent.skipDeltaModeCheck) {
isnot(SpecialPowers.wrap(aEvent).deltaMode, aEvent.deltaMode, description + "deltaMode should be changed for content if unchecked");
}
if (expectedAsyncHandlerCalls > 0 && --expectedAsyncHandlerCalls == 0) {
setTimeout(continueTest, 0);
@ -785,6 +798,12 @@ function continueTest()
function runTest()
{
SpecialPowers.pushPrefEnv({"set": [
// FIXME(emilio): This test is broken in HiDPI, unclear if
// MozMousePixelScroll is not properly converting to CSS pixels, or
// whether sendWheelAndWait expectes device rather than CSS pixels, or
// something else.
["layout.css.devPixelsPerPx", 1.0],
["mousewheel.default.delta_multiplier_x", 100],
["mousewheel.default.delta_multiplier_y", 100],
["mousewheel.default.delta_multiplier_z", 100],

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

@ -19,11 +19,10 @@ interface WheelEvent : MouseEvent
const unsigned long DOM_DELTA_LINE = 0x01;
const unsigned long DOM_DELTA_PAGE = 0x02;
readonly attribute double deltaX;
readonly attribute double deltaY;
readonly attribute double deltaZ;
readonly attribute unsigned long deltaMode;
[NeedsCallerType] readonly attribute double deltaX;
[NeedsCallerType] readonly attribute double deltaY;
[NeedsCallerType] readonly attribute double deltaZ;
[NeedsCallerType] readonly attribute unsigned long deltaMode;
};
dictionary WheelEventInit : MouseEventInit

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

@ -1670,6 +1670,33 @@
value: true
mirror: always
# Whether WheelEvent should return pixels instead of lines for
# WheelEvent.deltaX/Y/Z, when deltaMode hasn't been checked.
#
# Other browsers don't use line deltas and websites forget to check for it, see
# bug 1392460.
- name: dom.event.wheel-deltaMode-lines.disabled
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: always
# Mostly for debugging. Whether we should do the same as
# dom.event.wheel-deltaMode-lines.disabled, but unconditionally rather than
# only when deltaMode hasn't been checked.
- name: dom.event.wheel-deltaMode-lines.always-disabled
type: bool
value: false
mirror: always
# When dom.event.wheel-deltaMode-lines.disabled is true, this is the lines to
# pixels multiplier that gets used.
#
# The value here is taken from PIXELS_PER_LINE_SCALE from pdf.js.
- name: dom.event.wheel-deltaMode-lines-to-pixel-scale
type: uint32_t
value: 30
mirror: always
#if defined(XP_MACOSX)
# Whether to disable treating ctrl click as right click
- name: dom.event.treat_ctrl_click_as_right_click.disabled