From 17d75f38f0aded8cbde262b0c22f84d6904b97b1 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 20 Aug 2012 01:46:21 +0900 Subject: [PATCH] Bug 422132 part.1 Store unused fractional scroll amount for later wheel events r=smaug --- content/events/src/nsEventStateManager.cpp | 139 +++++++++++++-------- content/events/src/nsEventStateManager.h | 23 +++- layout/generic/nsGfxScrollFrame.cpp | 11 +- modules/libpref/src/init/all.js | 4 + 4 files changed, 121 insertions(+), 56 deletions(-) diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index 84b1daae60e5..bb44c8d39670 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -2516,6 +2516,13 @@ nsEventStateManager::DispatchLegacyMouseScrollEvents(nsIFrame* aTargetFrame, nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.width), nsPresContext::AppUnitsToIntCSSPixels(scrollAmount.height)); + // XXX We don't deal with fractional amount in legacy event, though the + // default action handler (DoScrollText()) deals with it. + // If we implemented such strict computation, we would need additional + // accumulated delta values. It would made the code more complicated. + // And also it would computes different delta values from older version. + // It doesn't make sense to implement such code for legacy events and + // rare cases. PRInt32 scrollDeltaX, scrollDeltaY, pixelDeltaX, pixelDeltaY; switch (aEvent->deltaMode) { case nsIDOMWheelEvent::DOM_DELTA_PAGE: @@ -2734,26 +2741,23 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame, continue; } - // Check if the scrollable view can be scrolled any further. - if (frameToScroll->GetLineScrollAmount().height) { - // For default action, we should climb up the tree if cannot scroll it - // by the event actually. - bool canScroll = CanScrollOn(frameToScroll, - aEvent->deltaX, aEvent->deltaY); - // Comboboxes need special care. - nsIComboboxControlFrame* comboBox = do_QueryFrame(scrollFrame); - if (comboBox) { - if (comboBox->IsDroppedDown()) { - // Don't propagate to parent when drop down menu is active. - return canScroll ? frameToScroll : nullptr; - } - // Always propagate when not dropped down (even if focused). - continue; + // For default action, we should climb up the tree if cannot scroll it + // by the event actually. + bool canScroll = CanScrollOn(frameToScroll, + aEvent->deltaX, aEvent->deltaY); + // Comboboxes need special care. + nsIComboboxControlFrame* comboBox = do_QueryFrame(scrollFrame); + if (comboBox) { + if (comboBox->IsDroppedDown()) { + // Don't propagate to parent when drop down menu is active. + return canScroll ? frameToScroll : nullptr; } + // Always propagate when not dropped down (even if focused). + continue; + } - if (canScroll) { - return frameToScroll; - } + if (canScroll) { + return frameToScroll; } } @@ -2825,18 +2829,6 @@ nsEventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame, return; } - // If the wheel event is line scroll and the delta value is computed from - // system settings, allow to override the system speed. - bool allowScrollSpeedOverride = - (!aEvent->customizedByUserPrefs && - aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE); - DeltaValues acceleratedDelta = - nsMouseWheelTransaction::AccelerateWheelDelta(aEvent, - allowScrollSpeedOverride); - - bool isDeltaModePixel = - (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL); - // Default action's actual scroll amount should be computed from device // pixels. nsPresContext* pc = scrollFrame->PresContext(); @@ -2844,17 +2836,9 @@ nsEventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame, nsIntSize scrollAmountInDevPixels( pc->AppUnitsToDevPixels(scrollAmount.width), pc->AppUnitsToDevPixels(scrollAmount.height)); - - nsIntPoint actualDevPixelScrollAmount(0, 0); - if (isDeltaModePixel) { - actualDevPixelScrollAmount.x = RoundDown(acceleratedDelta.deltaX); - actualDevPixelScrollAmount.y = RoundDown(acceleratedDelta.deltaY); - } else { - actualDevPixelScrollAmount.x = - RoundDown(scrollAmountInDevPixels.width * acceleratedDelta.deltaX); - actualDevPixelScrollAmount.y = - RoundDown(scrollAmountInDevPixels.height * acceleratedDelta.deltaY); - } + nsIntPoint actualDevPixelScrollAmount = + DeltaAccumulator::GetInstance()-> + ComputeScrollAmountForDefaultAction(aEvent, scrollAmountInDevPixels); nsIAtom* origin = nullptr; switch (aEvent->deltaMode) { @@ -2888,6 +2872,9 @@ nsEventStateManager::DoScrollText(nsIScrollableFrame* aScrollableFrame, -devPixelPageSize.height; } + bool isDeltaModePixel = + (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL); + nsIScrollableFrame::ScrollMode mode; switch (aEvent->scrollType) { case widget::WheelEvent::SCROLL_DEFAULT: @@ -5087,13 +5074,6 @@ nsEventStateManager::DeltaAccumulator::InitLineOrPageDelta( MOZ_ASSERT(aESM); MOZ_ASSERT(aEvent); - if (!(aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL && - aEvent->isPixelOnlyDevice) && - !WheelPrefs::GetInstance()->NeedToComputeLineOrPageDelta(aEvent)) { - Reset(); - return; - } - // Reset if the previous wheel event is too old. if (!mLastTime.IsNull()) { TimeDuration duration = TimeStamp::Now() - mLastTime; @@ -5102,7 +5082,7 @@ nsEventStateManager::DeltaAccumulator::InitLineOrPageDelta( } } // If we have accumulated delta, we may need to reset it. - if (mHandlingDeltaMode != PR_UINT32_MAX) { + if (IsInTransaction()) { // If wheel event type is changed, reset the values. if (mHandlingDeltaMode != aEvent->deltaMode || mHandlingPixelOnlyDevice != aEvent->isPixelOnlyDevice) { @@ -5111,10 +5091,10 @@ nsEventStateManager::DeltaAccumulator::InitLineOrPageDelta( // If the delta direction is changed, we should reset only the // accumulated values. if (mX && aEvent->deltaX && ((aEvent->deltaX > 0.0) != (mX > 0.0))) { - mX = 0.0; + mX = mPendingScrollAmountX = 0.0; } if (mY && aEvent->deltaY && ((aEvent->deltaY > 0.0) != (mY > 0.0))) { - mY = 0.0; + mY = mPendingScrollAmountY = 0.0; } } } @@ -5122,6 +5102,28 @@ nsEventStateManager::DeltaAccumulator::InitLineOrPageDelta( mHandlingDeltaMode = aEvent->deltaMode; mHandlingPixelOnlyDevice = aEvent->isPixelOnlyDevice; + // If it's handling neither pixel scroll mode for pixel only device nor + // delta values multiplied by prefs, we must not modify lineOrPageDelta + // values. + if (!(mHandlingDeltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL && + mHandlingPixelOnlyDevice) && + !nsEventStateManager::WheelPrefs::GetInstance()-> + NeedToComputeLineOrPageDelta(aEvent)) { + // Set the delta values to mX and mY. They would be used when above block + // resets mX/mY/mPendingScrollAmountX/mPendingScrollAmountY if the direction + // is changed. + // NOTE: We shouldn't accumulate the delta values, it might could cause + // overflow even though it's not a realistic situation. + if (aEvent->deltaX) { + mX = aEvent->deltaX; + } + if (aEvent->deltaY) { + mY = aEvent->deltaY; + } + mLastTime = TimeStamp::Now(); + return; + } + mX += aEvent->deltaX; mY += aEvent->deltaY; @@ -5162,10 +5164,45 @@ void nsEventStateManager::DeltaAccumulator::Reset() { mX = mY = 0.0; + mPendingScrollAmountX = mPendingScrollAmountY = 0.0; mHandlingDeltaMode = PR_UINT32_MAX; mHandlingPixelOnlyDevice = false; } +nsIntPoint +nsEventStateManager::DeltaAccumulator::ComputeScrollAmountForDefaultAction( + widget::WheelEvent* aEvent, + const nsIntSize& aScrollAmountInDevPixels) +{ + MOZ_ASSERT(aEvent); + + // If the wheel event is line scroll and the delta value is computed from + // system settings, allow to override the system speed. + bool allowScrollSpeedOverride = + (!aEvent->customizedByUserPrefs && + aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE); + DeltaValues acceleratedDelta = + nsMouseWheelTransaction::AccelerateWheelDelta(aEvent, + allowScrollSpeedOverride); + + nsIntPoint result(0, 0); + if (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) { + mPendingScrollAmountX += acceleratedDelta.deltaX; + mPendingScrollAmountY += acceleratedDelta.deltaY; + } else { + mPendingScrollAmountX += + aScrollAmountInDevPixels.width * acceleratedDelta.deltaX; + mPendingScrollAmountY += + aScrollAmountInDevPixels.height * acceleratedDelta.deltaY; + } + result.x = RoundDown(mPendingScrollAmountX); + result.y = RoundDown(mPendingScrollAmountY); + mPendingScrollAmountX -= result.x; + mPendingScrollAmountY -= result.y; + + return result; +} + /******************************************************************/ /* nsEventStateManager::WheelPrefs */ /******************************************************************/ diff --git a/content/events/src/nsEventStateManager.h b/content/events/src/nsEventStateManager.h index e776e5ac251c..f3ad407ce4a9 100644 --- a/content/events/src/nsEventStateManager.h +++ b/content/events/src/nsEventStateManager.h @@ -529,6 +529,8 @@ protected: sInstance = nullptr; } + bool IsInTransaction() { return mHandlingDeltaMode != PR_UINT32_MAX; } + /** * InitLineOrPageDelta() stores pixel delta values of WheelEvents which are * caused if it's needed. And if the accumulated delta becomes a @@ -539,19 +541,34 @@ protected: mozilla::widget::WheelEvent* aEvent); /** - * Reset() resets both delta values. + * Reset() resets all members. */ void Reset(); + /** + * ComputeScrollAmountForDefaultAction() computes the default action's + * scroll amount in device pixels with mPendingScrollAmount*. + */ + nsIntPoint ComputeScrollAmountForDefaultAction( + mozilla::widget::WheelEvent* aEvent, + const nsIntSize& aScrollAmountInDevPixels); + private: DeltaAccumulator() : - mX(0.0), mY(0.0), mHandlingDeltaMode(PR_UINT32_MAX), - mHandlingPixelOnlyDevice(false) + mX(0.0), mY(0.0), mPendingScrollAmountX(0.0), mPendingScrollAmountY(0.0), + mHandlingDeltaMode(PR_UINT32_MAX), mHandlingPixelOnlyDevice(false) { } double mX; double mY; + + // When default action of a wheel event is scroll but some delta values + // are ignored because the computed amount values are not integer, the + // fractional values are saved by these members. + double mPendingScrollAmountX; + double mPendingScrollAmountY; + TimeStamp mLastTime; PRUint32 mHandlingDeltaMode; diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 55c9e65ff5c6..40f67fd7f5b9 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -2548,9 +2548,16 @@ nsGfxScrollFrameInner::GetLineScrollAmount() const nsLayoutUtils::GetFontMetricsForFrame(mOuter, getter_AddRefs(fm), nsLayoutUtils::FontSizeInflationFor(mOuter)); NS_ASSERTION(fm, "FontMetrics is null, assuming fontHeight == 1 appunit"); - nscoord fontHeight = 1; + static nscoord sMinLineScrollAmountInPixels = -1; + if (sMinLineScrollAmountInPixels < 0) { + Preferences::AddIntVarCache(&sMinLineScrollAmountInPixels, + "mousewheel.min_line_scroll_amount", 1); + } + PRUint32 appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel(); + nscoord fontHeight = + NS_MAX(1, sMinLineScrollAmountInPixels) * appUnitsPerDevPixel; if (fm) { - fontHeight = fm->MaxHeight(); + fontHeight = NS_MAX(fm->MaxHeight(), fontHeight); } return nsSize(fontHeight, fontHeight); diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index fb7e10e855b7..c6f85c86fb7c 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1417,6 +1417,10 @@ pref("mousewheel.with_win.delta_multiplier_x", 100); pref("mousewheel.with_win.delta_multiplier_y", 100); pref("mousewheel.with_win.delta_multiplier_z", 100); +// If line-height is lower than this value (in device pixels), 1 line scroll +// scrolls this height. +pref("mousewheel.min_line_scroll_amount", 5); + // These define the smooth scroll behavior (min ms, max ms) for different triggers // Some triggers: // mouseWheel: Discrete mouse wheel events, Synaptics touchpads on windows (generate wheel events)