зеркало из https://github.com/mozilla/gecko-dev.git
Bug 350471 - Reenable pixel scrolling (two-finger touchpad), r=smaug r=smichaud sr=roc
This commit is contained in:
Родитель
e4827f616e
Коммит
32449e2eac
|
@ -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 }},
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -124,6 +124,7 @@ public:
|
|||
eDOMEvents_pageshow,
|
||||
eDOMEvents_pagehide,
|
||||
eDOMEvents_DOMMouseScroll,
|
||||
eDOMEvents_MozMousePixelScroll,
|
||||
eDOMEvents_offline,
|
||||
eDOMEvents_online,
|
||||
eDOMEvents_copy,
|
||||
|
|
|
@ -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<nsMouseScrollEvent*>(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<nsIContent> 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<nsIFontMetrics> 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) {
|
||||
case NS_MOUSE_PIXEL_SCROLL:
|
||||
{
|
||||
nsMouseScrollEvent *msEvent = static_cast<nsMouseScrollEvent*>(aEvent);
|
||||
|
||||
// Build the preference keys, based on the event properties.
|
||||
nsMouseScrollEvent *msEvent = (nsMouseScrollEvent*) aEvent;
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(actionslot, ".action");
|
||||
NS_NAMED_LITERAL_CSTRING(sysnumlinesslot, ".sysnumlines");
|
||||
|
||||
nsCAutoString baseKey;
|
||||
GetBasePrefKeyForMouseWheel(msEvent, baseKey);
|
||||
|
||||
// Extract the preferences
|
||||
nsCAutoString actionKey(baseKey);
|
||||
actionKey.Append(actionslot);
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case MOUSE_SCROLL_N_LINES:
|
||||
{
|
||||
DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta,
|
||||
(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
|
||||
eScrollByLine);
|
||||
}
|
||||
break;
|
||||
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");
|
||||
|
||||
case MOUSE_SCROLL_PAGE:
|
||||
{
|
||||
DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta,
|
||||
(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
|
||||
eScrollByPage);
|
||||
}
|
||||
break;
|
||||
nsCAutoString baseKey;
|
||||
GetBasePrefKeyForMouseWheel(msEvent, baseKey);
|
||||
|
||||
case MOUSE_SCROLL_PIXELS:
|
||||
{
|
||||
DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta,
|
||||
(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
|
||||
eScrollByPixel);
|
||||
}
|
||||
break;
|
||||
// Extract the preferences
|
||||
nsCAutoString actionKey(baseKey);
|
||||
actionKey.Append(actionslot);
|
||||
|
||||
case MOUSE_SCROLL_HISTORY:
|
||||
{
|
||||
DoScrollHistory(msEvent->delta);
|
||||
}
|
||||
break;
|
||||
nsCAutoString sysNumLinesKey(baseKey);
|
||||
sysNumLinesKey.Append(sysnumlinesslot);
|
||||
|
||||
case MOUSE_SCROLL_ZOOM:
|
||||
{
|
||||
DoScrollZoom(aTargetFrame, msEvent->delta);
|
||||
}
|
||||
break;
|
||||
PRInt32 action = nsContentUtils::GetIntPref(actionKey.get());
|
||||
PRBool useSysNumLines =
|
||||
nsContentUtils::GetBoolPref(sysNumLinesKey.get());
|
||||
|
||||
default: // Including -1 (do nothing)
|
||||
break;
|
||||
if (useSysNumLines) {
|
||||
if (msEvent->scrollFlags & nsMouseScrollEvent::kIsFullPage)
|
||||
action = MOUSE_SCROLL_PAGE;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case MOUSE_SCROLL_N_LINES:
|
||||
{
|
||||
DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta,
|
||||
(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
|
||||
eScrollByLine);
|
||||
}
|
||||
break;
|
||||
|
||||
case MOUSE_SCROLL_PAGE:
|
||||
{
|
||||
DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta,
|
||||
(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
|
||||
eScrollByPage);
|
||||
}
|
||||
break;
|
||||
|
||||
case MOUSE_SCROLL_PIXELS:
|
||||
{
|
||||
DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta,
|
||||
(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
|
||||
eScrollByPixel);
|
||||
}
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
*aStatus = nsEventStatus_eConsumeNoDefault;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NS_DRAGDROP_ENTER:
|
||||
|
|
|
@ -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<nsIDocShell> mTabbingFromDocShells;
|
||||
|
||||
// Unlocks pixel scrolling
|
||||
PRPackedBool mLastLineScrollConsumedX;
|
||||
PRPackedBool mLastLineScrollConsumedY;
|
||||
|
||||
#ifdef CLICK_HOLD_CONTEXT_MENUS
|
||||
enum { kClickHoldDelay = 500 } ; // 500ms == 1/2 second
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=350471
|
||||
-->
|
||||
<window title="Mozilla Bug 350471"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<title>Test for Bug 350471</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js" />
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"/>
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=350471">Mozilla Bug 350471</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<vbox style="height: 150px; background: cyan; overflow: auto;" id="scrollbox">
|
||||
<hbox style="height: 8000px;"><vbox style="width: 8000px;"/></hbox>
|
||||
</vbox>
|
||||
|
||||
<script class="testbody" type="application/javascript;version=1.7"><![CDATA[
|
||||
|
||||
/** Test for Bug 350471 **/
|
||||
|
||||
// This test depends on general.smoothScroll being off.
|
||||
|
||||
const minLineHeight = 10, maxLineHeight = 20;
|
||||
|
||||
function between(x, min, max) (min <= max) ? (min <= x && x <= max) : (max <= x && x <= min);
|
||||
function isbetween(x, min, max, msg) ok(between(x, min, max), msg + " - Expected " + min + " to " + max + ", got " + x);
|
||||
|
||||
function testEventDispatching() {
|
||||
function helper(aAxis, aDelta, aKind, aShiftKey, aCtrlKey, aAltKey, aMetaKey) {
|
||||
let expectedEvents = [];
|
||||
let deltaUnit = "";
|
||||
function listener(e) {
|
||||
if (!expectedEvents.length) {
|
||||
ok(false, "Received an event that I didn't expect. type: " + e.type +
|
||||
", axis: " + e.axis + ", delta: " + e.delta);
|
||||
return;
|
||||
}
|
||||
let expected = expectedEvents.shift();
|
||||
|
||||
["type", "shiftKey", "ctrlKey", "altKey", "metaKey"].forEach(function(field) {
|
||||
is(e[field], expected[field],
|
||||
"e." + field + " (" + e[field] + ") does not match expected value (" + expected[field] + ")");
|
||||
});
|
||||
|
||||
let expectedAxis = expected.axis == "horizontal" ? e.HORIZONTAL_AXIS : e.VERTICAL_AXIS;
|
||||
is(e.axis, expectedAxis,
|
||||
"e.axis (" + e.axis + ") does not match expected value (" + expectedAxis + ")");
|
||||
|
||||
// When modifier keys are pressed, cancel the event.
|
||||
// We don't want to zoom or navigate back / forward (history scroll).
|
||||
if (aShiftKey || aCtrlKey || aAltKey || aMetaKey) {
|
||||
e.preventDefault();
|
||||
// Note: If this is a DOMMouseScroll event without hasPixels, we still
|
||||
// expect a follow-up MozMousePixelScroll event.
|
||||
} else {
|
||||
// Only check the delta if no modifiers are pressed.
|
||||
// History scroll and zoom change the deltas in nsESM::PreHandleEvent.
|
||||
if (deltaUnit == (e.type == "DOMMouseScroll" ? "lines" : "pixels")) {
|
||||
// no unit conversion necessary
|
||||
is(e.detail, expected.delta,
|
||||
"e.detail (" + e.detail + ") does not match expected value (" + expected.delta + ")");
|
||||
} else if (e.type == "MozMousePixelScroll") {
|
||||
// We sent a line scroll event but are receiving a pixel scroll event,
|
||||
// so we need to convert the delta.
|
||||
let minDelta = expected.delta * minLineHeight;
|
||||
let maxDelta = expected.delta * maxLineHeight;
|
||||
isbetween(e.detail, minDelta, maxDelta, "wrong pixel scroll event delta");
|
||||
}
|
||||
}
|
||||
e.stopPropagation();
|
||||
}
|
||||
// Set up the expected values.
|
||||
if (aKind == 0 || aKind == 1) {
|
||||
expectedEvents.push({
|
||||
type: "DOMMouseScroll",
|
||||
axis: aAxis,
|
||||
delta: aDelta,
|
||||
hasPixels: (aKind == 1),
|
||||
shiftKey: aShiftKey,
|
||||
ctrlKey: aCtrlKey,
|
||||
altKey: aAltKey,
|
||||
metaKey: aMetaKey
|
||||
});
|
||||
}
|
||||
if (aKind == 0 || aKind == 2) {
|
||||
expectedEvents.push({
|
||||
type: "MozMousePixelScroll",
|
||||
axis: aAxis,
|
||||
delta: aDelta,
|
||||
shiftKey: aShiftKey,
|
||||
ctrlKey: aCtrlKey,
|
||||
altKey: aAltKey,
|
||||
metaKey: aMetaKey
|
||||
});
|
||||
}
|
||||
deltaUnit = aKind == 2 ? "pixels" : "lines";
|
||||
|
||||
document.addEventListener("DOMMouseScroll", listener, true);
|
||||
document.addEventListener("MozMousePixelScroll", listener, true);
|
||||
|
||||
// Send the event to the documentElement.
|
||||
synthesizeMouseScroll(document.documentElement, 10, 10, expectedEvents[0]);
|
||||
|
||||
document.removeEventListener("DOMMouseScroll", listener, true);
|
||||
document.removeEventListener("MozMousePixelScroll", listener, true);
|
||||
|
||||
// expectedEvents should be empty now. If it's not, print errors.
|
||||
expectedEvents.forEach(function(e) {
|
||||
ok(false, "Didn't receive expected event: " + JSON.toString(e));
|
||||
});
|
||||
};
|
||||
let i = 0;
|
||||
[0, 1, 2].forEach(function(aKind) {
|
||||
["horizontal", "vertical"].forEach(function(aAxis) {
|
||||
[false, true].forEach(function(aShift) {
|
||||
[false, true].forEach(function(aCtrl) {
|
||||
[false, true].forEach(function(aAlt) {
|
||||
[false, true].forEach(function(aMeta) {
|
||||
helper(aAxis, [-5, -1, 0, 1, 5][i++ % 5], aKind, aShift, aCtrl, aAlt, aMeta);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testDefaultHandling() {
|
||||
let scrollbox = document.getElementById("scrollbox");
|
||||
let currentTest = "";
|
||||
|
||||
function scrollWithPreventDefault(aEvent, aDoConsume) {
|
||||
function listener(e) {
|
||||
if (aDoConsume[e.type])
|
||||
e.preventDefault();
|
||||
}
|
||||
scrollbox.addEventListener("DOMMouseScroll", listener, true);
|
||||
scrollbox.addEventListener("MozMousePixelScroll", listener, true);
|
||||
synthesizeMouseScroll(scrollbox, 10, 10, aEvent);
|
||||
scrollbox.removeEventListener("DOMMouseScroll", listener, true);
|
||||
scrollbox.removeEventListener("MozMousePixelScroll", listener, true);
|
||||
}
|
||||
|
||||
function helper(aType, aHasPixels, aAxis, aStart, aDelta, aConsumeLine, aConsumePixel, aPositionShouldChange) {
|
||||
scrollbox.scrollLeft = aStart;
|
||||
scrollbox.scrollTop = aStart;
|
||||
scrollWithPreventDefault({
|
||||
type: aType,
|
||||
axis: aAxis,
|
||||
hasPixels: aHasPixels,
|
||||
delta: aDelta
|
||||
}, {
|
||||
"DOMMouseScroll": aConsumeLine,
|
||||
"MozMousePixelScroll": aConsumePixel
|
||||
});
|
||||
let newPos = scrollbox[aAxis == "horizontal" ? "scrollLeft" : "scrollTop"];
|
||||
let newPosWrongAxis = scrollbox[aAxis == "horizontal" ? "scrollTop" : "scrollLeft"];
|
||||
|
||||
is(newPosWrongAxis, aStart, currentTest + " wrong axis scrolled - type: " + aType);
|
||||
|
||||
if (aPositionShouldChange) {
|
||||
if (aType == "MozMousePixelScroll") {
|
||||
// aDelta is in pixels, no conversion necessary
|
||||
is(newPos, aStart + aDelta, currentTest + " wrong scroll position - type: " + aType);
|
||||
} else {
|
||||
// Use minLineHeight and maxLineHeight as an estimate for the conversion factor.
|
||||
isbetween(newPos, aStart + aDelta * minLineHeight, aStart + aDelta * maxLineHeight,
|
||||
currentTest + " wrong scroll position - type: " + aType);
|
||||
}
|
||||
} else {
|
||||
is(newPos, aStart, currentTest + " The scroll position shouldn't have changed. - type: " + aType);
|
||||
}
|
||||
}
|
||||
|
||||
["horizontal", "vertical"].forEach(function(aAxis) {
|
||||
[-5, 5].forEach(function(aDelta) {
|
||||
[false, true].forEach(function(aConsumeLine) {
|
||||
[false, true].forEach(function(aConsumePixel) {
|
||||
let shouldScroll = !aConsumeLine && !aConsumePixel;
|
||||
|
||||
currentTest = "normal DOMMouseScroll: only scroll if neither line nor pixel scroll are consumed.";
|
||||
helper("DOMMouseScroll", false, aAxis, 4000, aDelta, aConsumeLine, aConsumePixel, shouldScroll);
|
||||
|
||||
currentTest = "DOMMouseScroll with hasPixels: never scroll.";
|
||||
helper("DOMMouseScroll", true, aAxis, 4000, aDelta, aConsumeLine, aConsumePixel, false);
|
||||
|
||||
currentTest = "MozMousePixelScroll (consumed: " + aConsumePixel +
|
||||
") with preceding DOMMouseScroll (consumed: " + aConsumeLine +
|
||||
"): " + (shouldScroll ? "scroll." : "don't scroll.");
|
||||
// It shouldn't matter:
|
||||
// 1. whether hasPixels is set on the preceding DOMMouseScroll event or
|
||||
// 2. whether the preceding DOMMouseScroll event's MozMousePixelScroll event is consumed.
|
||||
helper("DOMMouseScroll", true, aAxis, 4000, aDelta, aConsumeLine, false, false);
|
||||
helper("MozMousePixelScroll", false, aAxis, 4000, aDelta, false, aConsumePixel, shouldScroll);
|
||||
helper("DOMMouseScroll", false, aAxis, 4000, aDelta, aConsumeLine, false, !aConsumeLine);
|
||||
helper("MozMousePixelScroll", false, aAxis, 4000, aDelta, false, aConsumePixel, shouldScroll);
|
||||
helper("DOMMouseScroll", false, aAxis, 4000, aDelta, aConsumeLine, true, false);
|
||||
helper("MozMousePixelScroll", false, aAxis, 4000, aDelta, false, aConsumePixel, shouldScroll);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
Components.utils.import("resource://gre/modules/JSON.jsm");
|
||||
|
||||
testEventDispatching();
|
||||
testDefaultHandling();
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.onload = function() { setTimeout(runTests, 0); };
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
]]></script>
|
||||
|
||||
</window>
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -73,59 +73,86 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=378028
|
|||
<script type="application/javascript"><![CDATA[
|
||||
|
||||
/** Test for Bug 378028 **/
|
||||
/* and for Bug 350471 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
/* There are three kinds of scroll events:
|
||||
1. line scrolls without hasPixels
|
||||
2. line scrolls with hasPixels
|
||||
3. pixel scrolls
|
||||
Listboxes and arrowscrollboxes (DOM event scrolling) should only react to
|
||||
line scrolls and ignore hasPixels.
|
||||
Richlistboxes ("native" scrolling) should be scrollable by kind 1 and 3.
|
||||
*/
|
||||
const kinds = [
|
||||
{ eventType: "DOMMouseScroll", hasPixels: false, shouldScrollDOM: true, shouldScrollNative: true },
|
||||
{ eventType: "DOMMouseScroll", hasPixels: true, shouldScrollDOM: true, shouldScrollNative: false },
|
||||
{ eventType: "MozMousePixelScroll", hasPixels: false, shouldScrollDOM: false, shouldScrollNative: true }
|
||||
];
|
||||
|
||||
|
||||
function testListbox(id)
|
||||
{
|
||||
var listbox = document.getElementById(id);
|
||||
|
||||
function helper(aStart, aDelta)
|
||||
function helper(aStart, aDelta, aKind)
|
||||
{
|
||||
listbox.scrollToIndex(aStart);
|
||||
synthesizeMouseScroll(listbox, 10, 10,
|
||||
{axis:"vertical", delta:aDelta});
|
||||
is(listbox.getIndexOfFirstVisibleRow(), aStart + aDelta,
|
||||
"mouse-scroll of '" + id + "' vertical starting " + aStart + " delta " + aDelta);
|
||||
{axis:"vertical", delta:aDelta, type:aKind.eventType,
|
||||
hasPixels:aKind.hasPixels});
|
||||
is(listbox.getIndexOfFirstVisibleRow(), aKind.shouldScrollDOM ? aStart + aDelta : aStart,
|
||||
"mouse-scroll of '" + id + "' vertical starting " + aStart + " delta " + aDelta
|
||||
+ " eventType " + aKind.eventType + " hasPixels " + aKind.hasPixels);
|
||||
|
||||
// Check that horizontal scrolling has no effect
|
||||
listbox.scrollToIndex(aStart);
|
||||
synthesizeMouseScroll(listbox, 10, 10,
|
||||
{axis:"horizontal", delta:aDelta});
|
||||
{axis:"horizontal", delta:aDelta, type:aKind.eventType,
|
||||
hasPixels:aKind.hasPixels});
|
||||
is(listbox.getIndexOfFirstVisibleRow(), aStart,
|
||||
"mouse-scroll of '" + id + "' horizontal starting " + aStart + " delta " + aDelta);
|
||||
"mouse-scroll of '" + id + "' horizontal starting " + aStart + " delta " + aDelta
|
||||
+ " eventType " + aKind.eventType + " hasPixels " + aKind.hasPixels);
|
||||
}
|
||||
|
||||
helper(2, -1);
|
||||
helper(2, 1);
|
||||
helper(2, -2);
|
||||
helper(2, 2);
|
||||
kinds.forEach(function(aKind) {
|
||||
helper(2, -1, aKind);
|
||||
helper(2, 1, aKind);
|
||||
helper(2, -2, aKind);
|
||||
helper(2, 2, aKind);
|
||||
});
|
||||
}
|
||||
|
||||
function testRichListbox(id)
|
||||
{
|
||||
var listbox = document.getElementById(id);
|
||||
|
||||
function helper(aStart, aDelta, aExpected)
|
||||
function helper(aStart, aDelta, aExpected, aKind)
|
||||
{
|
||||
listbox.scrollToIndex(aStart);
|
||||
synthesizeMouseScroll(listbox, 10, 10,
|
||||
{axis:"vertical", delta:aDelta});
|
||||
is(listbox.getIndexOfFirstVisibleRow(), aExpected,
|
||||
"mouse-scroll of '" + id + "' vertical starting " + aStart + " delta " + aDelta);
|
||||
{axis:"vertical", delta:aDelta, type:aKind.eventType,
|
||||
hasPixels:aKind.hasPixels});
|
||||
is(listbox.getIndexOfFirstVisibleRow(), aKind.shouldScrollNative ? aExpected : aStart,
|
||||
"mouse-scroll of '" + id + "' vertical starting " + aStart + " delta " + aDelta
|
||||
+ " eventType " + aKind.eventType + " hasPixels " + aKind.hasPixels);
|
||||
|
||||
// Check that horizontal scrolling has no effect
|
||||
listbox.scrollToIndex(aStart);
|
||||
synthesizeMouseScroll(listbox, 10, 10,
|
||||
{axis:"horizontal", delta:aDelta});
|
||||
{axis:"horizontal", delta:aDelta, type:aKind.eventType,
|
||||
hasPixels:aKind.hasPixels});
|
||||
is(listbox.getIndexOfFirstVisibleRow(), aStart,
|
||||
"mouse-scroll of '" + id + "' horizontal starting " + aStart + " delta " + aDelta);
|
||||
"mouse-scroll of '" + id + "' horizontal starting " + aStart + " delta " + aDelta
|
||||
+ " eventType " + aKind.eventType + " hasPixels " + aKind.hasPixels);
|
||||
}
|
||||
|
||||
// richlistbox currently uses native XUL scrolling, so the "line"
|
||||
// amounts don't necessarily correspond 1-to-1 with listbox items. So
|
||||
// we just check that scrolling up/down a lot hits the first/last items
|
||||
helper(2, -100, 0);
|
||||
helper(2, 100, listbox.getRowCount() - listbox.getNumberOfVisibleRows());
|
||||
kinds.forEach(function(aKind) {
|
||||
helper(2, -100, 0, aKind);
|
||||
helper(2, 100, listbox.getRowCount() - listbox.getNumberOfVisibleRows(), aKind);
|
||||
});
|
||||
}
|
||||
|
||||
function testArrowScrollbox(id)
|
||||
|
@ -134,7 +161,7 @@ function testArrowScrollbox(id)
|
|||
var scrollBoxObject = scrollbox.scrollBoxObject;
|
||||
var orient = scrollbox.getAttribute("orient");
|
||||
|
||||
function helper(aStart, aDelta, aExpected)
|
||||
function helper(aStart, aDelta, aExpected, aKind)
|
||||
{
|
||||
var xpos = {};
|
||||
var ypos = {};
|
||||
|
@ -142,23 +169,27 @@ function testArrowScrollbox(id)
|
|||
|
||||
scrollBoxObject.scrollTo(aStart, aStart);
|
||||
synthesizeMouseScroll(scrollbox, 5, 5,
|
||||
{axis:"vertical", delta:aDelta});
|
||||
{axis:"vertical", delta:aDelta, type:aKind.eventType,
|
||||
hasPixels:aKind.hasPixels});
|
||||
scrollBoxObject.getPosition(xpos, ypos);
|
||||
// Note, vertical mouse scrolling is allowed to scroll horizontal
|
||||
// arrowscrollboxes, because many users have no horizontal mouse scroll
|
||||
// capability
|
||||
is(pos.value, aExpected,
|
||||
"mouse-scroll of '" + id + "' vertical starting " + aStart + " delta " + aDelta);
|
||||
is(pos.value, aKind.shouldScrollDOM ? aExpected : aStart,
|
||||
"mouse-scroll of '" + id + "' vertical starting " + aStart + " delta " + aDelta
|
||||
+ " eventType " + aKind.eventType + " hasPixels " + aKind.hasPixels);
|
||||
|
||||
scrollBoxObject.scrollTo(aStart, aStart);
|
||||
synthesizeMouseScroll(scrollbox, 5, 5,
|
||||
{axis:"horizontal", delta:aDelta});
|
||||
{axis:"horizontal", delta:aDelta, type:aKind.eventType,
|
||||
hasPixels:aKind.hasPixels});
|
||||
// horizontal mouse scrolling is never allowed to scroll vertical
|
||||
// arrowscrollboxes
|
||||
scrollBoxObject.getPosition(xpos, ypos);
|
||||
var expected = orient == "horizontal" ? aExpected : aStart;
|
||||
var expected = (aKind.shouldScrollDOM && (orient == "horizontal")) ? aExpected : aStart;
|
||||
is(pos.value, expected,
|
||||
"mouse-scroll of '" + id + "' horizontal starting " + aStart + " delta " + aDelta);
|
||||
"mouse-scroll of '" + id + "' horizontal starting " + aStart + " delta " + aDelta
|
||||
+ " eventType " + aKind.eventType + " hasPixels " + aKind.hasPixels);
|
||||
}
|
||||
|
||||
var scrolledWidth = {};
|
||||
|
@ -168,8 +199,10 @@ function testArrowScrollbox(id)
|
|||
var scrollMaxY = scrolledHeight.value - scrollBoxObject.height;
|
||||
var scrollMax = orient == "horizontal" ? scrollMaxX : scrollMaxY;
|
||||
|
||||
helper(50, -100, 0);
|
||||
helper(50, 100, scrollMax);
|
||||
kinds.forEach(function(aKind) {
|
||||
helper(50, -100, 0, aKind);
|
||||
helper(50, 100, scrollMax, aKind);
|
||||
});
|
||||
}
|
||||
|
||||
function runTests()
|
||||
|
|
|
@ -1096,24 +1096,37 @@ function testtag_tree_column_reorder()
|
|||
|
||||
function testtag_tree_mousescroll(aTree)
|
||||
{
|
||||
function helper(aStart, aDelta)
|
||||
/* Scroll event kinds, see test_mousescroll.xul */
|
||||
const kinds = [
|
||||
{ eventType: "DOMMouseScroll", hasPixels: false, shouldScrollDOM: true, shouldScrollNative: true },
|
||||
{ eventType: "DOMMouseScroll", hasPixels: true, shouldScrollDOM: true, shouldScrollNative: false },
|
||||
{ eventType: "MozMousePixelScroll", hasPixels: false, shouldScrollDOM: false, shouldScrollNative: true }
|
||||
];
|
||||
function helper(aStart, aDelta, aKind)
|
||||
{
|
||||
aTree.treeBoxObject.scrollToRow(aStart);
|
||||
synthesizeMouseScroll(aTree.body, 1, 1,
|
||||
{type:"DOMMouseScroll", axis:"vertical", delta:aDelta});
|
||||
is(aTree.treeBoxObject.getFirstVisibleRow(), aStart + aDelta, "mouse-scroll vertical starting " + aStart + " delta " + aDelta);
|
||||
{axis:"vertical", delta:aDelta, type:aKind.eventType,
|
||||
hasPixels:aKind.hasPixels});
|
||||
var expected = aKind.shouldScrollDOM ? aStart + aDelta : aStart;
|
||||
is(aTree.treeBoxObject.getFirstVisibleRow(), expected, "mouse-scroll vertical starting " + aStart + " delta " + aDelta
|
||||
+ " eventType " + aKind.eventType + " hasPixels " + aKind.hasPixels);
|
||||
|
||||
aTree.treeBoxObject.scrollToRow(aStart);
|
||||
// Check that horizontal scrolling has no effect
|
||||
synthesizeMouseScroll(aTree.body, 1, 1,
|
||||
{type:"DOMMouseScroll", axis:"horizontal", delta:aDelta});
|
||||
is(aTree.treeBoxObject.getFirstVisibleRow(), aStart, "mouse-scroll horizontal starting " + aStart + " delta " + aDelta);
|
||||
{axis:"horizontal", delta:aDelta, type:aKind.eventType,
|
||||
hasPixels:aKind.hasPixels});
|
||||
is(aTree.treeBoxObject.getFirstVisibleRow(), aStart, "mouse-scroll horizontal starting " + aStart + " delta " + aDelta
|
||||
+ " eventType " + aKind.eventType + " hasPixels " + aKind.hasPixels);
|
||||
}
|
||||
|
||||
helper(2, -1);
|
||||
helper(2, 1);
|
||||
helper(2, -2);
|
||||
helper(2, 2);
|
||||
kinds.forEach(function(aKind) {
|
||||
helper(2, -1, aKind);
|
||||
helper(2, 1, aKind);
|
||||
helper(2, -2, aKind);
|
||||
helper(2, 2, aKind);
|
||||
});
|
||||
}
|
||||
|
||||
function synthesizeColumnDrag(aTree, aMouseDownColumnNumber, aMouseUpColumnNumber, aAfter)
|
||||
|
|
|
@ -271,6 +271,7 @@ class nsHashKey;
|
|||
// Scroll events
|
||||
#define NS_MOUSE_SCROLL_START 1600
|
||||
#define NS_MOUSE_SCROLL (NS_MOUSE_SCROLL_START)
|
||||
#define NS_MOUSE_PIXEL_SCROLL (NS_MOUSE_SCROLL_START + 1)
|
||||
|
||||
#define NS_SCROLLPORT_START 1700
|
||||
#define NS_SCROLLPORT_UNDERFLOW (NS_SCROLLPORT_START)
|
||||
|
@ -849,6 +850,43 @@ public:
|
|||
nsTextEventReply theReply;
|
||||
};
|
||||
|
||||
/* Mouse Scroll Events: Line Scrolling, Pixel Scrolling and Common Event Flows
|
||||
*
|
||||
* There are two common event flows:
|
||||
* (1) Normal line scrolling:
|
||||
* 1. An NS_MOUSE_SCROLL event without kHasPixels is dispatched to Gecko.
|
||||
* 2. A DOMMouseScroll event is sent into the DOM.
|
||||
* 3. A MozMousePixelScroll event is sent into the DOM.
|
||||
* 4. If neither event has been consumed, the default handling of the
|
||||
* NS_MOUSE_SCROLL event is executed.
|
||||
*
|
||||
* (2) Pixel scrolling:
|
||||
* 1. An NS_MOUSE_SCROLL event with kHasPixels is dispatched to Gecko.
|
||||
* 2. A DOMMouseScroll event is sent into the DOM.
|
||||
* 3. No scrolling takes place in the default handler.
|
||||
* 4. An NS_MOUSE_PIXEL_SCROLL event is dispatched to Gecko.
|
||||
* 5. A MozMousePixelScroll event is sent into the DOM.
|
||||
* 6. If neither the NS_MOUSE_PIXELSCROLL event nor the preceding
|
||||
* NS_MOUSE_SCROLL event have been consumed, the default handler scrolls.
|
||||
* 7. Steps 4.-6. are repeated for every pixel scroll that belongs to
|
||||
* the announced line scroll. Once enough pixels have been sent to
|
||||
* complete a line, a new NS_MOUSE_SCROLL event is sent (goto step 1.).
|
||||
*
|
||||
* If a DOMMouseScroll event has been preventDefaulted, the associated
|
||||
* following MozMousePixelScroll events are still sent - they just don't result
|
||||
* in any scrolling (their default handler isn't executed).
|
||||
*
|
||||
* How many pixel scrolls make up one line scroll is decided in the widget layer
|
||||
* where the NS_MOUSE(_PIXEL)_SCROLL events are created.
|
||||
*
|
||||
* This event flow model satisfies several requirements:
|
||||
* - DOMMouseScroll handlers don't need to be aware of the existence of pixel
|
||||
* scrolling.
|
||||
* - preventDefault on a DOMMouseScroll event results in no scrolling.
|
||||
* - DOMMouseScroll events aren't polluted with a kHasPixels flag.
|
||||
* - You can make use of pixel scroll DOM events (MozMousePixelScroll).
|
||||
*/
|
||||
|
||||
class nsMouseScrollEvent : public nsMouseEvent_base
|
||||
{
|
||||
public:
|
||||
|
@ -856,7 +894,15 @@ public:
|
|||
kIsFullPage = 1 << 0,
|
||||
kIsVertical = 1 << 1,
|
||||
kIsHorizontal = 1 << 2,
|
||||
kIsPixels = 1 << 3
|
||||
kHasPixels = 1 << 3 // Marks line scroll events that are provided as
|
||||
// a fallback for pixel scroll events.
|
||||
// These scroll events are used by things that can't
|
||||
// be scrolled pixel-wise, like trees. You should
|
||||
// ignore them when processing pixel scroll events
|
||||
// to avoid double-processing the same scroll gesture.
|
||||
// When kHasPixels is set, the event is guaranteed to
|
||||
// be followed up by an event that contains pixel
|
||||
// scrolling information.
|
||||
};
|
||||
|
||||
nsMouseScrollEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
|
||||
|
|
|
@ -99,6 +99,22 @@ extern "C" long TSMProcessRawKeyEvent(EventRef carbonEvent);
|
|||
|
||||
@end
|
||||
|
||||
// Needed to support pixel scrolling.
|
||||
// See http://developer.apple.com/qa/qa2005/qa1453.html.
|
||||
// kEventMouseScroll is only defined on 10.5+. Using the moz prefix avoids
|
||||
// potential symbol conflicts.
|
||||
// This should be changed when 10.4 support is dropped.
|
||||
enum {
|
||||
mozkEventMouseScroll = 11
|
||||
};
|
||||
|
||||
// Support for pixel scroll deltas, not part of NSEvent.h
|
||||
// See http://lists.apple.com/archives/cocoa-dev/2007/Feb/msg00050.html
|
||||
@interface NSEvent (DeviceDelta)
|
||||
- (float)deviceDeltaX;
|
||||
- (float)deviceDeltaY;
|
||||
@end
|
||||
|
||||
@interface ChildView : NSView<
|
||||
#ifdef ACCESSIBILITY
|
||||
mozAccessible,
|
||||
|
|
|
@ -3493,77 +3493,119 @@ static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted,
|
|||
if (!mGeckoChild)
|
||||
return;
|
||||
|
||||
float scrollDelta;
|
||||
float scrollDelta = 0;
|
||||
float scrollDeltaPixels = 0;
|
||||
PRBool checkPixels = PR_TRUE;
|
||||
|
||||
if (inAxis & nsMouseScrollEvent::kIsVertical)
|
||||
scrollDelta = -[theEvent deltaY];
|
||||
else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
|
||||
scrollDelta = -[theEvent deltaX];
|
||||
else
|
||||
return; // caller screwed up
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
if (prefs)
|
||||
prefs->GetBoolPref("mousewheel.enable_pixel_scrolling", &checkPixels);
|
||||
|
||||
if (scrollDelta == 0)
|
||||
// No sense in firing off a Gecko event. Note that as of 10.4 Tiger,
|
||||
// a single NSScrollWheel event might result in deltaX = deltaY = 0.
|
||||
return;
|
||||
|
||||
nsMouseScrollEvent geckoEvent(PR_TRUE, NS_MOUSE_SCROLL, nsnull);
|
||||
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
||||
geckoEvent.scrollFlags |= inAxis;
|
||||
|
||||
// Gecko only understands how to scroll by an integer value. Using floor
|
||||
// and ceil is better than truncating the fraction, especially when
|
||||
// |delta| < 1.
|
||||
if (scrollDelta < 0)
|
||||
geckoEvent.delta = (PRInt32)floorf(scrollDelta);
|
||||
else
|
||||
geckoEvent.delta = (PRInt32)ceilf(scrollDelta);
|
||||
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
mGeckoChild->DispatchWindowEvent(geckoEvent);
|
||||
if (!mGeckoChild)
|
||||
return;
|
||||
|
||||
// dispatch scroll wheel carbon event for plugins
|
||||
{
|
||||
EventRef theEvent;
|
||||
OSStatus err = ::MacCreateEvent(NULL,
|
||||
kEventClassMouse,
|
||||
kEventMouseWheelMoved,
|
||||
TicksToEventTime(TickCount()),
|
||||
kEventAttributeUserEvent,
|
||||
&theEvent);
|
||||
if (err == noErr) {
|
||||
EventMouseWheelAxis axis;
|
||||
if (inAxis & nsMouseScrollEvent::kIsVertical)
|
||||
axis = kEventMouseWheelAxisY;
|
||||
else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
|
||||
axis = kEventMouseWheelAxisX;
|
||||
|
||||
SetEventParameter(theEvent,
|
||||
kEventParamMouseWheelAxis,
|
||||
typeMouseWheelAxis,
|
||||
sizeof(EventMouseWheelAxis),
|
||||
&axis);
|
||||
|
||||
SInt32 delta = (SInt32)-geckoEvent.delta;
|
||||
SetEventParameter(theEvent,
|
||||
kEventParamMouseWheelDelta,
|
||||
typeLongInteger,
|
||||
sizeof(SInt32),
|
||||
&delta);
|
||||
|
||||
Point mouseLoc;
|
||||
::GetGlobalMouse(&mouseLoc);
|
||||
SetEventParameter(theEvent,
|
||||
kEventParamMouseLocation,
|
||||
typeQDPoint,
|
||||
sizeof(Point),
|
||||
&mouseLoc);
|
||||
|
||||
::SendEventToEventTarget(theEvent, GetWindowEventTarget((WindowRef)[[self window] windowRef]));
|
||||
ReleaseEvent(theEvent);
|
||||
EventRef theCarbonEvent = [theEvent _eventRef];
|
||||
UInt32 carbonEventKind = theCarbonEvent ? ::GetEventKind(theCarbonEvent) : 0;
|
||||
// Calling deviceDeltaX or deviceDeltaY on theEvent will trigger a Cocoa
|
||||
// assertion and an Objective-C NSInternalInconsistencyException if the
|
||||
// underlying "Carbon" event doesn't contain pixel scrolling information.
|
||||
// For these events, carbonEventKind is kEventMouseWheelMoved instead of
|
||||
// kEventMouseScroll.
|
||||
if (carbonEventKind != mozkEventMouseScroll)
|
||||
checkPixels = PR_FALSE;
|
||||
// Some scrolling devices supports pixel scrolling, e.g. a Macbook
|
||||
// touchpad or a Mighty Mouse. On those devices, [event deviceDeltaX/Y]
|
||||
// contains the amount of pixels to scroll.
|
||||
if (inAxis & nsMouseScrollEvent::kIsVertical) {
|
||||
scrollDelta = -[theEvent deltaY];
|
||||
if (checkPixels && (scrollDelta == 0 || scrollDelta != floor(scrollDelta))) {
|
||||
scrollDeltaPixels = -[theEvent deviceDeltaY];
|
||||
}
|
||||
} else if (inAxis & nsMouseScrollEvent::kIsHorizontal) {
|
||||
scrollDelta = -[theEvent deltaX];
|
||||
if (checkPixels && (scrollDelta == 0 || scrollDelta != floor(scrollDelta))) {
|
||||
scrollDeltaPixels = -[theEvent deviceDeltaX];
|
||||
}
|
||||
} else {
|
||||
return; // caller screwed up
|
||||
}
|
||||
|
||||
BOOL hasPixels = (scrollDeltaPixels != 0);
|
||||
|
||||
if (!hasPixels && scrollDelta == 0)
|
||||
// No sense in firing off a Gecko event.
|
||||
return;
|
||||
|
||||
if (scrollDelta != 0) {
|
||||
// Send the line scroll event.
|
||||
nsMouseScrollEvent geckoEvent(PR_TRUE, NS_MOUSE_SCROLL, nsnull);
|
||||
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
||||
geckoEvent.scrollFlags |= inAxis;
|
||||
|
||||
if (hasPixels)
|
||||
geckoEvent.scrollFlags |= nsMouseScrollEvent::kHasPixels;
|
||||
|
||||
// Gecko only understands how to scroll by an integer value. Using floor
|
||||
// and ceil is better than truncating the fraction, especially when
|
||||
// |delta| < 1.
|
||||
if (scrollDelta < 0)
|
||||
geckoEvent.delta = (PRInt32)floorf(scrollDelta);
|
||||
else
|
||||
geckoEvent.delta = (PRInt32)ceilf(scrollDelta);
|
||||
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
mGeckoChild->DispatchWindowEvent(geckoEvent);
|
||||
if (!mGeckoChild)
|
||||
return;
|
||||
|
||||
// dispatch scroll wheel carbon event for plugins
|
||||
{
|
||||
EventRef theEvent;
|
||||
OSStatus err = ::MacCreateEvent(NULL,
|
||||
kEventClassMouse,
|
||||
kEventMouseWheelMoved,
|
||||
TicksToEventTime(TickCount()),
|
||||
kEventAttributeUserEvent,
|
||||
&theEvent);
|
||||
if (err == noErr) {
|
||||
EventMouseWheelAxis axis;
|
||||
if (inAxis & nsMouseScrollEvent::kIsVertical)
|
||||
axis = kEventMouseWheelAxisY;
|
||||
else if (inAxis & nsMouseScrollEvent::kIsHorizontal)
|
||||
axis = kEventMouseWheelAxisX;
|
||||
|
||||
SetEventParameter(theEvent,
|
||||
kEventParamMouseWheelAxis,
|
||||
typeMouseWheelAxis,
|
||||
sizeof(EventMouseWheelAxis),
|
||||
&axis);
|
||||
|
||||
SInt32 delta = (SInt32)-geckoEvent.delta;
|
||||
SetEventParameter(theEvent,
|
||||
kEventParamMouseWheelDelta,
|
||||
typeLongInteger,
|
||||
sizeof(SInt32),
|
||||
&delta);
|
||||
|
||||
Point mouseLoc;
|
||||
::GetGlobalMouse(&mouseLoc);
|
||||
SetEventParameter(theEvent,
|
||||
kEventParamMouseLocation,
|
||||
typeQDPoint,
|
||||
sizeof(Point),
|
||||
&mouseLoc);
|
||||
|
||||
::SendEventToEventTarget(theEvent, GetWindowEventTarget((WindowRef)[[self window] windowRef]));
|
||||
ReleaseEvent(theEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasPixels) {
|
||||
// Send the pixel scroll event.
|
||||
nsMouseScrollEvent geckoEvent(PR_TRUE, NS_MOUSE_PIXEL_SCROLL, nsnull);
|
||||
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
||||
geckoEvent.scrollFlags |= inAxis;
|
||||
geckoEvent.delta = NSToIntRound(scrollDeltaPixels);
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
mGeckoChild->DispatchWindowEvent(geckoEvent);
|
||||
}
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
|
|
Загрузка…
Ссылка в новой задаче