diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index 861252754ae5..ddfa1eb71fbb 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -394,10 +394,10 @@ struct ParamTraits } }; -template<> -struct ParamTraits +template +struct ParamTraits > { - typedef mozilla::gfx::IntSize paramType; + typedef mozilla::gfx::IntSizeTyped paramType; static void Write(Message* msg, const paramType& param) { @@ -756,6 +756,7 @@ struct ParamTraits WriteParam(aMsg, aParam.mBackgroundColor); WriteParam(aMsg, aParam.mDoSmoothScroll); WriteParam(aMsg, aParam.mSmoothScrollOffset); + WriteParam(aMsg, aParam.GetLineScrollAmount()); WriteParam(aMsg, aParam.GetContentDescription()); } @@ -797,6 +798,7 @@ struct ParamTraits ReadParam(aMsg, aIter, &aResult->mBackgroundColor) && ReadParam(aMsg, aIter, &aResult->mDoSmoothScroll) && ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) && + ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) && ReadContentDescription(aMsg, aIter, aResult)); } }; diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index 52c79ba24364..ae4931f2e078 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -65,6 +65,7 @@ public: , mViewport(0, 0, 0, 0) , mExtraResolution(1) , mBackgroundColor(0, 0, 0, 0) + , mLineScrollAmount(0, 0) { } @@ -95,7 +96,8 @@ public: mUpdateScrollOffset == aOther.mUpdateScrollOffset && mExtraResolution == aOther.mExtraResolution && mBackgroundColor == aOther.mBackgroundColor && - mDoSmoothScroll == aOther.mDoSmoothScroll; + mDoSmoothScroll == aOther.mDoSmoothScroll && + mLineScrollAmount == aOther.mLineScrollAmount; } bool operator!=(const FrameMetrics& aOther) const { @@ -514,6 +516,16 @@ public: mMayHaveTouchListeners = aMayHaveTouchListeners; } + const LayoutDeviceIntSize& GetLineScrollAmount() const + { + return mLineScrollAmount; + } + + void SetLineScrollAmount(const LayoutDeviceIntSize& size) + { + mLineScrollAmount = size; + } + private: // New fields from now on should be made private and old fields should // be refactored to be private. @@ -605,6 +617,9 @@ private: // This is empty unless this is a scrollable layer and the // apz.printtree pref is turned on. nsCString mContentDescription; + + // The value of GetLineScrollAmount(), for scroll frames. + LayoutDeviceIntSize mLineScrollAmount; }; /** diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 68c7da00b3e4..5d8f22fd8243 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -611,6 +611,29 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent, MultiTouchInput& touchInput = aEvent.AsMultiTouchInput(); result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId); break; + } case SCROLLWHEEL_INPUT: { + ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput(); + nsRefPtr apzc = GetTargetAPZC(wheelInput.mOrigin, + &hitResult); + if (apzc) { + MOZ_ASSERT(hitResult == ApzcHitRegion || hitResult == ApzcContentRegion); + + transformToApzc = GetScreenToApzcTransform(apzc); + wheelInput.mLocalOrigin = + TransformTo(transformToApzc, wheelInput.mOrigin); + + result = mInputQueue->ReceiveInputEvent( + apzc, + /* aTargetConfirmed = */ hitResult, + wheelInput, aOutInputBlockId); + + // Update the out-parameters so they are what the caller expects. + apzc->GetGuid(aOutTargetGuid); + Matrix4x4 transformToGecko = transformToApzc * GetApzcToGeckoTransform(apzc); + wheelInput.mOrigin = + TransformTo(transformToGecko, wheelInput.mLocalOrigin); + } + break; } case PANGESTURE_INPUT: { PanGestureInput& panInput = aEvent.AsPanGestureInput(); nsRefPtr apzc = GetTargetAPZC(panInput.mPanStartPoint, diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index a08dac06c6da..1dd1dcd4c176 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -1070,6 +1070,11 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) } break; } + case SCROLLWHEEL_INPUT: { + const ScrollWheelInput& scrollInput = aEvent.AsScrollWheelInput(); + rv = OnScrollWheel(scrollInput); + break; + } default: return HandleGestureEvent(aEvent); } @@ -1453,6 +1458,72 @@ AsyncPanZoomController::ConvertToGecko(const ParentLayerPoint& aPoint, CSSPoint* return false; } +nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent) +{ + double deltaX = aEvent.mDeltaX; + double deltaY = aEvent.mDeltaY; + switch (aEvent.mDeltaType) { + case ScrollWheelInput::SCROLLDELTA_LINE: { + LayoutDeviceIntSize scrollAmount = mFrameMetrics.GetLineScrollAmount(); + deltaX *= scrollAmount.width; + deltaY *= scrollAmount.height; + break; + } + default: + MOZ_ASSERT_UNREACHABLE("unexpected scroll delta type"); + return nsEventStatus_eConsumeNoDefault; + } + + switch (aEvent.mScrollMode) { + case ScrollWheelInput::SCROLLMODE_INSTANT: { + // Decompose into pan events for simplicity. + PanGestureInput start(PanGestureInput::PANGESTURE_START, aEvent.mTime, aEvent.mTimeStamp, + aEvent.mOrigin, ScreenPoint(0, 0), aEvent.modifiers); + start.mLocalPanStartPoint = aEvent.mLocalOrigin; + OnPanBegin(start); + + // Pan gestures use natural directions which are inverted from scroll + // wheel and touchpad scroll gestures, so we invert x/y here. Since the + // zoom includes any device : css pixel zoom, we convert to CSS pixels + // before applying the zoom. + LayoutDevicePoint devicePixelDelta(-deltaX, -deltaY); + ParentLayerPoint delta = (devicePixelDelta / mFrameMetrics.mDevPixelsPerCSSPixel) * + mFrameMetrics.GetZoom(); + + PanGestureInput move(PanGestureInput::PANGESTURE_PAN, aEvent.mTime, aEvent.mTimeStamp, + aEvent.mOrigin, + ToScreenCoordinates(delta, aEvent.mLocalOrigin), + aEvent.modifiers); + move.mLocalPanStartPoint = aEvent.mLocalOrigin; + move.mLocalPanDisplacement = delta; + OnPan(move, false); + + PanGestureInput end(PanGestureInput::PANGESTURE_END, aEvent.mTime, aEvent.mTimeStamp, + aEvent.mOrigin, ScreenPoint(0, 0), aEvent.modifiers); + end.mLocalPanStartPoint = aEvent.mLocalOrigin; + OnPanEnd(start); + break; + } + + case ScrollWheelInput::SCROLLMODE_SMOOTH: { + CSSPoint delta = LayoutDevicePoint(deltaX, deltaY) / mFrameMetrics.mDevPixelsPerCSSPixel; + + // If we're already in a smooth scroll animation, don't cancel it. This + // lets us preserve the existing scrolling velocity. + if (mState != SMOOTH_SCROLL) { + CancelAnimation(); + mFrameMetrics.SetSmoothScrollOffset(mFrameMetrics.GetScrollOffset() + delta); + } else { + mFrameMetrics.SetSmoothScrollOffset(mFrameMetrics.GetSmoothScrollOffset() + delta); + } + StartSmoothScroll(); + break; + } + } + + return nsEventStatus_eConsumeNoDefault; +} + nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) { APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState); diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index d4e6bb864d30..8fcb5aa58c6e 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -414,6 +414,11 @@ protected: nsEventStatus OnPanMomentumStart(const PanGestureInput& aEvent); nsEventStatus OnPanMomentumEnd(const PanGestureInput& aEvent); + /** + * Helper methods for handling scroll wheel events. + */ + nsEventStatus OnScrollWheel(const ScrollWheelInput& aEvent); + /** * Helper methods for long press gestures. */ diff --git a/layout/base/Units.h b/layout/base/Units.h index a1ef2ffb78d0..2a0f2d24395e 100644 --- a/layout/base/Units.h +++ b/layout/base/Units.h @@ -236,6 +236,12 @@ struct LayoutDevicePixel { return FromUntyped(aRect.ToNearestPixels(aAppUnitsPerDevPixel)); } + static LayoutDeviceIntSize FromAppUnitsRounded(const nsSize& aSize, nscoord aAppUnitsPerDevPixel) { + return LayoutDeviceIntSize( + NSAppUnitsToIntPixels(aSize.width, aAppUnitsPerDevPixel), + NSAppUnitsToIntPixels(aSize.height, aAppUnitsPerDevPixel)); + } + static nsSize ToAppUnits(const LayoutDeviceIntSize& aSize, nscoord aAppUnitsPerDevPixel) { return nsSize(aSize.width * aAppUnitsPerDevPixel, aSize.height * aAppUnitsPerDevPixel); diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 0aa43e38a813..2b13fcab9b76 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -723,6 +723,11 @@ nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame, if (lastSmoothScrollOrigin) { metrics.SetSmoothScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration()); } + + nsSize lineScrollAmount = scrollableFrame->GetLineScrollAmount(); + LayoutDeviceIntSize lineScrollAmountInDevPixels = + LayoutDeviceIntSize::FromAppUnitsRounded(lineScrollAmount, presContext->AppUnitsPerDevPixel()); + metrics.SetLineScrollAmount(lineScrollAmountInDevPixels); } metrics.SetScrollId(scrollId); diff --git a/widget/InputData.h b/widget/InputData.h index 02c3ad320a08..c58eb1420834 100644 --- a/widget/InputData.h +++ b/widget/InputData.h @@ -27,13 +27,15 @@ enum InputType MULTITOUCH_INPUT, PANGESTURE_INPUT, PINCHGESTURE_INPUT, - TAPGESTURE_INPUT + TAPGESTURE_INPUT, + SCROLLWHEEL_INPUT }; class MultiTouchInput; class PanGestureInput; class PinchGestureInput; class TapGestureInput; +class ScrollWheelInput; // This looks unnecessary now, but as we add more and more classes that derive // from InputType (eventually probably almost as many as *Events.h has), it @@ -72,6 +74,7 @@ public: INPUTDATA_AS_CHILD_TYPE(PanGestureInput, PANGESTURE_INPUT) INPUTDATA_AS_CHILD_TYPE(PinchGestureInput, PINCHGESTURE_INPUT) INPUTDATA_AS_CHILD_TYPE(TapGestureInput, TAPGESTURE_INPUT) + INPUTDATA_AS_CHILD_TYPE(ScrollWheelInput, SCROLLWHEEL_INPUT) InputData() { @@ -431,6 +434,60 @@ public: ParentLayerPoint mLocalPoint; }; +// Encapsulation class for scroll-wheel events. These are generated by mice +// with physical scroll wheels, and on Windows by most touchpads when using +// scroll gestures. +class ScrollWheelInput : public InputData +{ +public: + enum ScrollDeltaType + { + // There are three kinds of scroll delta modes in Gecko: "page", "line" and + // "pixel". For apz, we currently only support "line" mode. + SCROLLDELTA_LINE + }; + + enum ScrollMode + { + SCROLLMODE_INSTANT, + SCROLLMODE_SMOOTH + }; + + ScrollWheelInput(uint32_t aTime, + TimeStamp aTimeStamp, + Modifiers aModifiers, + ScrollMode aScrollMode, + ScrollDeltaType aDeltaType, + const ScreenPoint& aOrigin, + double aDeltaX, + double aDeltaY) + : InputData(SCROLLWHEEL_INPUT, aTime, aTimeStamp, aModifiers), + mDeltaType(aDeltaType), + mScrollMode(aScrollMode), + mOrigin(aOrigin), + mDeltaX(aDeltaX), + mDeltaY(aDeltaY) + {} + + ScrollDeltaType mDeltaType; + ScrollMode mScrollMode; + ScreenPoint mOrigin; + + // Deltas are in units corresponding to the delta type. For line deltas, they + // are the number of line units to scroll. The number of device pixels for a + // horizontal and vertical line unit are in FrameMetrics::mLineScrollAmount. + // + // The horizontal (X) delta is > 0 for scrolling right and < 0 for scrolling + // left. The vertical (Y) delta is < 0 for scrolling up and > 0 for + // scrolling down. + double mDeltaX; + double mDeltaY; + + // The location of the scroll in local coordinates. This is set and used by + // APZ. + ParentLayerPoint mLocalOrigin; +}; + } #endif // InputData_h__