зеркало из https://github.com/mozilla/gecko-dev.git
Bug 984271 part.1-1 Separate mouse wheel handling helper classes and methods from nsEventStateManager.cpp r=smaug
--HG-- rename : dom/events/nsEventStateManager.cpp => dom/events/WheelHandlingHelper.cpp rename : dom/events/nsEventStateManager.cpp => dom/events/WheelHandlingHelper.h
This commit is contained in:
Родитель
a89e1563ef
Коммит
1ddce7fa83
|
@ -0,0 +1,526 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 sw=2 et tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WheelHandlingHelper.h"
|
||||
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/MouseEvents.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsEventStateManager.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "prtime.h"
|
||||
#include "Units.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/******************************************************************/
|
||||
/* mozilla::DeltaValues */
|
||||
/******************************************************************/
|
||||
|
||||
DeltaValues::DeltaValues(WidgetWheelEvent* aEvent)
|
||||
: deltaX(aEvent->deltaX)
|
||||
, deltaY(aEvent->deltaY)
|
||||
{
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* mozilla::WheelHandlingUtils */
|
||||
/******************************************************************/
|
||||
|
||||
/* static */ bool
|
||||
WheelHandlingUtils::CanScrollInRange(nscoord aMin, nscoord aValue, nscoord aMax,
|
||||
double aDirection)
|
||||
{
|
||||
return aDirection > 0.0 ? aValue < static_cast<double>(aMax) :
|
||||
static_cast<double>(aMin) < aValue;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
WheelHandlingUtils::CanScrollOn(nsIScrollableFrame* aScrollFrame,
|
||||
double aDirectionX, double aDirectionY)
|
||||
{
|
||||
MOZ_ASSERT(aScrollFrame);
|
||||
NS_ASSERTION(aDirectionX || aDirectionY,
|
||||
"One of the delta values must be non-zero at least");
|
||||
|
||||
nsPoint scrollPt = aScrollFrame->GetScrollPosition();
|
||||
nsRect scrollRange = aScrollFrame->GetScrollRange();
|
||||
uint32_t directions = aScrollFrame->GetPerceivedScrollingDirections();
|
||||
|
||||
return (aDirectionX && (directions & nsIScrollableFrame::HORIZONTAL) &&
|
||||
CanScrollInRange(scrollRange.x, scrollPt.x,
|
||||
scrollRange.XMost(), aDirectionX)) ||
|
||||
(aDirectionY && (directions & nsIScrollableFrame::VERTICAL) &&
|
||||
CanScrollInRange(scrollRange.y, scrollPt.y,
|
||||
scrollRange.YMost(), aDirectionY));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/******************************************************************/
|
||||
/* nsMouseWheelTransaction */
|
||||
/******************************************************************/
|
||||
|
||||
nsWeakFrame nsMouseWheelTransaction::sTargetFrame(nullptr);
|
||||
uint32_t nsMouseWheelTransaction::sTime = 0;
|
||||
uint32_t nsMouseWheelTransaction::sMouseMoved = 0;
|
||||
nsITimer* nsMouseWheelTransaction::sTimer = nullptr;
|
||||
int32_t nsMouseWheelTransaction::sScrollSeriesCounter = 0;
|
||||
bool nsMouseWheelTransaction::sOwnScrollbars = false;
|
||||
|
||||
bool
|
||||
nsMouseWheelTransaction::OutOfTime(uint32_t aBaseTime, uint32_t aThreshold)
|
||||
{
|
||||
uint32_t now = PR_IntervalToMilliseconds(PR_IntervalNow());
|
||||
return (now - aBaseTime > aThreshold);
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::OwnScrollbars(bool aOwn)
|
||||
{
|
||||
sOwnScrollbars = aOwn;
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::BeginTransaction(nsIFrame* aTargetFrame,
|
||||
WidgetWheelEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(!sTargetFrame, "previous transaction is not finished!");
|
||||
MOZ_ASSERT(aEvent->message == NS_WHEEL_WHEEL,
|
||||
"Transaction must be started with a wheel event");
|
||||
nsScrollbarsForWheel::OwnWheelTransaction(false);
|
||||
sTargetFrame = aTargetFrame;
|
||||
sScrollSeriesCounter = 0;
|
||||
if (!UpdateTransaction(aEvent)) {
|
||||
NS_ERROR("BeginTransaction is called even cannot scroll the frame");
|
||||
EndTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsMouseWheelTransaction::UpdateTransaction(WidgetWheelEvent* aEvent)
|
||||
{
|
||||
nsIScrollableFrame* sf = GetTargetFrame()->GetScrollTargetFrame();
|
||||
NS_ENSURE_TRUE(sf, false);
|
||||
|
||||
if (!WheelHandlingUtils::CanScrollOn(sf, aEvent->deltaX, aEvent->deltaY)) {
|
||||
OnFailToScrollTarget();
|
||||
// We should not modify the transaction state when the view will not be
|
||||
// scrolled actually.
|
||||
return false;
|
||||
}
|
||||
|
||||
SetTimeout();
|
||||
|
||||
if (sScrollSeriesCounter != 0 && OutOfTime(sTime, kScrollSeriesTimeout))
|
||||
sScrollSeriesCounter = 0;
|
||||
sScrollSeriesCounter++;
|
||||
|
||||
// We should use current time instead of WidgetEvent.time.
|
||||
// 1. Some events doesn't have the correct creation time.
|
||||
// 2. If the computer runs slowly by other processes eating the CPU resource,
|
||||
// the event creation time doesn't keep real time.
|
||||
sTime = PR_IntervalToMilliseconds(PR_IntervalNow());
|
||||
sMouseMoved = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::MayEndTransaction()
|
||||
{
|
||||
if (!sOwnScrollbars && nsScrollbarsForWheel::IsActive()) {
|
||||
nsScrollbarsForWheel::OwnWheelTransaction(true);
|
||||
} else {
|
||||
EndTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::EndTransaction()
|
||||
{
|
||||
if (sTimer)
|
||||
sTimer->Cancel();
|
||||
sTargetFrame = nullptr;
|
||||
sScrollSeriesCounter = 0;
|
||||
if (sOwnScrollbars) {
|
||||
sOwnScrollbars = false;
|
||||
nsScrollbarsForWheel::OwnWheelTransaction(false);
|
||||
nsScrollbarsForWheel::Inactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::OnEvent(WidgetEvent* aEvent)
|
||||
{
|
||||
if (!sTargetFrame)
|
||||
return;
|
||||
|
||||
if (OutOfTime(sTime, GetTimeoutTime())) {
|
||||
// Even if the scroll event which is handled after timeout, but onTimeout
|
||||
// was not fired by timer, then the scroll event will scroll old frame,
|
||||
// therefore, we should call OnTimeout here and ensure to finish the old
|
||||
// transaction.
|
||||
OnTimeout(nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aEvent->message) {
|
||||
case NS_WHEEL_WHEEL:
|
||||
if (sMouseMoved != 0 &&
|
||||
OutOfTime(sMouseMoved, GetIgnoreMoveDelayTime())) {
|
||||
// Terminate the current mousewheel transaction if the mouse moved more
|
||||
// than ignoremovedelay milliseconds ago
|
||||
EndTransaction();
|
||||
}
|
||||
return;
|
||||
case NS_MOUSE_MOVE:
|
||||
case NS_DRAGDROP_OVER: {
|
||||
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
|
||||
if (mouseEvent->IsReal()) {
|
||||
// If the cursor is moving to be outside the frame,
|
||||
// terminate the scrollwheel transaction.
|
||||
nsIntPoint pt = GetScreenPoint(mouseEvent);
|
||||
nsIntRect r = sTargetFrame->GetScreenRectExternal();
|
||||
if (!r.Contains(pt)) {
|
||||
EndTransaction();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the cursor is moving inside the frame, and it is less than
|
||||
// ignoremovedelay milliseconds since the last scroll operation, ignore
|
||||
// the mouse move; otherwise, record the current mouse move time to be
|
||||
// checked later
|
||||
if (OutOfTime(sTime, GetIgnoreMoveDelayTime())) {
|
||||
if (sMouseMoved == 0)
|
||||
sMouseMoved = PR_IntervalToMilliseconds(PR_IntervalNow());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case NS_KEY_PRESS:
|
||||
case NS_KEY_UP:
|
||||
case NS_KEY_DOWN:
|
||||
case NS_MOUSE_BUTTON_UP:
|
||||
case NS_MOUSE_BUTTON_DOWN:
|
||||
case NS_MOUSE_DOUBLECLICK:
|
||||
case NS_MOUSE_CLICK:
|
||||
case NS_CONTEXTMENU:
|
||||
case NS_DRAGDROP_DROP:
|
||||
EndTransaction();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::Shutdown()
|
||||
{
|
||||
NS_IF_RELEASE(sTimer);
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::OnFailToScrollTarget()
|
||||
{
|
||||
NS_PRECONDITION(sTargetFrame, "We don't have mouse scrolling transaction");
|
||||
|
||||
if (Preferences::GetBool("test.mousescroll", false)) {
|
||||
// This event is used for automated tests, see bug 442774.
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
sTargetFrame->GetContent()->OwnerDoc(),
|
||||
sTargetFrame->GetContent(),
|
||||
NS_LITERAL_STRING("MozMouseScrollFailed"),
|
||||
true, true);
|
||||
}
|
||||
// The target frame might be destroyed in the event handler, at that time,
|
||||
// we need to finish the current transaction
|
||||
if (!sTargetFrame) {
|
||||
EndTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::OnTimeout(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
if (!sTargetFrame) {
|
||||
// The transaction target was destroyed already
|
||||
EndTransaction();
|
||||
return;
|
||||
}
|
||||
// Store the sTargetFrame, the variable becomes null in EndTransaction.
|
||||
nsIFrame* frame = sTargetFrame;
|
||||
// We need to finish current transaction before DOM event firing. Because
|
||||
// the next DOM event might create strange situation for us.
|
||||
MayEndTransaction();
|
||||
|
||||
if (Preferences::GetBool("test.mousescroll", false)) {
|
||||
// This event is used for automated tests, see bug 442774.
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
frame->GetContent()->OwnerDoc(),
|
||||
frame->GetContent(),
|
||||
NS_LITERAL_STRING("MozMouseScrollTransactionTimeout"),
|
||||
true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::SetTimeout()
|
||||
{
|
||||
if (!sTimer) {
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
if (!timer)
|
||||
return;
|
||||
timer.swap(sTimer);
|
||||
}
|
||||
sTimer->Cancel();
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
#endif
|
||||
sTimer->InitWithFuncCallback(OnTimeout, nullptr, GetTimeoutTime(),
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "nsITimer::InitWithFuncCallback failed");
|
||||
}
|
||||
|
||||
nsIntPoint
|
||||
nsMouseWheelTransaction::GetScreenPoint(WidgetGUIEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(aEvent, "aEvent is null");
|
||||
NS_ASSERTION(aEvent->widget, "aEvent-widget is null");
|
||||
return LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint) +
|
||||
aEvent->widget->WidgetToScreenOffset();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsMouseWheelTransaction::GetTimeoutTime()
|
||||
{
|
||||
return Preferences::GetUint("mousewheel.transaction.timeout", 1500);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsMouseWheelTransaction::GetIgnoreMoveDelayTime()
|
||||
{
|
||||
return Preferences::GetUint("mousewheel.transaction.ignoremovedelay", 100);
|
||||
}
|
||||
|
||||
DeltaValues
|
||||
nsMouseWheelTransaction::AccelerateWheelDelta(WidgetWheelEvent* aEvent,
|
||||
bool aAllowScrollSpeedOverride)
|
||||
{
|
||||
DeltaValues result(aEvent);
|
||||
|
||||
// Don't accelerate the delta values if the event isn't line scrolling.
|
||||
if (aEvent->deltaMode != nsIDOMWheelEvent::DOM_DELTA_LINE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (aAllowScrollSpeedOverride) {
|
||||
result = OverrideSystemScrollSpeed(aEvent);
|
||||
}
|
||||
|
||||
// Accelerate by the sScrollSeriesCounter
|
||||
int32_t start = GetAccelerationStart();
|
||||
if (start >= 0 && sScrollSeriesCounter >= start) {
|
||||
int32_t factor = GetAccelerationFactor();
|
||||
if (factor > 0) {
|
||||
result.deltaX = ComputeAcceleratedWheelDelta(result.deltaX, factor);
|
||||
result.deltaY = ComputeAcceleratedWheelDelta(result.deltaY, factor);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
double
|
||||
nsMouseWheelTransaction::ComputeAcceleratedWheelDelta(double aDelta,
|
||||
int32_t aFactor)
|
||||
{
|
||||
if (aDelta == 0.0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (aDelta * sScrollSeriesCounter * (double)aFactor / 10);
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsMouseWheelTransaction::GetAccelerationStart()
|
||||
{
|
||||
return Preferences::GetInt("mousewheel.acceleration.start", -1);
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsMouseWheelTransaction::GetAccelerationFactor()
|
||||
{
|
||||
return Preferences::GetInt("mousewheel.acceleration.factor", -1);
|
||||
}
|
||||
|
||||
DeltaValues
|
||||
nsMouseWheelTransaction::OverrideSystemScrollSpeed(WidgetWheelEvent* aEvent)
|
||||
{
|
||||
MOZ_ASSERT(sTargetFrame, "We don't have mouse scrolling transaction");
|
||||
MOZ_ASSERT(aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE);
|
||||
|
||||
// If the event doesn't scroll to both X and Y, we don't need to do anything
|
||||
// here.
|
||||
if (!aEvent->deltaX && !aEvent->deltaY) {
|
||||
return DeltaValues(aEvent);
|
||||
}
|
||||
|
||||
// We shouldn't override the scrolling speed on non root scroll frame.
|
||||
if (sTargetFrame !=
|
||||
sTargetFrame->PresContext()->PresShell()->GetRootScrollFrame()) {
|
||||
return DeltaValues(aEvent);
|
||||
}
|
||||
|
||||
// Compute the overridden speed to nsIWidget. The widget can check the
|
||||
// conditions (e.g., checking the prefs, and also whether the user customized
|
||||
// the system settings of the mouse wheel scrolling or not), and can limit
|
||||
// the speed for preventing the unexpected high speed scrolling.
|
||||
nsCOMPtr<nsIWidget> widget(sTargetFrame->GetNearestWidget());
|
||||
NS_ENSURE_TRUE(widget, DeltaValues(aEvent));
|
||||
DeltaValues overriddenDeltaValues(0.0, 0.0);
|
||||
nsresult rv =
|
||||
widget->OverrideSystemMouseScrollSpeed(aEvent->deltaX, aEvent->deltaY,
|
||||
overriddenDeltaValues.deltaX,
|
||||
overriddenDeltaValues.deltaY);
|
||||
return NS_FAILED(rv) ? DeltaValues(aEvent) : overriddenDeltaValues;
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* nsScrollbarsForWheel */
|
||||
/******************************************************************/
|
||||
|
||||
const DeltaValues nsScrollbarsForWheel::directions[kNumberOfTargets] = {
|
||||
DeltaValues(-1, 0), DeltaValues(+1, 0), DeltaValues(0, -1), DeltaValues(0, +1)
|
||||
};
|
||||
|
||||
nsWeakFrame nsScrollbarsForWheel::sActiveOwner = nullptr;
|
||||
nsWeakFrame nsScrollbarsForWheel::sActivatedScrollTargets[kNumberOfTargets] = {
|
||||
nullptr, nullptr, nullptr, nullptr
|
||||
};
|
||||
|
||||
bool nsScrollbarsForWheel::sHadWheelStart = false;
|
||||
bool nsScrollbarsForWheel::sOwnWheelTransaction = false;
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::PrepareToScrollText(nsEventStateManager* aESM,
|
||||
nsIFrame* aTargetFrame,
|
||||
WidgetWheelEvent* aEvent)
|
||||
{
|
||||
if (aEvent->message == NS_WHEEL_START) {
|
||||
nsMouseWheelTransaction::OwnScrollbars(false);
|
||||
if (!IsActive()) {
|
||||
TemporarilyActivateAllPossibleScrollTargets(aESM, aTargetFrame, aEvent);
|
||||
sHadWheelStart = true;
|
||||
}
|
||||
} else {
|
||||
DeactivateAllTemporarilyActivatedScrollTargets();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::SetActiveScrollTarget(nsIScrollableFrame* aScrollTarget)
|
||||
{
|
||||
if (!sHadWheelStart) {
|
||||
return;
|
||||
}
|
||||
nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(aScrollTarget);
|
||||
if (!scrollbarOwner) {
|
||||
return;
|
||||
}
|
||||
sHadWheelStart = false;
|
||||
sActiveOwner = do_QueryFrame(aScrollTarget);
|
||||
scrollbarOwner->ScrollbarActivityStarted();
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::MayInactivate()
|
||||
{
|
||||
if (!sOwnWheelTransaction && nsMouseWheelTransaction::GetTargetFrame()) {
|
||||
nsMouseWheelTransaction::OwnScrollbars(true);
|
||||
} else {
|
||||
Inactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::Inactivate()
|
||||
{
|
||||
nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(sActiveOwner);
|
||||
if (scrollbarOwner) {
|
||||
scrollbarOwner->ScrollbarActivityStopped();
|
||||
}
|
||||
sActiveOwner = nullptr;
|
||||
DeactivateAllTemporarilyActivatedScrollTargets();
|
||||
if (sOwnWheelTransaction) {
|
||||
sOwnWheelTransaction = false;
|
||||
nsMouseWheelTransaction::OwnScrollbars(false);
|
||||
nsMouseWheelTransaction::EndTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsScrollbarsForWheel::IsActive()
|
||||
{
|
||||
if (sActiveOwner) {
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < kNumberOfTargets; ++i) {
|
||||
if (sActivatedScrollTargets[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::OwnWheelTransaction(bool aOwn)
|
||||
{
|
||||
sOwnWheelTransaction = aOwn;
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::TemporarilyActivateAllPossibleScrollTargets(
|
||||
nsEventStateManager* aESM,
|
||||
nsIFrame* aTargetFrame,
|
||||
WidgetWheelEvent* aEvent)
|
||||
{
|
||||
for (size_t i = 0; i < kNumberOfTargets; i++) {
|
||||
const DeltaValues *dir = &directions[i];
|
||||
nsWeakFrame* scrollTarget = &sActivatedScrollTargets[i];
|
||||
MOZ_ASSERT(!*scrollTarget, "scroll target still temporarily activated!");
|
||||
nsIScrollableFrame* target =
|
||||
aESM->ComputeScrollTarget(aTargetFrame, dir->deltaX, dir->deltaY, aEvent,
|
||||
nsEventStateManager::COMPUTE_DEFAULT_ACTION_TARGET);
|
||||
nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(target);
|
||||
if (scrollbarOwner) {
|
||||
nsIFrame* targetFrame = do_QueryFrame(target);
|
||||
*scrollTarget = targetFrame;
|
||||
scrollbarOwner->ScrollbarActivityStarted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::DeactivateAllTemporarilyActivatedScrollTargets()
|
||||
{
|
||||
for (size_t i = 0; i < kNumberOfTargets; i++) {
|
||||
nsWeakFrame* scrollTarget = &sActivatedScrollTargets[i];
|
||||
if (*scrollTarget) {
|
||||
nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(*scrollTarget);
|
||||
if (scrollbarOwner) {
|
||||
scrollbarOwner->ScrollbarActivityStopped();
|
||||
}
|
||||
*scrollTarget = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 sw=2 et tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_WheelHandlingHelper_h_
|
||||
#define mozilla_WheelHandlingHelper_h_
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "nsCoord.h"
|
||||
#include "nsIFrame.h"
|
||||
|
||||
class nsEventStateManager;
|
||||
class nsIScrollableFrame;
|
||||
class nsITimer;
|
||||
|
||||
struct nsIntPoint;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct DeltaValues
|
||||
{
|
||||
DeltaValues()
|
||||
: deltaX(0.0)
|
||||
, deltaY(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
DeltaValues(double aDeltaX, double aDeltaY)
|
||||
: deltaX(aDeltaX)
|
||||
, deltaY(aDeltaY)
|
||||
{
|
||||
}
|
||||
|
||||
explicit DeltaValues(WidgetWheelEvent* aEvent);
|
||||
|
||||
double deltaX;
|
||||
double deltaY;
|
||||
};
|
||||
|
||||
class WheelHandlingUtils
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returns true if the scrollable frame can be scrolled to either aDirectionX
|
||||
* or aDirectionY along each axis. Otherwise, false.
|
||||
*/
|
||||
static bool CanScrollOn(nsIScrollableFrame* aScrollFrame,
|
||||
double aDirectionX, double aDirectionY);
|
||||
|
||||
private:
|
||||
static bool CanScrollInRange(nscoord aMin, nscoord aValue, nscoord aMax,
|
||||
double aDirection);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
class nsScrollbarsForWheel
|
||||
{
|
||||
public:
|
||||
static void PrepareToScrollText(nsEventStateManager* aESM,
|
||||
nsIFrame* aTargetFrame,
|
||||
mozilla::WidgetWheelEvent* aEvent);
|
||||
static void SetActiveScrollTarget(nsIScrollableFrame* aScrollTarget);
|
||||
// Hide all scrollbars (both mActiveOwner's and mActivatedScrollTargets')
|
||||
static void MayInactivate();
|
||||
static void Inactivate();
|
||||
static bool IsActive();
|
||||
static void OwnWheelTransaction(bool aOwn);
|
||||
|
||||
protected:
|
||||
static const size_t kNumberOfTargets = 4;
|
||||
static const mozilla::DeltaValues directions[kNumberOfTargets];
|
||||
static nsWeakFrame sActiveOwner;
|
||||
static nsWeakFrame sActivatedScrollTargets[kNumberOfTargets];
|
||||
static bool sHadWheelStart;
|
||||
static bool sOwnWheelTransaction;
|
||||
|
||||
|
||||
/**
|
||||
* These two methods are called upon NS_WHEEL_START/NS_WHEEL_STOP events
|
||||
* to show/hide the right scrollbars.
|
||||
*/
|
||||
static void TemporarilyActivateAllPossibleScrollTargets(
|
||||
nsEventStateManager* aESM,
|
||||
nsIFrame* aTargetFrame,
|
||||
mozilla::WidgetWheelEvent* aEvent);
|
||||
static void DeactivateAllTemporarilyActivatedScrollTargets();
|
||||
};
|
||||
|
||||
class nsMouseWheelTransaction
|
||||
{
|
||||
public:
|
||||
static nsIFrame* GetTargetFrame() { return sTargetFrame; }
|
||||
static void BeginTransaction(nsIFrame* aTargetFrame,
|
||||
mozilla::WidgetWheelEvent* aEvent);
|
||||
// Be careful, UpdateTransaction may fire a DOM event, therefore, the target
|
||||
// frame might be destroyed in the event handler.
|
||||
static bool UpdateTransaction(mozilla::WidgetWheelEvent* aEvent);
|
||||
static void MayEndTransaction();
|
||||
static void EndTransaction();
|
||||
static void OnEvent(mozilla::WidgetEvent* aEvent);
|
||||
static void Shutdown();
|
||||
static uint32_t GetTimeoutTime();
|
||||
|
||||
static void OwnScrollbars(bool aOwn);
|
||||
|
||||
static mozilla::DeltaValues
|
||||
AccelerateWheelDelta(mozilla::WidgetWheelEvent* aEvent,
|
||||
bool aAllowScrollSpeedOverride);
|
||||
|
||||
protected:
|
||||
static const uint32_t kScrollSeriesTimeout = 80; // in milliseconds
|
||||
static nsIntPoint GetScreenPoint(mozilla::WidgetGUIEvent* aEvent);
|
||||
static void OnFailToScrollTarget();
|
||||
static void OnTimeout(nsITimer* aTimer, void* aClosure);
|
||||
static void SetTimeout();
|
||||
static uint32_t GetIgnoreMoveDelayTime();
|
||||
static int32_t GetAccelerationStart();
|
||||
static int32_t GetAccelerationFactor();
|
||||
static mozilla::DeltaValues
|
||||
OverrideSystemScrollSpeed(mozilla::WidgetWheelEvent* aEvent);
|
||||
static double ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor);
|
||||
static bool OutOfTime(uint32_t aBaseTime, uint32_t aThreshold);
|
||||
|
||||
static nsWeakFrame sTargetFrame;
|
||||
static uint32_t sTime; // in milliseconds
|
||||
static uint32_t sMouseMoved; // in milliseconds
|
||||
static nsITimer* sTimer;
|
||||
static int32_t sScrollSeriesCounter;
|
||||
static bool sOwnScrollbars;
|
||||
};
|
||||
|
||||
#endif // mozilla_WheelHandlingHelper_h_
|
|
@ -104,6 +104,7 @@ UNIFIED_SOURCES += [
|
|||
'TransitionEvent.cpp',
|
||||
'UIEvent.cpp',
|
||||
'WheelEvent.cpp',
|
||||
'WheelHandlingHelper.cpp',
|
||||
'XULCommandEvent.cpp',
|
||||
]
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "mozilla/dom/UIEvent.h"
|
||||
|
||||
#include "ContentEventHandler.h"
|
||||
#include "WheelHandlingHelper.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsEventStateManager.h"
|
||||
|
@ -134,14 +135,6 @@ RoundDown(double aDouble)
|
|||
static_cast<int32_t>(ceil(aDouble));
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsMouseEventReal(WidgetEvent* aEvent)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aEvent->AsMouseEvent(), "Not a mouse event");
|
||||
// Return true if not synthesized.
|
||||
return aEvent->AsMouseEvent()->reason == WidgetMouseEvent::eReal;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DOCSHELL_FOCUS
|
||||
static void
|
||||
PrintDocTree(nsIDocShellTreeItem* aParentItem, int aLevel)
|
||||
|
@ -252,579 +245,6 @@ GetDocumentFromWindow(nsIDOMWindow *aWindow)
|
|||
return win ? win->GetExtantDoc() : nullptr;
|
||||
}
|
||||
|
||||
struct DeltaValues
|
||||
{
|
||||
DeltaValues() : deltaX(0.0), deltaY(0.0) {}
|
||||
|
||||
DeltaValues(double aDeltaX, double aDeltaY) :
|
||||
deltaX(aDeltaX), deltaY(aDeltaY)
|
||||
{
|
||||
}
|
||||
|
||||
explicit DeltaValues(WidgetWheelEvent* aEvent) :
|
||||
deltaX(aEvent->deltaX), deltaY(aEvent->deltaY)
|
||||
{
|
||||
}
|
||||
|
||||
double deltaX;
|
||||
double deltaY;
|
||||
};
|
||||
|
||||
/******************************************************************/
|
||||
/* nsScrollbarsForWheel */
|
||||
/******************************************************************/
|
||||
|
||||
class nsScrollbarsForWheel {
|
||||
public:
|
||||
static void PrepareToScrollText(nsEventStateManager* aESM,
|
||||
nsIFrame* aTargetFrame,
|
||||
WidgetWheelEvent* aEvent);
|
||||
static void SetActiveScrollTarget(nsIScrollableFrame* aScrollTarget);
|
||||
// Hide all scrollbars (both mActiveOwner's and mActivatedScrollTargets')
|
||||
static void MayInactivate();
|
||||
static void Inactivate();
|
||||
static bool IsActive();
|
||||
static void OwnWheelTransaction(bool aOwn);
|
||||
|
||||
protected:
|
||||
static const size_t kNumberOfTargets = 4;
|
||||
static const DeltaValues directions[kNumberOfTargets];
|
||||
static nsWeakFrame sActiveOwner;
|
||||
static nsWeakFrame sActivatedScrollTargets[kNumberOfTargets];
|
||||
static bool sHadWheelStart;
|
||||
static bool sOwnWheelTransaction;
|
||||
|
||||
|
||||
/**
|
||||
* These two methods are called upon NS_WHEEL_START/NS_WHEEL_STOP events
|
||||
* to show/hide the right scrollbars.
|
||||
*/
|
||||
static void TemporarilyActivateAllPossibleScrollTargets(
|
||||
nsEventStateManager* aESM,
|
||||
nsIFrame* aTargetFrame,
|
||||
WidgetWheelEvent* aEvent);
|
||||
static void DeactivateAllTemporarilyActivatedScrollTargets();
|
||||
};
|
||||
|
||||
const DeltaValues nsScrollbarsForWheel::directions[kNumberOfTargets] = {
|
||||
DeltaValues(-1, 0), DeltaValues(+1, 0), DeltaValues(0, -1), DeltaValues(0, +1)
|
||||
};
|
||||
nsWeakFrame nsScrollbarsForWheel::sActiveOwner = nullptr;
|
||||
nsWeakFrame nsScrollbarsForWheel::sActivatedScrollTargets[kNumberOfTargets] = {
|
||||
nullptr, nullptr, nullptr, nullptr
|
||||
};
|
||||
bool nsScrollbarsForWheel::sHadWheelStart = false;
|
||||
bool nsScrollbarsForWheel::sOwnWheelTransaction = false;
|
||||
|
||||
/******************************************************************/
|
||||
/* nsMouseWheelTransaction */
|
||||
/******************************************************************/
|
||||
|
||||
class nsMouseWheelTransaction {
|
||||
public:
|
||||
static nsIFrame* GetTargetFrame() { return sTargetFrame; }
|
||||
static void BeginTransaction(nsIFrame* aTargetFrame,
|
||||
WidgetWheelEvent* aEvent);
|
||||
// Be careful, UpdateTransaction may fire a DOM event, therefore, the target
|
||||
// frame might be destroyed in the event handler.
|
||||
static bool UpdateTransaction(WidgetWheelEvent* aEvent);
|
||||
static void MayEndTransaction();
|
||||
static void EndTransaction();
|
||||
static void OnEvent(WidgetEvent* aEvent);
|
||||
static void Shutdown();
|
||||
static uint32_t GetTimeoutTime();
|
||||
|
||||
static void OwnScrollbars(bool aOwn);
|
||||
|
||||
static DeltaValues AccelerateWheelDelta(WidgetWheelEvent* aEvent,
|
||||
bool aAllowScrollSpeedOverride);
|
||||
|
||||
enum {
|
||||
kScrollSeriesTimeout = 80
|
||||
};
|
||||
protected:
|
||||
static nsIntPoint GetScreenPoint(WidgetGUIEvent* aEvent);
|
||||
static void OnFailToScrollTarget();
|
||||
static void OnTimeout(nsITimer *aTimer, void *aClosure);
|
||||
static void SetTimeout();
|
||||
static uint32_t GetIgnoreMoveDelayTime();
|
||||
static int32_t GetAccelerationStart();
|
||||
static int32_t GetAccelerationFactor();
|
||||
static DeltaValues OverrideSystemScrollSpeed(WidgetWheelEvent* aEvent);
|
||||
static double ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor);
|
||||
static bool OutOfTime(uint32_t aBaseTime, uint32_t aThreshold);
|
||||
|
||||
static nsWeakFrame sTargetFrame;
|
||||
static uint32_t sTime; // in milliseconds
|
||||
static uint32_t sMouseMoved; // in milliseconds
|
||||
static nsITimer* sTimer;
|
||||
static int32_t sScrollSeriesCounter;
|
||||
static bool sOwnScrollbars;
|
||||
};
|
||||
|
||||
nsWeakFrame nsMouseWheelTransaction::sTargetFrame(nullptr);
|
||||
uint32_t nsMouseWheelTransaction::sTime = 0;
|
||||
uint32_t nsMouseWheelTransaction::sMouseMoved = 0;
|
||||
nsITimer* nsMouseWheelTransaction::sTimer = nullptr;
|
||||
int32_t nsMouseWheelTransaction::sScrollSeriesCounter = 0;
|
||||
bool nsMouseWheelTransaction::sOwnScrollbars = false;
|
||||
|
||||
static bool
|
||||
CanScrollInRange(nscoord aMin, nscoord aValue, nscoord aMax, double aDirection)
|
||||
{
|
||||
return aDirection > 0.0 ? aValue < static_cast<double>(aMax) :
|
||||
static_cast<double>(aMin) < aValue;
|
||||
}
|
||||
|
||||
static bool
|
||||
CanScrollOn(nsIScrollableFrame* aScrollFrame, double aDirectionX, double aDirectionY)
|
||||
{
|
||||
MOZ_ASSERT(aScrollFrame);
|
||||
NS_ASSERTION(aDirectionX || aDirectionY,
|
||||
"One of the delta values must be non-zero at least");
|
||||
|
||||
nsPoint scrollPt = aScrollFrame->GetScrollPosition();
|
||||
nsRect scrollRange = aScrollFrame->GetScrollRange();
|
||||
uint32_t directions = aScrollFrame->GetPerceivedScrollingDirections();
|
||||
|
||||
return (aDirectionX && (directions & nsIScrollableFrame::HORIZONTAL) &&
|
||||
CanScrollInRange(scrollRange.x, scrollPt.x, scrollRange.XMost(), aDirectionX)) ||
|
||||
(aDirectionY && (directions & nsIScrollableFrame::VERTICAL) &&
|
||||
CanScrollInRange(scrollRange.y, scrollPt.y, scrollRange.YMost(), aDirectionY));
|
||||
}
|
||||
|
||||
bool
|
||||
nsMouseWheelTransaction::OutOfTime(uint32_t aBaseTime, uint32_t aThreshold)
|
||||
{
|
||||
uint32_t now = PR_IntervalToMilliseconds(PR_IntervalNow());
|
||||
return (now - aBaseTime > aThreshold);
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::OwnScrollbars(bool aOwn)
|
||||
{
|
||||
sOwnScrollbars = aOwn;
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::BeginTransaction(nsIFrame* aTargetFrame,
|
||||
WidgetWheelEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(!sTargetFrame, "previous transaction is not finished!");
|
||||
MOZ_ASSERT(aEvent->message == NS_WHEEL_WHEEL,
|
||||
"Transaction must be started with a wheel event");
|
||||
nsScrollbarsForWheel::OwnWheelTransaction(false);
|
||||
sTargetFrame = aTargetFrame;
|
||||
sScrollSeriesCounter = 0;
|
||||
if (!UpdateTransaction(aEvent)) {
|
||||
NS_ERROR("BeginTransaction is called even cannot scroll the frame");
|
||||
EndTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsMouseWheelTransaction::UpdateTransaction(WidgetWheelEvent* aEvent)
|
||||
{
|
||||
nsIScrollableFrame* sf = GetTargetFrame()->GetScrollTargetFrame();
|
||||
NS_ENSURE_TRUE(sf, false);
|
||||
|
||||
if (!CanScrollOn(sf, aEvent->deltaX, aEvent->deltaY)) {
|
||||
OnFailToScrollTarget();
|
||||
// We should not modify the transaction state when the view will not be
|
||||
// scrolled actually.
|
||||
return false;
|
||||
}
|
||||
|
||||
SetTimeout();
|
||||
|
||||
if (sScrollSeriesCounter != 0 && OutOfTime(sTime, kScrollSeriesTimeout))
|
||||
sScrollSeriesCounter = 0;
|
||||
sScrollSeriesCounter++;
|
||||
|
||||
// We should use current time instead of WidgetEvent.time.
|
||||
// 1. Some events doesn't have the correct creation time.
|
||||
// 2. If the computer runs slowly by other processes eating the CPU resource,
|
||||
// the event creation time doesn't keep real time.
|
||||
sTime = PR_IntervalToMilliseconds(PR_IntervalNow());
|
||||
sMouseMoved = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::MayEndTransaction()
|
||||
{
|
||||
if (!sOwnScrollbars && nsScrollbarsForWheel::IsActive()) {
|
||||
nsScrollbarsForWheel::OwnWheelTransaction(true);
|
||||
} else {
|
||||
EndTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::EndTransaction()
|
||||
{
|
||||
if (sTimer)
|
||||
sTimer->Cancel();
|
||||
sTargetFrame = nullptr;
|
||||
sScrollSeriesCounter = 0;
|
||||
if (sOwnScrollbars) {
|
||||
sOwnScrollbars = false;
|
||||
nsScrollbarsForWheel::OwnWheelTransaction(false);
|
||||
nsScrollbarsForWheel::Inactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::OnEvent(WidgetEvent* aEvent)
|
||||
{
|
||||
if (!sTargetFrame)
|
||||
return;
|
||||
|
||||
if (OutOfTime(sTime, GetTimeoutTime())) {
|
||||
// Even if the scroll event which is handled after timeout, but onTimeout
|
||||
// was not fired by timer, then the scroll event will scroll old frame,
|
||||
// therefore, we should call OnTimeout here and ensure to finish the old
|
||||
// transaction.
|
||||
OnTimeout(nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aEvent->message) {
|
||||
case NS_WHEEL_WHEEL:
|
||||
if (sMouseMoved != 0 &&
|
||||
OutOfTime(sMouseMoved, GetIgnoreMoveDelayTime())) {
|
||||
// Terminate the current mousewheel transaction if the mouse moved more
|
||||
// than ignoremovedelay milliseconds ago
|
||||
EndTransaction();
|
||||
}
|
||||
return;
|
||||
case NS_MOUSE_MOVE:
|
||||
case NS_DRAGDROP_OVER:
|
||||
if (IsMouseEventReal(aEvent)) {
|
||||
// If the cursor is moving to be outside the frame,
|
||||
// terminate the scrollwheel transaction.
|
||||
nsIntPoint pt = GetScreenPoint(aEvent->AsGUIEvent());
|
||||
nsIntRect r = sTargetFrame->GetScreenRectExternal();
|
||||
if (!r.Contains(pt)) {
|
||||
EndTransaction();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the cursor is moving inside the frame, and it is less than
|
||||
// ignoremovedelay milliseconds since the last scroll operation, ignore
|
||||
// the mouse move; otherwise, record the current mouse move time to be
|
||||
// checked later
|
||||
if (OutOfTime(sTime, GetIgnoreMoveDelayTime())) {
|
||||
if (sMouseMoved == 0)
|
||||
sMouseMoved = PR_IntervalToMilliseconds(PR_IntervalNow());
|
||||
}
|
||||
}
|
||||
return;
|
||||
case NS_KEY_PRESS:
|
||||
case NS_KEY_UP:
|
||||
case NS_KEY_DOWN:
|
||||
case NS_MOUSE_BUTTON_UP:
|
||||
case NS_MOUSE_BUTTON_DOWN:
|
||||
case NS_MOUSE_DOUBLECLICK:
|
||||
case NS_MOUSE_CLICK:
|
||||
case NS_CONTEXTMENU:
|
||||
case NS_DRAGDROP_DROP:
|
||||
EndTransaction();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::Shutdown()
|
||||
{
|
||||
NS_IF_RELEASE(sTimer);
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::OnFailToScrollTarget()
|
||||
{
|
||||
NS_PRECONDITION(sTargetFrame, "We don't have mouse scrolling transaction");
|
||||
|
||||
if (Preferences::GetBool("test.mousescroll", false)) {
|
||||
// This event is used for automated tests, see bug 442774.
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
sTargetFrame->GetContent()->OwnerDoc(),
|
||||
sTargetFrame->GetContent(),
|
||||
NS_LITERAL_STRING("MozMouseScrollFailed"),
|
||||
true, true);
|
||||
}
|
||||
// The target frame might be destroyed in the event handler, at that time,
|
||||
// we need to finish the current transaction
|
||||
if (!sTargetFrame) {
|
||||
EndTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::OnTimeout(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
if (!sTargetFrame) {
|
||||
// The transaction target was destroyed already
|
||||
EndTransaction();
|
||||
return;
|
||||
}
|
||||
// Store the sTargetFrame, the variable becomes null in EndTransaction.
|
||||
nsIFrame* frame = sTargetFrame;
|
||||
// We need to finish current transaction before DOM event firing. Because
|
||||
// the next DOM event might create strange situation for us.
|
||||
MayEndTransaction();
|
||||
|
||||
if (Preferences::GetBool("test.mousescroll", false)) {
|
||||
// This event is used for automated tests, see bug 442774.
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
frame->GetContent()->OwnerDoc(),
|
||||
frame->GetContent(),
|
||||
NS_LITERAL_STRING("MozMouseScrollTransactionTimeout"),
|
||||
true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMouseWheelTransaction::SetTimeout()
|
||||
{
|
||||
if (!sTimer) {
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
if (!timer)
|
||||
return;
|
||||
timer.swap(sTimer);
|
||||
}
|
||||
sTimer->Cancel();
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
#endif
|
||||
sTimer->InitWithFuncCallback(OnTimeout, nullptr, GetTimeoutTime(),
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "nsITimer::InitWithFuncCallback failed");
|
||||
}
|
||||
|
||||
nsIntPoint
|
||||
nsMouseWheelTransaction::GetScreenPoint(WidgetGUIEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(aEvent, "aEvent is null");
|
||||
NS_ASSERTION(aEvent->widget, "aEvent-widget is null");
|
||||
return LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint) +
|
||||
aEvent->widget->WidgetToScreenOffset();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsMouseWheelTransaction::GetTimeoutTime()
|
||||
{
|
||||
return Preferences::GetUint("mousewheel.transaction.timeout", 1500);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
nsMouseWheelTransaction::GetIgnoreMoveDelayTime()
|
||||
{
|
||||
return Preferences::GetUint("mousewheel.transaction.ignoremovedelay", 100);
|
||||
}
|
||||
|
||||
DeltaValues
|
||||
nsMouseWheelTransaction::AccelerateWheelDelta(WidgetWheelEvent* aEvent,
|
||||
bool aAllowScrollSpeedOverride)
|
||||
{
|
||||
DeltaValues result(aEvent);
|
||||
|
||||
// Don't accelerate the delta values if the event isn't line scrolling.
|
||||
if (aEvent->deltaMode != nsIDOMWheelEvent::DOM_DELTA_LINE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (aAllowScrollSpeedOverride) {
|
||||
result = OverrideSystemScrollSpeed(aEvent);
|
||||
}
|
||||
|
||||
// Accelerate by the sScrollSeriesCounter
|
||||
int32_t start = GetAccelerationStart();
|
||||
if (start >= 0 && sScrollSeriesCounter >= start) {
|
||||
int32_t factor = GetAccelerationFactor();
|
||||
if (factor > 0) {
|
||||
result.deltaX = ComputeAcceleratedWheelDelta(result.deltaX, factor);
|
||||
result.deltaY = ComputeAcceleratedWheelDelta(result.deltaY, factor);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
double
|
||||
nsMouseWheelTransaction::ComputeAcceleratedWheelDelta(double aDelta,
|
||||
int32_t aFactor)
|
||||
{
|
||||
if (aDelta == 0.0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (aDelta * sScrollSeriesCounter * (double)aFactor / 10);
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsMouseWheelTransaction::GetAccelerationStart()
|
||||
{
|
||||
return Preferences::GetInt("mousewheel.acceleration.start", -1);
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsMouseWheelTransaction::GetAccelerationFactor()
|
||||
{
|
||||
return Preferences::GetInt("mousewheel.acceleration.factor", -1);
|
||||
}
|
||||
|
||||
DeltaValues
|
||||
nsMouseWheelTransaction::OverrideSystemScrollSpeed(WidgetWheelEvent* aEvent)
|
||||
{
|
||||
MOZ_ASSERT(sTargetFrame, "We don't have mouse scrolling transaction");
|
||||
MOZ_ASSERT(aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE);
|
||||
|
||||
// If the event doesn't scroll to both X and Y, we don't need to do anything
|
||||
// here.
|
||||
if (!aEvent->deltaX && !aEvent->deltaY) {
|
||||
return DeltaValues(aEvent);
|
||||
}
|
||||
|
||||
// We shouldn't override the scrolling speed on non root scroll frame.
|
||||
if (sTargetFrame !=
|
||||
sTargetFrame->PresContext()->PresShell()->GetRootScrollFrame()) {
|
||||
return DeltaValues(aEvent);
|
||||
}
|
||||
|
||||
// Compute the overridden speed to nsIWidget. The widget can check the
|
||||
// conditions (e.g., checking the prefs, and also whether the user customized
|
||||
// the system settings of the mouse wheel scrolling or not), and can limit
|
||||
// the speed for preventing the unexpected high speed scrolling.
|
||||
nsCOMPtr<nsIWidget> widget(sTargetFrame->GetNearestWidget());
|
||||
NS_ENSURE_TRUE(widget, DeltaValues(aEvent));
|
||||
DeltaValues overriddenDeltaValues(0.0, 0.0);
|
||||
nsresult rv =
|
||||
widget->OverrideSystemMouseScrollSpeed(aEvent->deltaX, aEvent->deltaY,
|
||||
overriddenDeltaValues.deltaX,
|
||||
overriddenDeltaValues.deltaY);
|
||||
return NS_FAILED(rv) ? DeltaValues(aEvent) : overriddenDeltaValues;
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* nsScrollbarsForWheel */
|
||||
/******************************************************************/
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::PrepareToScrollText(
|
||||
nsEventStateManager* aESM,
|
||||
nsIFrame* aTargetFrame,
|
||||
WidgetWheelEvent* aEvent)
|
||||
{
|
||||
if (aEvent->message == NS_WHEEL_START) {
|
||||
nsMouseWheelTransaction::OwnScrollbars(false);
|
||||
if (!IsActive()) {
|
||||
TemporarilyActivateAllPossibleScrollTargets(aESM, aTargetFrame, aEvent);
|
||||
sHadWheelStart = true;
|
||||
}
|
||||
} else {
|
||||
DeactivateAllTemporarilyActivatedScrollTargets();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::SetActiveScrollTarget(nsIScrollableFrame* aScrollTarget)
|
||||
{
|
||||
if (!sHadWheelStart) {
|
||||
return;
|
||||
}
|
||||
nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(aScrollTarget);
|
||||
if (!scrollbarOwner) {
|
||||
return;
|
||||
}
|
||||
sHadWheelStart = false;
|
||||
sActiveOwner = do_QueryFrame(aScrollTarget);
|
||||
scrollbarOwner->ScrollbarActivityStarted();
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::MayInactivate()
|
||||
{
|
||||
if (!sOwnWheelTransaction && nsMouseWheelTransaction::GetTargetFrame()) {
|
||||
nsMouseWheelTransaction::OwnScrollbars(true);
|
||||
} else {
|
||||
Inactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::Inactivate()
|
||||
{
|
||||
nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(sActiveOwner);
|
||||
if (scrollbarOwner) {
|
||||
scrollbarOwner->ScrollbarActivityStopped();
|
||||
}
|
||||
sActiveOwner = nullptr;
|
||||
DeactivateAllTemporarilyActivatedScrollTargets();
|
||||
if (sOwnWheelTransaction) {
|
||||
sOwnWheelTransaction = false;
|
||||
nsMouseWheelTransaction::OwnScrollbars(false);
|
||||
nsMouseWheelTransaction::EndTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsScrollbarsForWheel::IsActive()
|
||||
{
|
||||
if (sActiveOwner) {
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < kNumberOfTargets; ++i) {
|
||||
if (sActivatedScrollTargets[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::OwnWheelTransaction(bool aOwn)
|
||||
{
|
||||
sOwnWheelTransaction = aOwn;
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::TemporarilyActivateAllPossibleScrollTargets(
|
||||
nsEventStateManager* aESM,
|
||||
nsIFrame* aTargetFrame,
|
||||
WidgetWheelEvent* aEvent)
|
||||
{
|
||||
for (size_t i = 0; i < kNumberOfTargets; i++) {
|
||||
const DeltaValues *dir = &directions[i];
|
||||
nsWeakFrame* scrollTarget = &sActivatedScrollTargets[i];
|
||||
MOZ_ASSERT(!*scrollTarget, "scroll target still temporarily activated!");
|
||||
nsIScrollableFrame* target =
|
||||
aESM->ComputeScrollTarget(aTargetFrame, dir->deltaX, dir->deltaY, aEvent,
|
||||
nsEventStateManager::COMPUTE_DEFAULT_ACTION_TARGET);
|
||||
nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(target);
|
||||
if (scrollbarOwner) {
|
||||
nsIFrame* targetFrame = do_QueryFrame(target);
|
||||
*scrollTarget = targetFrame;
|
||||
scrollbarOwner->ScrollbarActivityStarted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsScrollbarsForWheel::DeactivateAllTemporarilyActivatedScrollTargets()
|
||||
{
|
||||
for (size_t i = 0; i < kNumberOfTargets; i++) {
|
||||
nsWeakFrame* scrollTarget = &sActivatedScrollTargets[i];
|
||||
if (*scrollTarget) {
|
||||
nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(*scrollTarget);
|
||||
if (scrollbarOwner) {
|
||||
scrollbarOwner->ScrollbarActivityStopped();
|
||||
}
|
||||
*scrollTarget = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* OverOutElementsWrapper */
|
||||
/******************************************************************/
|
||||
|
@ -1025,7 +445,7 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
|||
// can use these values as constants.
|
||||
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
|
||||
if (aEvent->mFlags.mIsTrusted &&
|
||||
((mouseEvent && IsMouseEventReal(mouseEvent)) ||
|
||||
((mouseEvent && mouseEvent->IsReal()) ||
|
||||
aEvent->eventStructType == NS_WHEEL_EVENT) &&
|
||||
!sIsPointerLocked) {
|
||||
sLastScreenPoint =
|
||||
|
@ -1037,7 +457,7 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
|||
// Do not take account NS_MOUSE_ENTER/EXIT so that loading a page
|
||||
// when user is not active doesn't change the state to active.
|
||||
if (aEvent->mFlags.mIsTrusted &&
|
||||
((mouseEvent && IsMouseEventReal(mouseEvent) &&
|
||||
((mouseEvent && mouseEvent->IsReal() &&
|
||||
mouseEvent->message != NS_MOUSE_ENTER &&
|
||||
mouseEvent->message != NS_MOUSE_EXIT) ||
|
||||
aEvent->eventStructType == NS_WHEEL_EVENT ||
|
||||
|
@ -2841,7 +2261,8 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
|
|||
|
||||
// For default action, we should climb up the tree if cannot scroll it
|
||||
// by the event actually.
|
||||
bool canScroll = CanScrollOn(frameToScroll, aDirectionX, aDirectionY);
|
||||
bool canScroll =
|
||||
WheelHandlingUtils::CanScrollOn(frameToScroll, aDirectionX, aDirectionY);
|
||||
// Comboboxes need special care.
|
||||
nsIComboboxControlFrame* comboBox = do_QueryFrame(scrollFrame);
|
||||
if (comboBox) {
|
||||
|
@ -3402,14 +2823,14 @@ nsEventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
|||
case NS_MOUSE_BUTTON_UP:
|
||||
{
|
||||
ClearGlobalActiveContent(this);
|
||||
if (IsMouseEventReal(aEvent)) {
|
||||
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
|
||||
if (mouseEvent && mouseEvent->IsReal()) {
|
||||
if (!mCurrentTarget) {
|
||||
GetEventTarget();
|
||||
}
|
||||
// Make sure to dispatch the click even if there is no frame for
|
||||
// the current target element. This is required for Web compatibility.
|
||||
ret = CheckForAndDispatchClick(presContext, aEvent->AsMouseEvent(),
|
||||
aStatus);
|
||||
ret = CheckForAndDispatchClick(presContext, mouseEvent, aStatus);
|
||||
}
|
||||
|
||||
nsIPresShell *shell = presContext->GetPresShell();
|
||||
|
@ -5404,8 +4825,8 @@ nsEventStateManager::DoContentCommandScrollEvent(
|
|||
ps->GetFrameToScrollAsScrollable(nsIPresShell::eEither);
|
||||
aEvent->mIsEnabled = sf ?
|
||||
(aEvent->mScroll.mIsHorizontal ?
|
||||
CanScrollOn(sf, aEvent->mScroll.mAmount, 0) :
|
||||
CanScrollOn(sf, 0, aEvent->mScroll.mAmount)) : false;
|
||||
WheelHandlingUtils::CanScrollOn(sf, aEvent->mScroll.mAmount, 0) :
|
||||
WheelHandlingUtils::CanScrollOn(sf, 0, aEvent->mScroll.mAmount)) : false;
|
||||
|
||||
if (!aEvent->mIsEnabled || aEvent->mOnlyEnabledCheck) {
|
||||
return NS_OK;
|
||||
|
|
|
@ -28,6 +28,8 @@ class EnterLeaveDispatcher;
|
|||
class nsIMarkupDocumentViewer;
|
||||
class nsIScrollableFrame;
|
||||
class nsITimer;
|
||||
class nsMouseWheelTransaction;
|
||||
class nsScrollbarsForWheel;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
|
|
@ -277,6 +277,15 @@ public:
|
|||
{
|
||||
return message == NS_CONTEXTMENU && context == eContextMenuKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the event is a real mouse event. Otherwise, i.e., it's
|
||||
* a synthesized event by scroll or something, returns false.
|
||||
*/
|
||||
bool IsReal() const
|
||||
{
|
||||
return reason == eReal;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
|
|
Загрузка…
Ссылка в новой задаче