mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 719320 part.3 Use ComputeScrollTarget() for deciding detail value of legacy mouse scroll events r=smaug
This commit is contained in:
parent
adb8fea7af
commit
a0e8d18871
@ -139,9 +139,6 @@ static nscoord gPixelScrollDeltaX = 0;
|
||||
static nscoord gPixelScrollDeltaY = 0;
|
||||
static PRUint32 gPixelScrollDeltaTimeout = 0;
|
||||
|
||||
static nscoord
|
||||
GetScrollableLineHeight(nsIFrame* aTargetFrame);
|
||||
|
||||
TimeStamp nsEventStateManager::sHandlingInputStart;
|
||||
|
||||
static inline bool
|
||||
@ -1213,18 +1210,21 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
||||
|
||||
// If needed send a line scroll event for pixel scrolls with kNoLines
|
||||
if (msEvent->scrollFlags & nsMouseScrollEvent::kNoLines) {
|
||||
nscoord pixelHeight = aPresContext->AppUnitsToIntCSSPixels(
|
||||
GetScrollableLineHeight(aTargetFrame));
|
||||
|
||||
nsIScrollableFrame* scrollableFrame =
|
||||
ComputeScrollTarget(aTargetFrame, msEvent, false);
|
||||
PRInt32 pixelsPerUnit =
|
||||
nsPresContext::AppUnitsToIntCSSPixels(
|
||||
GetScrollAmount(aPresContext, msEvent, aTargetFrame,
|
||||
scrollableFrame));
|
||||
if (msEvent->scrollFlags & nsMouseScrollEvent::kIsVertical) {
|
||||
gPixelScrollDeltaX += msEvent->delta;
|
||||
if (!gPixelScrollDeltaX || !pixelHeight)
|
||||
if (!gPixelScrollDeltaX || !pixelsPerUnit)
|
||||
break;
|
||||
|
||||
if (NS_ABS(gPixelScrollDeltaX) >= pixelHeight) {
|
||||
PRInt32 numLines = (PRInt32)ceil((float)gPixelScrollDeltaX/(float)pixelHeight);
|
||||
if (NS_ABS(gPixelScrollDeltaX) >= pixelsPerUnit) {
|
||||
PRInt32 numLines = (PRInt32)ceil((float)gPixelScrollDeltaX/(float)pixelsPerUnit);
|
||||
|
||||
gPixelScrollDeltaX -= numLines*pixelHeight;
|
||||
gPixelScrollDeltaX -= numLines*pixelsPerUnit;
|
||||
|
||||
nsWeakFrame weakFrame(aTargetFrame);
|
||||
SendLineScrollEvent(aTargetFrame, msEvent, aPresContext,
|
||||
@ -1233,13 +1233,13 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
||||
}
|
||||
} else if (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal) {
|
||||
gPixelScrollDeltaY += msEvent->delta;
|
||||
if (!gPixelScrollDeltaY || !pixelHeight)
|
||||
if (!gPixelScrollDeltaY || !pixelsPerUnit)
|
||||
break;
|
||||
|
||||
if (NS_ABS(gPixelScrollDeltaY) >= pixelHeight) {
|
||||
PRInt32 numLines = (PRInt32)ceil((float)gPixelScrollDeltaY/(float)pixelHeight);
|
||||
if (NS_ABS(gPixelScrollDeltaY) >= pixelsPerUnit) {
|
||||
PRInt32 numLines = (PRInt32)ceil((float)gPixelScrollDeltaY/(float)pixelsPerUnit);
|
||||
|
||||
gPixelScrollDeltaY -= numLines*pixelHeight;
|
||||
gPixelScrollDeltaY -= numLines*pixelsPerUnit;
|
||||
|
||||
nsWeakFrame weakFrame(aTargetFrame);
|
||||
SendLineScrollEvent(aTargetFrame, msEvent, aPresContext,
|
||||
@ -2625,25 +2625,6 @@ GetParentFrameToScroll(nsIFrame* aFrame)
|
||||
return aFrame->GetParent();
|
||||
}
|
||||
|
||||
static nscoord
|
||||
GetScrollableLineHeight(nsIFrame* aTargetFrame)
|
||||
{
|
||||
for (nsIFrame* f = aTargetFrame; f; f = GetParentFrameToScroll(f)) {
|
||||
nsIScrollableFrame* sf = f->GetScrollTargetFrame();
|
||||
if (sf)
|
||||
return sf->GetLineScrollAmount().height;
|
||||
}
|
||||
|
||||
// Fall back to the font height of the target frame.
|
||||
nsRefPtr<nsFontMetrics> fm;
|
||||
nsLayoutUtils::GetFontMetricsForFrame(aTargetFrame, getter_AddRefs(fm),
|
||||
nsLayoutUtils::FontSizeInflationFor(aTargetFrame));
|
||||
NS_ASSERTION(fm, "FontMetrics is null!");
|
||||
if (fm)
|
||||
return fm->MaxHeight();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nsEventStateManager::SendLineScrollEvent(nsIFrame* aTargetFrame,
|
||||
nsMouseScrollEvent* aEvent,
|
||||
@ -2692,7 +2673,16 @@ nsEventStateManager::SendPixelScrollEvent(nsIFrame* aTargetFrame,
|
||||
targetContent = targetContent->GetParent();
|
||||
}
|
||||
|
||||
nscoord lineHeight = GetScrollableLineHeight(aTargetFrame);
|
||||
// The delta value for pixel scroll event should be computed from scroll
|
||||
// target of default action.
|
||||
// XXX This is very strange. When we're computing the detail value of line
|
||||
// scroll event if a pixel scroll event doesn't have a line scroll event,
|
||||
// we're using nearest scrollable frame's information.
|
||||
nsIScrollableFrame* scrollableFrame =
|
||||
ComputeScrollTarget(aTargetFrame, aEvent, true);
|
||||
PRInt32 pixelsPerUnit = nsPresContext::AppUnitsToIntCSSPixels(
|
||||
GetScrollAmount(aPresContext, aEvent, aTargetFrame,
|
||||
scrollableFrame));
|
||||
|
||||
bool isTrusted = (aEvent->flags & NS_EVENT_FLAG_TRUSTED) != 0;
|
||||
nsMouseScrollEvent event(isTrusted, NS_MOUSE_PIXEL_SCROLL, nullptr);
|
||||
@ -2703,7 +2693,12 @@ nsEventStateManager::SendPixelScrollEvent(nsIFrame* aTargetFrame,
|
||||
event.buttons = aEvent->buttons;
|
||||
event.scrollFlags = aEvent->scrollFlags;
|
||||
event.inputSource = static_cast<nsMouseEvent_base*>(aEvent)->inputSource;
|
||||
event.delta = aPresContext->AppUnitsToIntCSSPixels(aEvent->delta * lineHeight);
|
||||
if (aEvent->scrollFlags & nsMouseScrollEvent::kIsFullPage) {
|
||||
event.delta = !aEvent->delta ? 0 :
|
||||
aEvent->delta > 0 ? pixelsPerUnit : -pixelsPerUnit;
|
||||
} else {
|
||||
event.delta = aEvent->delta * pixelsPerUnit;
|
||||
}
|
||||
|
||||
nsEventDispatcher::Dispatch(targetContent, aPresContext, &event, nullptr, aStatus);
|
||||
}
|
||||
@ -2810,31 +2805,34 @@ nsEventStateManager::UseSystemScrollSettingFor(nsMouseScrollEvent* aMouseEvent)
|
||||
|
||||
nsIScrollableFrame*
|
||||
nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
|
||||
nsMouseScrollEvent* aEvent)
|
||||
nsMouseScrollEvent* aEvent,
|
||||
bool aForDefaultAction)
|
||||
{
|
||||
PRInt32 numLines = aEvent->delta;
|
||||
bool isHorizontal = aEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal;
|
||||
|
||||
// If the user recently scrolled with the mousewheel, then they probably want
|
||||
// to scroll the same view as before instead of the view under the cursor.
|
||||
// nsMouseWheelTransaction tracks the frame currently being scrolled with the
|
||||
// mousewheel. We consider the transaction ended when the mouse moves more than
|
||||
// "mousewheel.transaction.ignoremovedelay" milliseconds after the last scroll
|
||||
// operation, or any time the mouse moves 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 = nsMouseWheelTransaction::GetTargetFrame();
|
||||
if (lastScrollFrame) {
|
||||
nsIScrollableFrame* frameToScroll = lastScrollFrame->GetScrollTargetFrame();
|
||||
if (frameToScroll) {
|
||||
return frameToScroll;
|
||||
if (aForDefaultAction) {
|
||||
// If the user recently scrolled with the mousewheel, then they probably
|
||||
// want to scroll the same view as before instead of the view under the
|
||||
// cursor. nsMouseWheelTransaction tracks the frame currently being
|
||||
// scrolled with the mousewheel. We consider the transaction ended when the
|
||||
// mouse moves more than "mousewheel.transaction.ignoremovedelay"
|
||||
// milliseconds after the last scroll operation, or any time the mouse moves
|
||||
// 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 = nsMouseWheelTransaction::GetTargetFrame();
|
||||
if (lastScrollFrame) {
|
||||
nsIScrollableFrame* frameToScroll =
|
||||
lastScrollFrame->GetScrollTargetFrame();
|
||||
if (frameToScroll) {
|
||||
return frameToScroll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsIScrollableFrame* frameToScroll = nullptr;
|
||||
bool passToParent = true;
|
||||
for (nsIFrame* scrollFrame = aTargetFrame;
|
||||
scrollFrame && passToParent;
|
||||
for (nsIFrame* scrollFrame = aTargetFrame; scrollFrame;
|
||||
scrollFrame = GetParentFrameToScroll(scrollFrame)) {
|
||||
// Check whether the frame wants to provide us with a scrollable view.
|
||||
frameToScroll = scrollFrame->GetScrollTargetFrame();
|
||||
@ -2842,6 +2840,12 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
|
||||
continue;
|
||||
}
|
||||
|
||||
// At computing scroll target for legacy mouse events, we should return
|
||||
// first scrollable element even when it's not scrollable to the direction.
|
||||
if (!aForDefaultAction) {
|
||||
return frameToScroll;
|
||||
}
|
||||
|
||||
nsPresContext::ScrollbarStyles ss = frameToScroll->GetScrollbarStyles();
|
||||
if (NS_STYLE_OVERFLOW_HIDDEN ==
|
||||
(isHorizontal ? ss.mHorizontal : ss.mVertical)) {
|
||||
@ -2849,36 +2853,69 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
|
||||
}
|
||||
|
||||
// Check if the scrollable view can be scrolled any further.
|
||||
nscoord lineHeight = frameToScroll->GetLineScrollAmount().height;
|
||||
if (lineHeight != 0) {
|
||||
if (CanScrollOn(frameToScroll, numLines, isHorizontal)) {
|
||||
passToParent = false;
|
||||
}
|
||||
|
||||
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, numLines, isHorizontal);
|
||||
// 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.
|
||||
if (passToParent) {
|
||||
passToParent = false;
|
||||
frameToScroll = nullptr;
|
||||
}
|
||||
} else {
|
||||
// Always propagate when not dropped down (even if focused).
|
||||
passToParent = true;
|
||||
return canScroll ? frameToScroll : nullptr;
|
||||
}
|
||||
// Always propagate when not dropped down (even if focused).
|
||||
continue;
|
||||
}
|
||||
|
||||
if (canScroll) {
|
||||
return frameToScroll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!passToParent) {
|
||||
return frameToScroll;
|
||||
}
|
||||
|
||||
nsIFrame* newFrame = nsLayoutUtils::GetCrossDocParentFrame(
|
||||
aTargetFrame->PresContext()->FrameManager()->GetRootFrame());
|
||||
return newFrame ? ComputeScrollTarget(newFrame, aEvent) : nullptr;
|
||||
return newFrame ?
|
||||
ComputeScrollTarget(newFrame, aEvent, aForDefaultAction) : nullptr;
|
||||
}
|
||||
|
||||
nscoord
|
||||
nsEventStateManager::GetScrollAmount(nsPresContext* aPresContext,
|
||||
nsMouseScrollEvent* aEvent,
|
||||
nsIFrame* aTargetFrame,
|
||||
nsIScrollableFrame* aScrollableFrame)
|
||||
{
|
||||
MOZ_ASSERT(aPresContext);
|
||||
MOZ_ASSERT(aEvent);
|
||||
MOZ_ASSERT(aTargetFrame);
|
||||
|
||||
bool isPage = (aEvent->scrollFlags & nsMouseScrollEvent::kIsFullPage) != 0;
|
||||
bool isHorizontal =
|
||||
(aEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal) != 0;
|
||||
|
||||
if (aScrollableFrame) {
|
||||
nsSize size = isPage ? aScrollableFrame->GetPageScrollAmount() :
|
||||
aScrollableFrame->GetLineScrollAmount();
|
||||
return isHorizontal ? size.width : size.height;
|
||||
}
|
||||
|
||||
// If there is no scrollable frame and page scrolling, use view port size.
|
||||
if (isPage) {
|
||||
nsRect visibleRect = aPresContext->GetVisibleArea();
|
||||
return isHorizontal ? visibleRect.width : visibleRect.height;
|
||||
}
|
||||
|
||||
// If there is no scrollable frame, we should use root frame's information.
|
||||
nsIFrame* rootFrame = aPresContext->PresShell()->GetRootFrame();
|
||||
if (!rootFrame) {
|
||||
return 0;
|
||||
}
|
||||
nsRefPtr<nsFontMetrics> fm;
|
||||
nsLayoutUtils::GetFontMetricsForFrame(rootFrame, getter_AddRefs(fm),
|
||||
nsLayoutUtils::FontSizeInflationFor(rootFrame));
|
||||
NS_ENSURE_TRUE(fm, 0);
|
||||
return fm->MaxHeight();
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -2892,7 +2929,7 @@ nsEventStateManager::DoScrollText(nsIFrame* aTargetFrame,
|
||||
aMouseEvent->scrollOverflow = aMouseEvent->delta;
|
||||
|
||||
nsIScrollableFrame* frameToScroll =
|
||||
ComputeScrollTarget(aTargetFrame, aMouseEvent);
|
||||
ComputeScrollTarget(aTargetFrame, aMouseEvent, true);
|
||||
if (!frameToScroll) {
|
||||
nsMouseWheelTransaction::EndTransaction();
|
||||
return NS_OK;
|
||||
|
@ -333,10 +333,34 @@ protected:
|
||||
*
|
||||
* @param aTargetFrame The event target of the wheel event.
|
||||
* @param aEvent The handling mouse wheel event.
|
||||
* @param aForDefaultAction Whether this uses wheel transaction or not.
|
||||
* If true, returns the latest scrolled frame if
|
||||
* there is it. Otherwise, the nearest ancestor
|
||||
* scrollable frame from aTargetFrame.
|
||||
* @return The scrollable frame which should be scrolled.
|
||||
*/
|
||||
nsIScrollableFrame* ComputeScrollTarget(nsIFrame* aTargetFrame,
|
||||
nsMouseScrollEvent* aMouseEvent);
|
||||
nsMouseScrollEvent* aMouseEvent,
|
||||
bool aForDefaultAction);
|
||||
|
||||
/**
|
||||
* GetScrollAmount() returns the scroll amount in app units of one line or
|
||||
* one page. If the mouse scroll event scrolls a page, returns the page width
|
||||
* or height. Otherwise, returns line height.
|
||||
*
|
||||
* @param aTargetFrame The event target of the wheel event.
|
||||
* Must not be NULL.
|
||||
* @param aScrollableFrame A frame which will be scrolled by the event.
|
||||
* The result of ComputeScrollTarget() is
|
||||
* expected for this value.
|
||||
* This can be NULL if there is no scrollable
|
||||
* frame. Then, this method uses root frame's
|
||||
* line height or visible area's width or height.
|
||||
*/
|
||||
nscoord GetScrollAmount(nsPresContext* aPresContext,
|
||||
nsMouseScrollEvent* aEvent,
|
||||
nsIFrame* aTargetFrame,
|
||||
nsIScrollableFrame* aScrollableFrame);
|
||||
|
||||
/**
|
||||
* @param aQueryEvent If you set vailid pointer for this, DoScrollText()
|
||||
|
@ -1819,17 +1819,8 @@ function runNextTest(aTests, aIndex)
|
||||
test.description + ":(" + aEvent.type + "), axis mismatch");
|
||||
ok(aEvent.detail != 0,
|
||||
test.description + ":(" + aEvent.type + "), delta must not be 0");
|
||||
if (aEvent.type == kMousePixelScrollEvent &&
|
||||
(test.message == WM_VSCROLL || test.message == WM_HSCROLL) &&
|
||||
(test.delta == SB_PAGEUP || test.delta == SB_PAGEDOWN)) {
|
||||
// pixel delta value generated by ESM is buggy.
|
||||
todo_is(aEvent.detail, expectedDelta,
|
||||
test.description + ":(" + aEvent.type + "), delta mismatch");
|
||||
} else {
|
||||
is(aEvent.detail, expectedDelta,
|
||||
test.description + ":(" + aEvent.type + "), delta mismatch");
|
||||
}
|
||||
|
||||
is(aEvent.detail, expectedDelta,
|
||||
test.description + ":(" + aEvent.type + "), delta mismatch");
|
||||
is(aEvent.shiftKey, (test.modifiers & (SHIFT_L | SHIFT_R)) != 0,
|
||||
test.description + ":(" + aEvent.type + "), shiftKey mismatch");
|
||||
is(aEvent.ctrlKey, (test.modifiers & (CTRL_L | CTRL_R)) != 0,
|
||||
|
Loading…
Reference in New Issue
Block a user