From 154163bcb8da061708138ce984d288e3aca68697 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Wed, 17 Sep 2008 13:27:19 +0200 Subject: [PATCH] Bug 350471 - Reenable pixel scrolling (two-finger touchpad), r=smaug r=smichaud sr=roc --- content/base/src/nsContentUtils.cpp | 1 + content/base/src/nsGkAtomList.h | 1 + content/events/src/nsDOMEvent.cpp | 8 +- content/events/src/nsDOMEvent.h | 1 + content/events/src/nsEventStateManager.cpp | 224 ++++++++++++++------ content/events/src/nsEventStateManager.h | 8 + content/events/test/Makefile.in | 1 + content/events/test/test_bug350471.xul | 229 +++++++++++++++++++++ dom/public/idl/base/nsIDOMWindowUtils.idl | 3 +- dom/src/base/nsDOMWindowUtils.cpp | 2 + modules/libpref/src/init/all.js | 3 + testing/mochitest/tests/SimpleTest/EventUtils.js | 16 +- toolkit/content/tests/widgets/test_mousescroll.xul | 89 +++++--- toolkit/content/tests/widgets/tree_shared.js | 31 ++- widget/public/nsGUIEvent.h | 48 ++++- widget/src/cocoa/nsChildView.h | 16 ++ widget/src/cocoa/nsChildView.mm | 170 +++++++++------ 17 files changed, 678 insertions(+), 173 deletions(-) create mode 100644 content/events/test/test_bug350471.xul diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 754a55a771..3e44c193cd 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -410,6 +410,7 @@ nsContentUtils::InitializeEventTable() { { &nsGkAtoms::onDOMFocusIn, { NS_UI_FOCUSIN, EventNameType_HTMLXUL }}, { &nsGkAtoms::onDOMFocusOut, { NS_UI_FOCUSOUT, EventNameType_HTMLXUL }}, { &nsGkAtoms::onDOMMouseScroll, { NS_MOUSE_SCROLL, EventNameType_HTMLXUL }}, + { &nsGkAtoms::onMozMousePixelScroll, { NS_MOUSE_PIXEL_SCROLL, EventNameType_HTMLXUL }}, { &nsGkAtoms::oninput, { NS_FORM_INPUT, EventNameType_HTMLXUL }}, { &nsGkAtoms::onpageshow, { NS_PAGE_SHOW, EventNameType_HTML }}, { &nsGkAtoms::onpagehide, { NS_PAGE_HIDE, EventNameType_HTML }}, diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index c46a6141a1..c9a30dc361 100755 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -641,6 +641,7 @@ GK_ATOM(onmousemove, "onmousemove") GK_ATOM(onmouseout, "onmouseout") GK_ATOM(onmouseover, "onmouseover") GK_ATOM(onmouseup, "onmouseup") +GK_ATOM(onMozMousePixelScroll, "onMozMousePixelScroll") GK_ATOM(ononline, "ononline") GK_ATOM(onoffline, "onoffline") GK_ATOM(onoverflow, "onoverflow") diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index a0965098bc..189619831e 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -70,8 +70,8 @@ static const char* const sEventNames[] = { "DOMNodeRemovedFromDocument", "DOMNodeInsertedIntoDocument", "DOMAttrModified", "DOMCharacterDataModified", "DOMActivate", "DOMFocusIn", "DOMFocusOut", - "pageshow", "pagehide", "DOMMouseScroll", "offline", "online", - "copy", "cut", "paste" + "pageshow", "pagehide", "DOMMouseScroll", "MozMousePixelScroll", + "offline", "online", "copy", "cut", "paste" #ifdef MOZ_SVG , "SVGLoad", "SVGUnload", "SVGAbort", "SVGError", "SVGResize", "SVGScroll", @@ -479,6 +479,8 @@ nsDOMEvent::SetEventType(const nsAString& aEventTypeArg) } else if (mEvent->eventStructType == NS_MOUSE_SCROLL_EVENT) { if (atom == nsGkAtoms::onDOMMouseScroll) mEvent->message = NS_MOUSE_SCROLL; + else if (atom == nsGkAtoms::onMozMousePixelScroll) + mEvent->message = NS_MOUSE_PIXEL_SCROLL; } else if (mEvent->eventStructType == NS_DRAG_EVENT) { if (atom == nsGkAtoms::ondragstart) mEvent->message = NS_DRAGDROP_START; @@ -1393,6 +1395,8 @@ const char* nsDOMEvent::GetEventName(PRUint32 aEventType) return sEventNames[eDOMEvents_pagehide]; case NS_MOUSE_SCROLL: return sEventNames[eDOMEvents_DOMMouseScroll]; + case NS_MOUSE_PIXEL_SCROLL: + return sEventNames[eDOMEvents_MozMousePixelScroll]; case NS_OFFLINE: return sEventNames[eDOMEvents_offline]; case NS_ONLINE: diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h index 8178eac74c..ab56fe1c71 100644 --- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -124,6 +124,7 @@ public: eDOMEvents_pageshow, eDOMEvents_pagehide, eDOMEvents_DOMMouseScroll, + eDOMEvents_MozMousePixelScroll, eDOMEvents_offline, eDOMEvents_online, eDOMEvents_copy, diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index 18c964dcc8..316721d90b 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -113,7 +113,7 @@ #include "nsIScrollableViewProvider.h" #include "nsIDOMDocumentRange.h" #include "nsIDOMDocumentEvent.h" -#include "nsIDOMMouseEvent.h" +#include "nsIDOMMouseScrollEvent.h" #include "nsIDOMDragEvent.h" #include "nsIDOMEventTarget.h" #include "nsIDOMDocumentView.h" @@ -141,6 +141,7 @@ #include "nsServiceManagerUtils.h" #include "nsITimer.h" +#include "nsIFontMetrics.h" #include "nsIDragService.h" #include "nsIDragSession.h" @@ -460,7 +461,9 @@ nsEventStateManager::nsEventStateManager() mNormalLMouseEventInProcess(PR_FALSE), m_haveShutdown(PR_FALSE), mBrowseWithCaret(PR_FALSE), - mTabbedThroughDocument(PR_FALSE) + mTabbedThroughDocument(PR_FALSE), + mLastLineScrollConsumedX(PR_FALSE), + mLastLineScrollConsumedY(PR_FALSE) { if (sESMInstanceCount == 0) { gUserInteractionTimerCallback = new nsUITimerCallback(); @@ -1407,6 +1410,20 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext, } } break; + case NS_MOUSE_PIXEL_SCROLL: + { + if (mCurrentFocus) { + mCurrentTargetContent = mCurrentFocus; + } + + // When the last line scroll has been canceled, eat the pixel scroll event + nsMouseScrollEvent *msEvent = static_cast(aEvent); + if ((msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal) ? + mLastLineScrollConsumedX : mLastLineScrollConsumedY) { + *aStatus = nsEventStatus_eConsumeNoDefault; + } + } + break; case NS_QUERY_SELECTED_TEXT: { nsQueryContentEventHandler handler(mPresContext); @@ -2414,6 +2431,66 @@ GetParentFrameToScroll(nsPresContext* aPresContext, nsIFrame* aFrame) return aFrame->GetParent(); } +static nsIScrollableView* +GetScrollableViewForFrame(nsPresContext* aPresContext, nsIFrame* aFrame) +{ + for (; aFrame; aFrame = GetParentFrameToScroll(aPresContext, aFrame)) { + nsIScrollableViewProvider* svp; + CallQueryInterface(aFrame, &svp); + if (svp) { + nsIScrollableView* scrollView = svp->GetScrollableView(); + if (scrollView) + return scrollView; + } + } + return nsnull; +} + +void +nsEventStateManager::SendPixelScrollEvent(nsIFrame* aTargetFrame, + nsMouseScrollEvent* aEvent, + nsPresContext* aPresContext, + nsEventStatus* aStatus) +{ + nsCOMPtr targetContent = aTargetFrame->GetContent(); + if (!targetContent) + GetFocusedContent(getter_AddRefs(targetContent)); + if (!targetContent) + return; + + while (targetContent->IsNodeOfType(nsINode::eTEXT)) { + targetContent = targetContent->GetParent(); + } + + nsIScrollableView* scrollView = GetScrollableViewForFrame(aPresContext, aTargetFrame); + nscoord lineHeight = 0; + if (scrollView) { + scrollView->GetLineHeight(&lineHeight); + } else { + // Fall back to the font height of the target frame. + const nsStyleFont* font = aTargetFrame->GetStyleFont(); + const nsFont& f = font->mFont; + nsCOMPtr fm = aPresContext->GetMetricsFor(f); + NS_ASSERTION(fm, "FontMetrics is null!"); + if (fm) + fm->GetHeight(lineHeight); + } + + PRBool isTrusted = (aEvent->flags & NS_EVENT_FLAG_TRUSTED) != 0; + nsMouseScrollEvent event(isTrusted, NS_MOUSE_PIXEL_SCROLL, nsnull); + event.refPoint = aEvent->refPoint; + event.widget = aEvent->widget; + event.time = aEvent->time; + event.isShift = aEvent->isShift; + event.isControl = aEvent->isControl; + event.isAlt = aEvent->isAlt; + event.isMeta = aEvent->isMeta; + event.scrollFlags = aEvent->scrollFlags; + event.delta = aPresContext->AppUnitsToIntCSSPixels(aEvent->delta * lineHeight); + + nsEventDispatcher::Dispatch(targetContent, aPresContext, &event, nsnull, aStatus); +} + nsresult nsEventStateManager::DoScrollText(nsPresContext* aPresContext, nsIFrame* aTargetFrame, @@ -2732,79 +2809,106 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext, } break; case NS_MOUSE_SCROLL: - if (nsEventStatus_eConsumeNoDefault != *aStatus) { - - // Build the preference keys, based on the event properties. - nsMouseScrollEvent *msEvent = (nsMouseScrollEvent*) aEvent; + case NS_MOUSE_PIXEL_SCROLL: + { + nsMouseScrollEvent *msEvent = static_cast(aEvent); + + if (aEvent->message == NS_MOUSE_SCROLL) { + // Mark the subsequent pixel scrolls as valid / invalid, based on the + // observation if the previous line scroll has been canceled + if (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal) { + mLastLineScrollConsumedX = (nsEventStatus_eConsumeNoDefault == *aStatus); + } else if (msEvent->scrollFlags & nsMouseScrollEvent::kIsVertical) { + mLastLineScrollConsumedY = (nsEventStatus_eConsumeNoDefault == *aStatus); + } + if (!(msEvent->scrollFlags & nsMouseScrollEvent::kHasPixels)) { + // No generated pixel scroll event will follow. + // Create and send a pixel scroll DOM event now. + SendPixelScrollEvent(aTargetFrame, msEvent, presContext, aStatus); + } + } - NS_NAMED_LITERAL_CSTRING(actionslot, ".action"); - NS_NAMED_LITERAL_CSTRING(sysnumlinesslot, ".sysnumlines"); + if (*aStatus != nsEventStatus_eConsumeNoDefault) { + // Build the preference keys, based on the event properties. + NS_NAMED_LITERAL_CSTRING(actionslot, ".action"); + NS_NAMED_LITERAL_CSTRING(sysnumlinesslot, ".sysnumlines"); - nsCAutoString baseKey; - GetBasePrefKeyForMouseWheel(msEvent, baseKey); + nsCAutoString baseKey; + GetBasePrefKeyForMouseWheel(msEvent, baseKey); - // Extract the preferences - nsCAutoString actionKey(baseKey); - actionKey.Append(actionslot); + // Extract the preferences + nsCAutoString actionKey(baseKey); + actionKey.Append(actionslot); - nsCAutoString sysNumLinesKey(baseKey); - sysNumLinesKey.Append(sysnumlinesslot); + nsCAutoString sysNumLinesKey(baseKey); + sysNumLinesKey.Append(sysnumlinesslot); - PRInt32 action = nsContentUtils::GetIntPref(actionKey.get()); - PRBool useSysNumLines = - nsContentUtils::GetBoolPref(sysNumLinesKey.get()); - - if (useSysNumLines) { - if (msEvent->scrollFlags & nsMouseScrollEvent::kIsFullPage) - action = MOUSE_SCROLL_PAGE; - else if (msEvent->scrollFlags & nsMouseScrollEvent::kIsPixels) - action = MOUSE_SCROLL_PIXELS; - } + PRInt32 action = nsContentUtils::GetIntPref(actionKey.get()); + PRBool useSysNumLines = + nsContentUtils::GetBoolPref(sysNumLinesKey.get()); - switch (action) { - case MOUSE_SCROLL_N_LINES: - { - DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta, - (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal), - eScrollByLine); + if (useSysNumLines) { + if (msEvent->scrollFlags & nsMouseScrollEvent::kIsFullPage) + action = MOUSE_SCROLL_PAGE; } - break; - case MOUSE_SCROLL_PAGE: - { - DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta, - (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal), - eScrollByPage); + if (aEvent->message == NS_MOUSE_PIXEL_SCROLL) { + if (action == MOUSE_SCROLL_N_LINES) { + action = MOUSE_SCROLL_PIXELS; + } else { + // Do not scroll pixels when zooming + action = -1; + } + } else if (msEvent->scrollFlags & nsMouseScrollEvent::kHasPixels) { + if (action == MOUSE_SCROLL_N_LINES) { + // We shouldn't scroll lines when a pixel scroll event will follow. + action = -1; + } } - break; - case MOUSE_SCROLL_PIXELS: - { - DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta, - (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal), - eScrollByPixel); - } - break; + switch (action) { + case MOUSE_SCROLL_N_LINES: + { + DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta, + (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal), + eScrollByLine); + } + break; - case MOUSE_SCROLL_HISTORY: - { - DoScrollHistory(msEvent->delta); - } - break; + case MOUSE_SCROLL_PAGE: + { + DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta, + (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal), + eScrollByPage); + } + break; - case MOUSE_SCROLL_ZOOM: - { - DoScrollZoom(aTargetFrame, msEvent->delta); - } - break; + case MOUSE_SCROLL_PIXELS: + { + DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta, + (msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal), + eScrollByPixel); + } + break; - default: // Including -1 (do nothing) - break; - } - *aStatus = nsEventStatus_eConsumeNoDefault; + case MOUSE_SCROLL_HISTORY: + { + DoScrollHistory(msEvent->delta); + } + break; - } + case MOUSE_SCROLL_ZOOM: + { + DoScrollZoom(aTargetFrame, msEvent->delta); + } + break; + default: // Including -1 (do nothing) + break; + } + *aStatus = nsEventStatus_eConsumeNoDefault; + } + } break; case NS_DRAGDROP_ENTER: diff --git a/content/events/src/nsEventStateManager.h b/content/events/src/nsEventStateManager.h index be81231a25..6e5dacb5ba 100644 --- a/content/events/src/nsEventStateManager.h +++ b/content/events/src/nsEventStateManager.h @@ -295,6 +295,10 @@ protected: nsIFrame* &targetOuterFrame, nsPresContext* &presCtxOuter); + void SendPixelScrollEvent(nsIFrame* aTargetFrame, + nsMouseScrollEvent* aEvent, + nsPresContext* aPresContext, + nsEventStatus* aStatus); typedef enum { eScrollByPixel, eScrollByLine, @@ -445,6 +449,10 @@ protected: nsCOMArray mTabbingFromDocShells; + // Unlocks pixel scrolling + PRPackedBool mLastLineScrollConsumedX; + PRPackedBool mLastLineScrollConsumedY; + #ifdef CLICK_HOLD_CONTEXT_MENUS enum { kClickHoldDelay = 500 } ; // 500ms == 1/2 second diff --git a/content/events/test/Makefile.in b/content/events/test/Makefile.in index 9e8986ad50..de19d27f6e 100644 --- a/content/events/test/Makefile.in +++ b/content/events/test/Makefile.in @@ -51,6 +51,7 @@ _TEST_FILES = \ test_bug336682_1.html \ test_bug336682_2.xul \ test_bug336682.js \ + test_bug350471.xul \ test_bug367781.html \ test_bug368835.html \ test_bug379120.html \ diff --git a/content/events/test/test_bug350471.xul b/content/events/test/test_bug350471.xul new file mode 100644 index 0000000000..484d46fd34 --- /dev/null +++ b/content/events/test/test_bug350471.xul @@ -0,0 +1,229 @@ + + + + + + + Test for Bug 350471 + + + diff --git a/dom/public/idl/base/nsIDOMWindowUtils.idl b/dom/public/idl/base/nsIDOMWindowUtils.idl index c28689faa1..2136e37fd8 100644 --- a/dom/public/idl/base/nsIDOMWindowUtils.idl +++ b/dom/public/idl/base/nsIDOMWindowUtils.idl @@ -130,6 +130,7 @@ interface nsIDOMWindowUtils : nsISupports { /** Synthesize a mouse scroll event for a window. The event types supported * are: * DOMMouseScroll + * MozMousePixelScroll * * Events are sent in coordinates offset by aX and aY from the window. * @@ -143,7 +144,7 @@ interface nsIDOMWindowUtils : nsISupports { * @param aButton button to synthesize * @param aScrollFlags flag bits --- see nsMouseScrollFlags in nsGUIEvent.h * @param aDelta the direction and amount to scroll (in lines or pixels, - * depending on whether kIsPixels is set in aScrollFlags) + * depending on the event type) * @param aModifiers modifiers pressed, using constants defined in nsIDOMNSEvent */ void sendMouseScrollEvent(in AString aType, diff --git a/dom/src/base/nsDOMWindowUtils.cpp b/dom/src/base/nsDOMWindowUtils.cpp index 65ba1c7ce2..4d61622ff3 100644 --- a/dom/src/base/nsDOMWindowUtils.cpp +++ b/dom/src/base/nsDOMWindowUtils.cpp @@ -270,6 +270,8 @@ nsDOMWindowUtils::SendMouseScrollEvent(const nsAString& aType, PRInt32 msg; if (aType.EqualsLiteral("DOMMouseScroll")) msg = NS_MOUSE_SCROLL; + else if (aType.EqualsLiteral("MozMousePixelScroll")) + msg = NS_MOUSE_PIXEL_SCROLL; else return NS_ERROR_UNEXPECTED; diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 37e3e189ae..737ea0e807 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -902,6 +902,9 @@ pref("mousewheel.transaction.timeout", 1500); // mouse wheel scroll transaction is held even if the mouse cursor is moved. pref("mousewheel.transaction.ignoremovedelay", 100); +// Macbook touchpad two finger pixel scrolling +pref("mousewheel.enable_pixel_scrolling", true); + // 0=lines, 1=pages, 2=history , 3=text size pref("mousewheel.withnokey.action",0); pref("mousewheel.withnokey.numlines",1); diff --git a/testing/mochitest/tests/SimpleTest/EventUtils.js b/testing/mochitest/tests/SimpleTest/EventUtils.js index 74153f9c75..87fa1bbde3 100644 --- a/testing/mochitest/tests/SimpleTest/EventUtils.js +++ b/testing/mochitest/tests/SimpleTest/EventUtils.js @@ -230,17 +230,18 @@ function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) * aOffsetY. * * aEvent is an object which may contain the properties: - * shiftKey, ctrlKey, altKey, metaKey, accessKey, button, type, axis, units, delta + * shiftKey, ctrlKey, altKey, metaKey, accessKey, button, type, axis, delta, hasPixels * - * If the type is specified, an mouse scroll event of that type is fired. Otherwise, + * If the type is specified, a mouse scroll event of that type is fired. Otherwise, * "DOMMouseScroll" is used. * * If the axis is specified, it must be one of "horizontal" or "vertical". If not specified, * "vertical" is used. * * 'delta' is the amount to scroll by (can be positive or negative). It must - * be specified. 'units' is the units of 'delta', either "pixels" or "lines"; "lines" - * is the default if 'units' is ommitted. + * be specified. + * + * 'hasPixels' specifies whether kHasPixels should be set in the scrollFlags. * * aWindow is optional, and defaults to the current window object. */ @@ -257,7 +258,7 @@ function synthesizeMouseScroll(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) // See nsMouseScrollFlags in nsGUIEvent.h const kIsVertical = 0x02; const kIsHorizontal = 0x04; - const kIsPixels = 0x08; + const kHasPixels = 0x08; var button = aEvent.button || 0; var modifiers = _parseModifiers(aEvent); @@ -267,10 +268,9 @@ function synthesizeMouseScroll(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) var type = aEvent.type || "DOMMouseScroll"; var axis = aEvent.axis || "vertical"; - var units = aEvent.units || "lines"; var scrollFlags = (axis == "horizontal") ? kIsHorizontal : kIsVertical; - if (units == "pixels") { - scrollFlags |= kIsPixels; + if (aEvent.hasPixels) { + scrollFlags |= kHasPixels; } utils.sendMouseScrollEvent(type, left + aOffsetX, top + aOffsetY, button, scrollFlags, aEvent.delta, modifiers); diff --git a/toolkit/content/tests/widgets/test_mousescroll.xul b/toolkit/content/tests/widgets/test_mousescroll.xul index 122967f68c..a524ecf6fc 100644 --- a/toolkit/content/tests/widgets/test_mousescroll.xul +++ b/toolkit/content/tests/widgets/test_mousescroll.xul @@ -73,59 +73,86 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=378028