summaryrefslogtreecommitdiff
path: root/Source/WebCore/page/EventHandler.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2012-11-22 09:09:45 +0100
committerSimon Hausmann <simon.hausmann@digia.com>2012-11-22 09:10:13 +0100
commit470286ecfe79d59df14944e5b5d34630fc739391 (patch)
tree43983212872e06cebefd2ae474418fa2908ca54c /Source/WebCore/page/EventHandler.cpp
parent23037105e948c2065da5a937d3a2396b0ff45c1e (diff)
downloadqtwebkit-470286ecfe79d59df14944e5b5d34630fc739391.tar.gz
Imported WebKit commit e89504fa9195b2063b2530961d4b73dd08de3242 (http://svn.webkit.org/repository/webkit/trunk@135485)
Change-Id: I03774e5ac79721c13ffa30d152537a74d0b12e66 Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Diffstat (limited to 'Source/WebCore/page/EventHandler.cpp')
-rw-r--r--Source/WebCore/page/EventHandler.cpp182
1 files changed, 117 insertions, 65 deletions
diff --git a/Source/WebCore/page/EventHandler.cpp b/Source/WebCore/page/EventHandler.cpp
index 92055a123..ac50de5c6 100644
--- a/Source/WebCore/page/EventHandler.cpp
+++ b/Source/WebCore/page/EventHandler.cpp
@@ -136,11 +136,17 @@ using namespace SVGNames;
// When the autoscroll or the panScroll is triggered when do the scroll every 0.05s to make it smooth
const double autoscrollInterval = 0.05;
-// The amount of time to wait before sending a fake mouse event, triggered
-// during a scroll. The short interval is used if the content responds to the mouse events quickly enough,
-// otherwise the long interval is used.
-const double fakeMouseMoveShortInterval = 0.1;
-const double fakeMouseMoveLongInterval = 0.250;
+// The amount of time to wait before sending a fake mouse event, triggered during a scroll.
+const double fakeMouseMoveMinimumInterval = 0.1;
+// Amount to increase the fake mouse event throttling when the running average exceeds the delay.
+// Picked fairly arbitrarily.
+const double fakeMouseMoveIntervalIncrease = 0.05;
+const double fakeMouseMoveRunningAverageCount = 10;
+// Decrease the fakeMouseMoveInterval when the current delay is >2x the running average,
+// but only decrease to 3/4 the current delay to avoid too much thrashing.
+// Not sure this distinction really matters in practice.
+const double fakeMouseMoveIntervalReductionLimit = 0.5;
+const double fakeMouseMoveIntervalReductionFraction = 0.75;
enum NoCursorChangeType { NoCursorChange };
@@ -150,28 +156,35 @@ public:
OptionalCursor(const Cursor& cursor) : m_isCursorChange(true), m_cursor(cursor) { }
bool isCursorChange() const { return m_isCursorChange; }
- const Cursor& cursor() const { return m_cursor; }
+ const Cursor& cursor() const { ASSERT(m_isCursorChange); return m_cursor; }
private:
bool m_isCursorChange;
Cursor m_cursor;
};
-class MaximumDurationTracker {
+class RunningAverageDurationTracker {
public:
- explicit MaximumDurationTracker(double *maxDuration)
- : m_maxDuration(maxDuration)
+ RunningAverageDurationTracker(double* average, unsigned numberOfRunsToTrack)
+ : m_average(average)
+ , m_numberOfRunsToTrack(numberOfRunsToTrack)
, m_start(monotonicallyIncreasingTime())
{
}
- ~MaximumDurationTracker()
+ ~RunningAverageDurationTracker()
{
- *m_maxDuration = max(*m_maxDuration, monotonicallyIncreasingTime() - m_start);
+ double duration = monotonicallyIncreasingTime() - m_start;
+ if (!*m_average) {
+ *m_average = duration;
+ return;
+ }
+ *m_average = (*m_average * (m_numberOfRunsToTrack - 1) + (duration)) / m_numberOfRunsToTrack;
}
private:
- double* m_maxDuration;
+ double* m_average;
+ unsigned m_numberOfRunsToTrack;
double m_start;
};
@@ -316,7 +329,7 @@ EventHandler::EventHandler(Frame* frame)
, m_autoscrollInProgress(false)
, m_mouseDownMayStartAutoscroll(false)
, m_mouseDownWasInSubframe(false)
- , m_fakeMouseMoveEventTimer(this, &EventHandler::fakeMouseMoveEventTimerFired)
+ , m_fakeMouseMoveEventTimer(this, &EventHandler::fakeMouseMoveEventTimerFired, fakeMouseMoveMinimumInterval)
#if ENABLE(SVG)
, m_svgPan(false)
#endif
@@ -333,7 +346,7 @@ EventHandler::EventHandler(Frame* frame)
#if ENABLE(TOUCH_EVENTS)
, m_touchPressed(false)
#endif
- , m_maxMouseMovedDuration(0)
+ , m_mouseMovedDurationRunningAverage(0)
, m_baseEventType(PlatformEvent::NoType)
{
}
@@ -384,8 +397,9 @@ void EventHandler::clear()
#endif
#if ENABLE(GESTURE_EVENTS)
m_scrollGestureHandlingNode = 0;
+ m_scrollbarHandlingScrollGesture = 0;
#endif
- m_maxMouseMovedDuration = 0;
+ m_mouseMovedDurationRunningAverage = 0;
m_baseEventType = PlatformEvent::NoType;
}
@@ -773,7 +787,7 @@ bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const
HitTestResult result(view->windowToContents(event.position()));
m_frame->contentRenderer()->hitTest(request, result);
DragState state;
- return result.innerNode() && page->dragController()->draggableNode(m_frame, result.innerNode(), roundedIntPoint(result.point()), state);
+ return result.innerNode() && page->dragController()->draggableNode(m_frame, result.innerNode(), result.roundedPointInInnerNodeFrame(), state);
}
void EventHandler::updateSelectionForMouseDrag()
@@ -1101,9 +1115,23 @@ DragSourceAction EventHandler::updateDragSourceActionsAllowed() const
return page->dragController()->delegateDragSourceAction(view->contentsToRootView(m_mouseDownPos));
}
#endif // ENABLE(DRAG_SUPPORT)
-
+
HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, bool allowShadowContent, bool ignoreClipping, HitTestScrollbars testScrollbars, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding)
{
+ // We always send hitTestResultAtPoint to the main frame if we have one,
+ // otherwise we might hit areas that are obscured by higher frames.
+ if (Page* page = m_frame->page()) {
+ Frame* mainFrame = page->mainFrame();
+ if (m_frame != mainFrame) {
+ FrameView* frameView = m_frame->view();
+ FrameView* mainView = mainFrame->view();
+ if (frameView && mainView) {
+ IntPoint mainFramePoint = mainView->rootViewToContents(frameView->contentsToRootView(roundedIntPoint(point)));
+ return mainFrame->eventHandler()->hitTestResultAtPoint(mainFramePoint, allowShadowContent, ignoreClipping, testScrollbars, hitType, padding);
+ }
+ }
+ }
+
HitTestResult result(point, padding.height(), padding.width(), padding.height(), padding.width());
if (!m_frame->contentRenderer())
@@ -1112,7 +1140,8 @@ HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, bool
hitType |= HitTestRequest::IgnoreClipping;
if (allowShadowContent)
hitType |= HitTestRequest::AllowShadowContent;
- m_frame->contentRenderer()->hitTest(HitTestRequest(hitType), result);
+ HitTestRequest request(hitType);
+ m_frame->contentRenderer()->hitTest(request, result);
while (true) {
Node* n = result.innerNode();
@@ -1129,7 +1158,8 @@ HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, bool
LayoutPoint widgetPoint(result.localPoint().x() + view->scrollX() - renderWidget->borderLeft() - renderWidget->paddingLeft(),
result.localPoint().y() + view->scrollY() - renderWidget->borderTop() - renderWidget->paddingTop());
HitTestResult widgetHitTestResult(widgetPoint, padding.height(), padding.width(), padding.height(), padding.width());
- frame->contentRenderer()->hitTest(HitTestRequest(hitType), widgetHitTestResult);
+ widgetHitTestResult.setPointInMainFrame(result.pointInMainFrame());
+ frame->contentRenderer()->hitTest(request, widgetHitTestResult);
result = widgetHitTestResult;
if (testScrollbars == ShouldHitTestScrollbars) {
@@ -1138,21 +1168,6 @@ HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, bool
result.setScrollbar(eventScrollbar);
}
}
-
- // If our HitTestResult is not visible, then we started hit testing too far down the frame chain.
- // Another hit test at the main frame level should get us the correct visible result.
- Frame* resultFrame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : 0;
- if (Page* page = m_frame->page()) {
- Frame* mainFrame = page->mainFrame();
- if (m_frame != mainFrame && resultFrame && resultFrame != mainFrame) {
- FrameView* resultView = resultFrame->view();
- FrameView* mainView = mainFrame->view();
- if (resultView && mainView) {
- IntPoint mainFramePoint = mainView->rootViewToContents(resultView->contentsToRootView(roundedIntPoint(result.point())));
- result = mainFrame->eventHandler()->hitTestResultAtPoint(mainFramePoint, allowShadowContent, ignoreClipping, testScrollbars, hitType, padding);
- }
- }
- }
if (!allowShadowContent)
result.setToNonShadowAncestor();
@@ -1526,7 +1541,7 @@ OptionalCursor EventHandler::selectCursor(const MouseEventWithHitTestResults& ev
}
return pointerCursor();
}
-
+
static LayoutPoint documentPointForWindowPoint(Frame* frame, const IntPoint& windowPoint)
{
FrameView* view = frame->view();
@@ -1732,7 +1747,7 @@ static RenderLayer* layerForNode(Node* node)
bool EventHandler::mouseMoved(const PlatformMouseEvent& event)
{
RefPtr<FrameView> protector(m_frame->view());
- MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration);
+ RunningAverageDurationTracker durationTracker(&m_mouseMovedDurationRunningAverage, fakeMouseMoveRunningAverageCount);
#if ENABLE(TOUCH_EVENTS)
@@ -1861,8 +1876,10 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi
scrollbar->mouseMoved(mouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering.
if (FrameView* view = m_frame->view()) {
OptionalCursor optionalCursor = selectCursor(mev, scrollbar);
- if (optionalCursor.isCursorChange())
- view->setCursor(optionalCursor.cursor());
+ if (optionalCursor.isCursorChange()) {
+ m_currentMouseCursor = optionalCursor.cursor();
+ view->setCursor(m_currentMouseCursor);
+ }
}
}
@@ -1985,7 +2002,7 @@ bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& mouseEve
Frame* focusFrame = m_frame->page()->focusController()->focusedOrMainFrame();
// Do not paste here if the focus was moved somewhere else.
if (m_frame == focusFrame && m_frame->editor()->client()->supportsGlobalSelection())
- return m_frame->editor()->command(AtomicString("PasteGlobalSelection")).execute();
+ return m_frame->editor()->command(ASCIILiteral("PasteGlobalSelection")).execute();
return false;
}
@@ -2506,15 +2523,17 @@ bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
return false;
Node* eventTarget = 0;
- if (gestureEvent.type() == PlatformEvent::GestureScrollEnd || gestureEvent.type() == PlatformEvent::GestureScrollUpdate)
+ Scrollbar* scrollbar = 0;
+ if (gestureEvent.type() == PlatformEvent::GestureScrollEnd || gestureEvent.type() == PlatformEvent::GestureScrollUpdate) {
+ scrollbar = m_scrollbarHandlingScrollGesture.get();
eventTarget = m_scrollGestureHandlingNode.get();
+ }
IntPoint adjustedPoint = gestureEvent.position();
HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent;
if (gestureEvent.type() == PlatformEvent::GestureTapDown) {
#if ENABLE(TOUCH_ADJUSTMENT)
- if (!gestureEvent.area().isEmpty())
- adjustGesturePosition(gestureEvent, adjustedPoint);
+ adjustGesturePosition(gestureEvent, adjustedPoint);
#endif
hitType |= HitTestRequest::Active;
} else if (gestureEvent.type() == PlatformEvent::GestureTap || gestureEvent.type() == PlatformEvent::GestureTapDownCancel)
@@ -2525,10 +2544,27 @@ bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
if (!shouldGesturesTriggerActive())
hitType |= HitTestRequest::ReadOnly;
- if (!eventTarget || !(hitType & HitTestRequest::ReadOnly)) {
+ if ((!scrollbar && !eventTarget) || !(hitType & HitTestRequest::ReadOnly)) {
IntPoint hitTestPoint = m_frame->view()->windowToContents(adjustedPoint);
- HitTestResult result = hitTestResultAtPoint(hitTestPoint, false, false, DontHitTestScrollbars, hitType);
+ HitTestResult result = hitTestResultAtPoint(hitTestPoint, false, false, ShouldHitTestScrollbars, hitType);
eventTarget = result.targetNode();
+ if (!scrollbar) {
+ FrameView* view = m_frame->view();
+ scrollbar = view ? view->scrollbarAtPoint(gestureEvent.position()) : 0;
+ }
+ if (!scrollbar)
+ scrollbar = result.scrollbar();
+ }
+
+ if (scrollbar) {
+ bool eventSwallowed = scrollbar->gestureEvent(gestureEvent);
+ if (gestureEvent.type() == PlatformEvent::GestureScrollBegin && eventSwallowed)
+ m_scrollbarHandlingScrollGesture = scrollbar;
+ else if (gestureEvent.type() == PlatformEvent::GestureScrollEnd || !eventSwallowed)
+ m_scrollbarHandlingScrollGesture = 0;
+
+ if (eventSwallowed)
+ return true;
}
if (eventTarget) {
@@ -2582,8 +2618,7 @@ bool EventHandler::handleGestureTap(const PlatformGestureEvent& gestureEvent)
// FIXME: Refactor this code to not hit test multiple times. We use the adjusted position to ensure that the correct node is targeted by the later redundant hit tests.
IntPoint adjustedPoint = gestureEvent.position();
#if ENABLE(TOUCH_ADJUSTMENT)
- if (!gestureEvent.area().isEmpty())
- adjustGesturePosition(gestureEvent, adjustedPoint);
+ adjustGesturePosition(gestureEvent, adjustedPoint);
#endif
PlatformMouseEvent fakeMouseMove(adjustedPoint, gestureEvent.globalPosition(),
@@ -2645,6 +2680,11 @@ bool EventHandler::handleGestureScrollUpdate(const PlatformGestureEvent& gesture
return handleGestureScrollCore(gestureEvent, ScrollByPixelWheelEvent, true);
}
+bool EventHandler::isScrollbarHandlingGestures() const
+{
+ return m_scrollbarHandlingScrollGesture.get();
+}
+
bool EventHandler::handleGestureScrollCore(const PlatformGestureEvent& gestureEvent, PlatformWheelEventGranularity granularity, bool latchedWheel)
{
const float tickDivisor = (float)WheelEvent::tickMultiplier;
@@ -2660,6 +2700,14 @@ bool EventHandler::handleGestureScrollCore(const PlatformGestureEvent& gestureEv
#endif
#if ENABLE(TOUCH_ADJUSTMENT)
+bool EventHandler::shouldApplyTouchAdjustment(const PlatformGestureEvent& event) const
+{
+ if (m_frame->settings() && !m_frame->settings()->touchAdjustmentEnabled())
+ return false;
+ return !event.area().isEmpty();
+}
+
+
bool EventHandler::bestClickableNodeForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntPoint& targetPoint, Node*& targetNode)
{
HitTestRequest::HitTestRequestType hitType = HitTestRequest::ReadOnly | HitTestRequest::Active;
@@ -2703,6 +2751,9 @@ bool EventHandler::bestZoomableAreaForTouchPoint(const IntPoint& touchCenter, co
bool EventHandler::adjustGesturePosition(const PlatformGestureEvent& gestureEvent, IntPoint& adjustedPoint)
{
+ if (!shouldApplyTouchAdjustment(gestureEvent))
+ return false;
+
Node* targetNode = 0;
switch (gestureEvent.type()) {
case PlatformEvent::GestureTap:
@@ -2729,6 +2780,8 @@ bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event)
if (!v)
return false;
+ // Clear mouse press state to avoid initiating a drag while context menu is up.
+ m_mousePressed = false;
bool swallowEvent;
LayoutPoint viewportPos = v->windowToContents(event.position());
HitTestRequest request(HitTestRequest::Active);
@@ -2759,6 +2812,9 @@ bool EventHandler::sendContextMenuEventForKey()
if (!doc)
return false;
+ // Clear mouse press state to avoid initiating a drag while context menu is up.
+ m_mousePressed = false;
+
static const int kContextMenuMargin = 1;
#if OS(WINDOWS) && !OS(WINCE)
@@ -2833,8 +2889,7 @@ bool EventHandler::sendContextMenuEventForGesture(const PlatformGestureEvent& ev
IntPoint adjustedPoint = event.position();
#if ENABLE(TOUCH_ADJUSTMENT)
- if (!event.area().isEmpty())
- adjustGesturePosition(event, adjustedPoint);
+ adjustGesturePosition(event, adjustedPoint);
#endif
PlatformMouseEvent mouseEvent(adjustedPoint, event.globalPosition(), RightButton, eventType, 1, false, false, false, false, WTF::currentTime());
// To simulate right-click behavior, we send a right mouse down and then
@@ -2862,18 +2917,15 @@ void EventHandler::dispatchFakeMouseMoveEventSoon()
if (settings && !settings->deviceSupportsMouse())
return;
- // If the content has ever taken longer than fakeMouseMoveShortInterval we
- // reschedule the timer and use a longer time. This will cause the content
- // to receive these moves only after the user is done scrolling, reducing
- // pauses during the scroll.
- if (m_maxMouseMovedDuration > fakeMouseMoveShortInterval) {
- if (m_fakeMouseMoveEventTimer.isActive())
- m_fakeMouseMoveEventTimer.stop();
- m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveLongInterval);
- } else {
- if (!m_fakeMouseMoveEventTimer.isActive())
- m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveShortInterval);
- }
+ // Adjust the mouse move throttling so that it's roughly around our running average of the duration of mousemove events.
+ // This will cause the content to receive these moves only after the user is done scrolling, reducing pauses during the scroll.
+ // This will only measure the duration of the mousemove event though (not for example layouts),
+ // so maintain at least a minimum interval.
+ if (m_mouseMovedDurationRunningAverage > m_fakeMouseMoveEventTimer.delay())
+ m_fakeMouseMoveEventTimer.setDelay(m_mouseMovedDurationRunningAverage + fakeMouseMoveIntervalIncrease);
+ else if (m_mouseMovedDurationRunningAverage < fakeMouseMoveIntervalReductionLimit * m_fakeMouseMoveEventTimer.delay())
+ m_fakeMouseMoveEventTimer.setDelay(max(fakeMouseMoveMinimumInterval, fakeMouseMoveIntervalReductionFraction * m_fakeMouseMoveEventTimer.delay()));
+ m_fakeMouseMoveEventTimer.restart();
}
void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad)
@@ -2893,7 +2945,7 @@ void EventHandler::cancelFakeMouseMoveEvent()
m_fakeMouseMoveEventTimer.stop();
}
-void EventHandler::fakeMouseMoveEventTimerFired(Timer<EventHandler>* timer)
+void EventHandler::fakeMouseMoveEventTimerFired(DeferrableOneShotTimer<EventHandler>* timer)
{
ASSERT_UNUSED(timer, timer == &m_fakeMouseMoveEventTimer);
ASSERT(!m_mousePressed);
@@ -3112,10 +3164,10 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent)
static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier)
{
- DEFINE_STATIC_LOCAL(AtomicString, Down, ("Down"));
- DEFINE_STATIC_LOCAL(AtomicString, Up, ("Up"));
- DEFINE_STATIC_LOCAL(AtomicString, Left, ("Left"));
- DEFINE_STATIC_LOCAL(AtomicString, Right, ("Right"));
+ DEFINE_STATIC_LOCAL(AtomicString, Down, ("Down", AtomicString::ConstructFromLiteral));
+ DEFINE_STATIC_LOCAL(AtomicString, Up, ("Up", AtomicString::ConstructFromLiteral));
+ DEFINE_STATIC_LOCAL(AtomicString, Left, ("Left", AtomicString::ConstructFromLiteral));
+ DEFINE_STATIC_LOCAL(AtomicString, Right, ("Right", AtomicString::ConstructFromLiteral));
FocusDirection retVal = FocusDirectionNone;