2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
4 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "core/page/EventHandler.h"
31 #include "HTMLNames.h"
32 #include "RuntimeEnabledFeatures.h"
34 #include "bindings/v8/ExceptionStatePlaceholder.h"
35 #include "core/dom/Clipboard.h"
36 #include "core/dom/DataObject.h"
37 #include "core/dom/Document.h"
38 #include "core/dom/DocumentMarkerController.h"
39 #include "core/dom/FullscreenElementStack.h"
40 #include "core/dom/NodeRenderingTraversal.h"
41 #include "core/dom/TouchList.h"
42 #include "core/dom/shadow/ShadowRoot.h"
43 #include "core/editing/Editor.h"
44 #include "core/editing/FrameSelection.h"
45 #include "core/editing/TextIterator.h"
46 #include "core/editing/htmlediting.h"
47 #include "core/events/DOMWindowEventQueue.h"
48 #include "core/events/EventPath.h"
49 #include "core/events/KeyboardEvent.h"
50 #include "core/events/MouseEvent.h"
51 #include "core/events/TextEvent.h"
52 #include "core/events/ThreadLocalEventNames.h"
53 #include "core/events/TouchEvent.h"
54 #include "core/events/WheelEvent.h"
55 #include "core/fetch/ImageResource.h"
56 #include "core/html/HTMLDialogElement.h"
57 #include "core/html/HTMLFrameElementBase.h"
58 #include "core/html/HTMLFrameSetElement.h"
59 #include "core/html/HTMLInputElement.h"
60 #include "core/loader/FrameLoader.h"
61 #include "core/loader/FrameLoaderClient.h"
62 #include "core/page/AutoscrollController.h"
63 #include "core/page/BackForwardClient.h"
64 #include "core/page/Chrome.h"
65 #include "core/page/ChromeClient.h"
66 #include "core/page/DragController.h"
67 #include "core/page/DragState.h"
68 #include "core/page/EditorClient.h"
69 #include "core/page/FocusController.h"
70 #include "core/frame/Frame.h"
71 #include "core/page/FrameTree.h"
72 #include "core/frame/FrameView.h"
73 #include "core/inspector/InspectorController.h"
74 #include "core/page/MouseEventWithHitTestResults.h"
75 #include "core/page/Page.h"
76 #include "core/frame/Settings.h"
77 #include "core/page/SpatialNavigation.h"
78 #include "core/page/TouchAdjustment.h"
79 #include "core/rendering/HitTestRequest.h"
80 #include "core/rendering/HitTestResult.h"
81 #include "core/rendering/RenderFlowThread.h"
82 #include "core/rendering/RenderLayer.h"
83 #include "core/rendering/RenderRegion.h"
84 #include "core/rendering/RenderTextControlSingleLine.h"
85 #include "core/rendering/RenderView.h"
86 #include "core/rendering/RenderWidget.h"
87 #include "core/rendering/style/CursorList.h"
88 #include "core/rendering/style/RenderStyle.h"
89 #include "core/svg/SVGDocument.h"
90 #include "core/svg/SVGElementInstance.h"
91 #include "core/svg/SVGUseElement.h"
92 #include "platform/PlatformGestureEvent.h"
93 #include "platform/PlatformKeyboardEvent.h"
94 #include "platform/PlatformTouchEvent.h"
95 #include "platform/PlatformWheelEvent.h"
96 #include "platform/WindowsKeyboardCodes.h"
97 #include "platform/geometry/FloatPoint.h"
98 #include "platform/graphics/Image.h"
99 #include "platform/scroll/ScrollAnimator.h"
100 #include "platform/scroll/Scrollbar.h"
101 #include "wtf/Assertions.h"
102 #include "wtf/CurrentTime.h"
103 #include "wtf/StdLibExtras.h"
104 #include "wtf/TemporaryChange.h"
108 using namespace HTMLNames;
109 using namespace SVGNames;
111 // The link drag hysteresis is much larger than the others because there
112 // needs to be enough space to cancel the link press without starting a link drag,
113 // and because dragging links is rare.
114 static const int LinkDragHysteresis = 40;
115 static const int ImageDragHysteresis = 5;
116 static const int TextDragHysteresis = 3;
117 static const int GeneralDragHysteresis = 3;
119 // The amount of time to wait before sending a fake mouse event, triggered
120 // during a scroll. The short interval is used if the content responds to the mouse events quickly enough,
121 // otherwise the long interval is used.
122 static const double fakeMouseMoveShortInterval = 0.1;
123 static const double fakeMouseMoveLongInterval = 0.250;
125 // The amount of time to wait for a cursor update on style and layout changes
126 // Set to 50Hz, no need to be faster than common screen refresh rate
127 static const double cursorUpdateInterval = 0.02;
129 static const int maximumCursorSize = 128;
131 // It's pretty unlikely that a scale of less than one would ever be used. But all we really
132 // need to ensure here is that the scale isn't so small that integer overflow can occur when
133 // dividing cursor sizes (limited above) by the scale.
134 static const double minimumCursorScale = 0.001;
136 // The minimum amount of time an element stays active after a ShowPress
137 // This is roughly 2 frames, which should be long enough to be noticeable.
138 static const double minimumActiveInterval = 0.032;
141 static const double TextDragDelay = 0.15;
143 static const double TextDragDelay = 0.0;
146 enum NoCursorChangeType { NoCursorChange };
148 class OptionalCursor {
150 OptionalCursor(NoCursorChangeType) : m_isCursorChange(false) { }
151 OptionalCursor(const Cursor& cursor) : m_isCursorChange(true), m_cursor(cursor) { }
153 bool isCursorChange() const { return m_isCursorChange; }
154 const Cursor& cursor() const { ASSERT(m_isCursorChange); return m_cursor; }
157 bool m_isCursorChange;
161 class MaximumDurationTracker {
163 explicit MaximumDurationTracker(double *maxDuration)
164 : m_maxDuration(maxDuration)
165 , m_start(monotonicallyIncreasingTime())
169 ~MaximumDurationTracker()
171 *m_maxDuration = max(*m_maxDuration, monotonicallyIncreasingTime() - m_start);
175 double* m_maxDuration;
179 class SyntheticTouchPoint : public PlatformTouchPoint {
182 // The default values are based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
183 explicit SyntheticTouchPoint(const PlatformMouseEvent& event)
185 const static int idDefaultValue = 0;
186 const static int radiusYDefaultValue = 1;
187 const static int radiusXDefaultValue = 1;
188 const static float rotationAngleDefaultValue = 0.0f;
189 const static float forceDefaultValue = 1.0f;
191 m_id = idDefaultValue; // There is only one active TouchPoint.
192 m_screenPos = event.globalPosition();
193 m_pos = event.position();
194 m_radiusY = radiusYDefaultValue;
195 m_radiusX = radiusXDefaultValue;
196 m_rotationAngle = rotationAngleDefaultValue;
197 m_force = forceDefaultValue;
199 PlatformEvent::Type type = event.type();
200 ASSERT(type == PlatformEvent::MouseMoved || type == PlatformEvent::MousePressed || type == PlatformEvent::MouseReleased);
203 case PlatformEvent::MouseMoved:
204 m_state = TouchMoved;
206 case PlatformEvent::MousePressed:
207 m_state = TouchPressed;
209 case PlatformEvent::MouseReleased:
210 m_state = TouchReleased;
213 ASSERT_NOT_REACHED();
219 class SyntheticSingleTouchEvent : public PlatformTouchEvent {
221 explicit SyntheticSingleTouchEvent(const PlatformMouseEvent& event)
223 switch (event.type()) {
224 case PlatformEvent::MouseMoved:
227 case PlatformEvent::MousePressed:
230 case PlatformEvent::MouseReleased:
234 ASSERT_NOT_REACHED();
238 m_timestamp = event.timestamp();
239 m_modifiers = event.modifiers();
240 m_touchPoints.append(SyntheticTouchPoint(event));
244 static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned deltaMode)
247 case WheelEvent::DOM_DELTA_PAGE:
249 case WheelEvent::DOM_DELTA_LINE:
251 case WheelEvent::DOM_DELTA_PIXEL:
252 return ScrollByPixel;
254 return ScrollByPixel;
258 // Refetch the event target node if it is removed or currently is the shadow node inside an <input> element.
259 // If a mouse event handler changes the input element type to one that has a widget associated,
260 // we'd like to EventHandler::handleMousePressEvent to pass the event to the widget and thus the
261 // event target node can't still be the shadow node.
262 static inline bool shouldRefetchEventTarget(const MouseEventWithHitTestResults& mev)
264 Node* targetNode = mev.targetNode();
265 if (!targetNode || !targetNode->parentNode())
267 return targetNode->isShadowRoot() && toShadowRoot(targetNode)->host()->hasTagName(inputTag);
270 EventHandler::EventHandler(Frame* frame)
272 , m_mousePressed(false)
273 , m_capturesDragging(false)
274 , m_mouseDownMayStartSelect(false)
275 , m_mouseDownMayStartDrag(false)
276 , m_mouseDownWasSingleClickInSelection(false)
277 , m_selectionInitiationState(HaveNotStartedSelection)
278 , m_hoverTimer(this, &EventHandler::hoverTimerFired)
279 , m_cursorUpdateTimer(this, &EventHandler::cursorUpdateTimerFired)
280 , m_mouseDownMayStartAutoscroll(false)
281 , m_mouseDownWasInSubframe(false)
282 , m_fakeMouseMoveEventTimer(this, &EventHandler::fakeMouseMoveEventTimerFired)
284 , m_resizeScrollableArea(0)
285 , m_eventHandlerWillResetCapturingMouseEventsNode(0)
287 , m_shouldOnlyFireDragOverEvent(false)
288 , m_mousePositionIsUnknown(true)
289 , m_mouseDownTimestamp(0)
290 , m_widgetIsLatched(false)
291 , m_originatingTouchPointTargetKey(0)
292 , m_touchPressed(false)
293 , m_scrollGestureHandlingNode(0)
294 , m_lastHitTestResultOverWidget(false)
295 , m_maxMouseMovedDuration(0)
296 , m_baseEventType(PlatformEvent::NoType)
297 , m_didStartDrag(false)
298 , m_longTapShouldInvokeContextMenu(false)
299 , m_syntheticPageScaleFactor(0)
300 , m_activeIntervalTimer(this, &EventHandler::activeIntervalTimerFired)
301 , m_lastShowPressTimestamp(0)
305 EventHandler::~EventHandler()
307 ASSERT(!m_fakeMouseMoveEventTimer.isActive());
310 DragState& EventHandler::dragState()
312 DEFINE_STATIC_LOCAL(DragState, state, ());
316 void EventHandler::clear()
319 m_cursorUpdateTimer.stop();
320 m_fakeMouseMoveEventTimer.stop();
321 m_activeIntervalTimer.stop();
322 m_resizeScrollableArea = 0;
323 m_nodeUnderMouse = 0;
324 m_lastNodeUnderMouse = 0;
325 m_instanceUnderMouse = 0;
326 m_lastInstanceUnderMouse = 0;
327 m_lastMouseMoveEventSubframe = 0;
328 m_lastScrollbarUnderMouse = 0;
331 m_frameSetBeingResized = 0;
333 m_shouldOnlyFireDragOverEvent = false;
334 m_mousePositionIsUnknown = true;
335 m_lastKnownMousePosition = IntPoint();
336 m_lastKnownMouseGlobalPosition = IntPoint();
337 m_lastMouseDownUserGestureToken.clear();
338 m_mousePressNode = 0;
339 m_mousePressed = false;
340 m_capturesDragging = false;
341 m_capturingMouseEventsNode = 0;
342 m_latchedWheelEventNode = 0;
343 m_previousWheelScrolledNode = 0;
344 m_originatingTouchPointTargets.clear();
345 m_originatingTouchPointDocument.clear();
346 m_originatingTouchPointTargetKey = 0;
347 m_scrollGestureHandlingNode = 0;
348 m_lastHitTestResultOverWidget = false;
349 m_previousGestureScrolledNode = 0;
350 m_scrollbarHandlingScrollGesture = 0;
351 m_maxMouseMovedDuration = 0;
352 m_baseEventType = PlatformEvent::NoType;
353 m_didStartDrag = false;
354 m_touchPressed = false;
355 m_mouseDownMayStartSelect = false;
356 m_mouseDownMayStartDrag = false;
357 m_lastShowPressTimestamp = 0;
358 m_lastDeferredTapElement = 0;
361 void EventHandler::nodeWillBeRemoved(Node& nodeToBeRemoved)
363 if (!nodeToBeRemoved.containsIncludingShadowDOM(m_clickNode.get()))
365 if (nodeToBeRemoved.isInShadowTree()) {
366 m_clickNode = nodeToBeRemoved.parentOrShadowHostNode();
368 // We don't dispatch click events if the mousedown node is removed
369 // before a mouseup event. It is compatible with IE and Firefox.
374 static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& newSelection)
376 if (selection.selection() != newSelection)
377 selection.setSelection(newSelection);
380 static inline bool dispatchSelectStart(Node* node)
382 if (!node || !node->renderer())
385 return node->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart));
388 static VisibleSelection expandSelectionToRespectUserSelectAll(Node* targetNode, const VisibleSelection& selection)
390 Node* rootUserSelectAll = Position::rootUserSelectAllForNode(targetNode);
391 if (!rootUserSelectAll)
394 VisibleSelection newSelection(selection);
395 newSelection.setBase(positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary));
396 newSelection.setExtent(positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary));
401 bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& selection, TextGranularity granularity)
403 if (Position::nodeIsUserSelectNone(targetNode))
406 if (!dispatchSelectStart(targetNode))
409 if (selection.isRange())
410 m_selectionInitiationState = ExtendedSelection;
412 granularity = CharacterGranularity;
413 m_selectionInitiationState = PlacedCaret;
416 m_frame->selection().setNonDirectionalSelectionIfNeeded(selection, granularity);
421 void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& result, AppendTrailingWhitespace appendTrailingWhitespace)
423 Node* innerNode = result.targetNode();
424 VisibleSelection newSelection;
426 if (innerNode && innerNode->renderer()) {
427 VisiblePosition pos(innerNode->renderer()->positionForPoint(result.localPoint()));
428 if (pos.isNotNull()) {
429 newSelection = VisibleSelection(pos);
430 newSelection.expandUsingGranularity(WordGranularity);
433 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
434 newSelection.appendTrailingWhitespace();
436 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
440 void EventHandler::selectClosestMisspellingFromHitTestResult(const HitTestResult& result, AppendTrailingWhitespace appendTrailingWhitespace)
442 Node* innerNode = result.targetNode();
443 VisibleSelection newSelection;
445 if (innerNode && innerNode->renderer()) {
446 VisiblePosition pos(innerNode->renderer()->positionForPoint(result.localPoint()));
447 Position start = pos.deepEquivalent();
448 Position end = pos.deepEquivalent();
449 if (pos.isNotNull()) {
450 Vector<DocumentMarker*> markers = innerNode->document().markers()->markersInRange(makeRange(pos, pos).get(), DocumentMarker::MisspellingMarkers());
451 if (markers.size() == 1) {
452 start.moveToOffset(markers[0]->startOffset());
453 end.moveToOffset(markers[0]->endOffset());
454 newSelection = VisibleSelection(start, end);
458 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
459 newSelection.appendTrailingWhitespace();
461 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
465 void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& result)
467 if (m_mouseDownMayStartSelect) {
468 selectClosestWordFromHitTestResult(result.hitTestResult(),
469 (result.event().clickCount() == 2 && m_frame->editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace);
473 void EventHandler::selectClosestMisspellingFromMouseEvent(const MouseEventWithHitTestResults& result)
475 if (m_mouseDownMayStartSelect) {
476 selectClosestMisspellingFromHitTestResult(result.hitTestResult(),
477 (result.event().clickCount() == 2 && m_frame->editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace);
481 void EventHandler::selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result)
483 if (!result.hitTestResult().isLiveLink())
484 return selectClosestWordFromMouseEvent(result);
486 Node* innerNode = result.targetNode();
488 if (innerNode && innerNode->renderer() && m_mouseDownMayStartSelect) {
489 VisibleSelection newSelection;
490 Element* URLElement = result.hitTestResult().URLElement();
491 VisiblePosition pos(innerNode->renderer()->positionForPoint(result.localPoint()));
492 if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(URLElement))
493 newSelection = VisibleSelection::selectionFromContentsOfNode(URLElement);
495 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
499 bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestResults& event)
501 if (event.event().button() != LeftButton)
504 if (m_frame->selection().isRange()) {
505 // A double-click when range is already selected
506 // should not change the selection. So, do not call
507 // selectClosestWordFromMouseEvent, but do set
508 // m_beganSelectingText to prevent handleMouseReleaseEvent
509 // from setting caret selection.
510 m_selectionInitiationState = ExtendedSelection;
512 selectClosestWordFromMouseEvent(event);
517 bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestResults& event)
519 if (event.event().button() != LeftButton)
522 Node* innerNode = event.targetNode();
523 if (!(innerNode && innerNode->renderer() && m_mouseDownMayStartSelect))
526 VisibleSelection newSelection;
527 VisiblePosition pos(innerNode->renderer()->positionForPoint(event.localPoint()));
528 if (pos.isNotNull()) {
529 newSelection = VisibleSelection(pos);
530 newSelection.expandUsingGranularity(ParagraphGranularity);
533 return updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), ParagraphGranularity);
536 static int textDistance(const Position& start, const Position& end)
538 RefPtr<Range> range = Range::create(*start.document(), start, end);
539 return TextIterator::rangeLength(range.get(), true);
542 bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestResults& event)
544 m_frame->document()->updateLayoutIgnorePendingStylesheets();
545 Node* innerNode = event.targetNode();
546 if (!(innerNode && innerNode->renderer() && m_mouseDownMayStartSelect))
549 // Extend the selection if the Shift key is down, unless the click is in a link.
550 bool extendSelection = event.event().shiftKey() && !event.isOverLink();
552 // Don't restart the selection when the mouse is pressed on an
553 // existing selection so we can allow for text dragging.
554 if (FrameView* view = m_frame->view()) {
555 LayoutPoint vPoint = view->windowToContents(event.event().position());
556 if (!extendSelection && m_frame->selection().contains(vPoint)) {
557 m_mouseDownWasSingleClickInSelection = true;
562 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(event.localPoint()));
563 if (visiblePos.isNull())
564 visiblePos = VisiblePosition(firstPositionInOrBeforeNode(innerNode), DOWNSTREAM);
565 Position pos = visiblePos.deepEquivalent();
567 VisibleSelection newSelection = m_frame->selection().selection();
568 TextGranularity granularity = CharacterGranularity;
570 if (extendSelection && newSelection.isCaretOrRange()) {
571 VisibleSelection selectionInUserSelectAll = expandSelectionToRespectUserSelectAll(innerNode, VisibleSelection(pos));
572 if (selectionInUserSelectAll.isRange()) {
573 if (comparePositions(selectionInUserSelectAll.start(), newSelection.start()) < 0)
574 pos = selectionInUserSelectAll.start();
575 else if (comparePositions(newSelection.end(), selectionInUserSelectAll.end()) < 0)
576 pos = selectionInUserSelectAll.end();
579 if (!m_frame->editor().behavior().shouldConsiderSelectionAsDirectional()) {
580 // See <rdar://problem/3668157> REGRESSION (Mail): shift-click deselects when selection
581 // was created right-to-left
582 Position start = newSelection.start();
583 Position end = newSelection.end();
584 int distanceToStart = textDistance(start, pos);
585 int distanceToEnd = textDistance(pos, end);
586 if (distanceToStart <= distanceToEnd)
587 newSelection = VisibleSelection(end, pos);
589 newSelection = VisibleSelection(start, pos);
591 newSelection.setExtent(pos);
593 if (m_frame->selection().granularity() != CharacterGranularity) {
594 granularity = m_frame->selection().granularity();
595 newSelection.expandUsingGranularity(m_frame->selection().granularity());
598 newSelection = expandSelectionToRespectUserSelectAll(innerNode, visiblePos);
600 bool handled = updateSelectionForMouseDownDispatchingSelectStart(innerNode, newSelection, granularity);
604 static inline bool canMouseDownStartSelect(Node* node)
606 if (!node || !node->renderer())
609 if (!node->canStartSelection())
615 bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& event)
618 dragState().m_dragSrc = 0;
620 cancelFakeMouseMoveEvent();
622 m_frame->document()->updateLayoutIgnorePendingStylesheets();
624 if (ScrollView* scrollView = m_frame->view()) {
625 if (scrollView->isPointInScrollbarCorner(event.event().position()))
629 bool singleClick = event.event().clickCount() <= 1;
631 // If we got the event back, that must mean it wasn't prevented,
632 // so it's allowed to start a drag or selection if it wasn't in a scrollbar.
633 m_mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode()) && !event.scrollbar();
635 m_mouseDownMayStartDrag = singleClick;
637 m_mouseDownWasSingleClickInSelection = false;
639 m_mouseDown = event.event();
641 if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event))
644 if (m_frame->document()->isSVGDocument()
645 && toSVGDocument(m_frame->document())->zoomAndPanEnabled()) {
646 if (event.event().shiftKey() && singleClick) {
648 toSVGDocument(m_frame->document())->startPan(m_frame->view()->windowToContents(event.event().position()));
653 // We don't do this at the start of mouse down handling,
654 // because we don't want to do it until we know we didn't hit a widget.
658 Node* innerNode = event.targetNode();
660 m_mousePressNode = innerNode;
661 m_dragStartPos = event.event().position();
663 bool swallowEvent = false;
664 m_mousePressed = true;
665 m_selectionInitiationState = HaveNotStartedSelection;
667 if (event.event().clickCount() == 2)
668 swallowEvent = handleMousePressEventDoubleClick(event);
669 else if (event.event().clickCount() >= 3)
670 swallowEvent = handleMousePressEventTripleClick(event);
672 swallowEvent = handleMousePressEventSingleClick(event);
674 m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect
675 || (m_mousePressNode && m_mousePressNode->renderBox() && m_mousePressNode->renderBox()->canBeProgramaticallyScrolled());
680 bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event)
685 if (handleDrag(event, ShouldCheckDragHysteresis))
688 Node* targetNode = event.targetNode();
689 if (event.event().button() != LeftButton || !targetNode)
692 RenderObject* renderer = targetNode->renderer();
694 Node* parent = EventPath::parent(targetNode);
698 renderer = parent->renderer();
699 if (!renderer || !renderer->isListBox())
703 m_mouseDownMayStartDrag = false;
705 if (m_mouseDownMayStartAutoscroll && !panScrollInProgress()) {
706 if (AutoscrollController* controller = autoscrollController()) {
707 controller->startAutoscrollForSelection(renderer);
708 m_mouseDownMayStartAutoscroll = false;
712 if (m_selectionInitiationState != ExtendedSelection) {
713 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
714 HitTestResult result(m_mouseDownPos);
715 m_frame->document()->renderView()->hitTest(request, result);
717 updateSelectionForMouseDrag(result);
719 updateSelectionForMouseDrag(event.hitTestResult());
723 void EventHandler::updateSelectionForMouseDrag()
725 FrameView* view = m_frame->view();
728 RenderView* renderer = m_frame->contentRenderer();
732 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
733 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
734 renderer->hitTest(request, result);
735 updateSelectionForMouseDrag(result);
738 void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResult)
740 if (!m_mouseDownMayStartSelect)
743 Node* target = hitTestResult.targetNode();
747 VisiblePosition targetPosition = m_frame->selection().selection().visiblePositionRespectingEditingBoundary(hitTestResult.localPoint(), target);
748 // Don't modify the selection if we're not on a node.
749 if (targetPosition.isNull())
752 // Restart the selection if this is the first mouse move. This work is usually
753 // done in handleMousePressEvent, but not if the mouse press was on an existing selection.
754 VisibleSelection newSelection = m_frame->selection().selection();
756 // Special case to limit selection to the containing block for SVG text.
757 // FIXME: Isn't there a better non-SVG-specific way to do this?
758 if (Node* selectionBaseNode = newSelection.base().deprecatedNode())
759 if (RenderObject* selectionBaseRenderer = selectionBaseNode->renderer())
760 if (selectionBaseRenderer->isSVGText())
761 if (target->renderer()->containingBlock() != selectionBaseRenderer->containingBlock())
764 if (m_selectionInitiationState == HaveNotStartedSelection && !dispatchSelectStart(target))
767 if (m_selectionInitiationState != ExtendedSelection) {
768 // Always extend selection here because it's caused by a mouse drag
769 m_selectionInitiationState = ExtendedSelection;
770 newSelection = VisibleSelection(targetPosition);
773 if (RuntimeEnabledFeatures::userSelectAllEnabled()) {
774 Node* rootUserSelectAllForMousePressNode = Position::rootUserSelectAllForNode(m_mousePressNode.get());
775 if (rootUserSelectAllForMousePressNode && rootUserSelectAllForMousePressNode == Position::rootUserSelectAllForNode(target)) {
776 newSelection.setBase(positionBeforeNode(rootUserSelectAllForMousePressNode).upstream(CanCrossEditingBoundary));
777 newSelection.setExtent(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary));
779 // Reset base for user select all when base is inside user-select-all area and extent < base.
780 if (rootUserSelectAllForMousePressNode && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint()), m_mousePressNode->renderer()->positionForPoint(m_dragStartPos)) < 0)
781 newSelection.setBase(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary));
783 Node* rootUserSelectAllForTarget = Position::rootUserSelectAllForNode(target);
784 if (rootUserSelectAllForTarget && m_mousePressNode->renderer() && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint()), m_mousePressNode->renderer()->positionForPoint(m_dragStartPos)) < 0)
785 newSelection.setExtent(positionBeforeNode(rootUserSelectAllForTarget).upstream(CanCrossEditingBoundary));
786 else if (rootUserSelectAllForTarget && m_mousePressNode->renderer())
787 newSelection.setExtent(positionAfterNode(rootUserSelectAllForTarget).downstream(CanCrossEditingBoundary));
789 newSelection.setExtent(targetPosition);
792 newSelection.setExtent(targetPosition);
795 if (m_frame->selection().granularity() != CharacterGranularity)
796 newSelection.expandUsingGranularity(m_frame->selection().granularity());
798 m_frame->selection().setNonDirectionalSelectionIfNeeded(newSelection, m_frame->selection().granularity(),
799 FrameSelection::AdjustEndpointsAtBidiBoundary);
802 bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
804 AutoscrollController* controller = autoscrollController();
805 if (controller && controller->autoscrollInProgress())
808 // Used to prevent mouseMoveEvent from initiating a drag before
809 // the mouse is pressed again.
810 m_mousePressed = false;
811 m_capturesDragging = false;
812 m_mouseDownMayStartDrag = false;
813 m_mouseDownMayStartSelect = false;
814 m_mouseDownMayStartAutoscroll = false;
815 m_mouseDownWasInSubframe = false;
817 bool handled = false;
819 // Clear the selection if the mouse didn't move after the last mouse
820 // press and it's not a context menu click. We do this so when clicking
821 // on the selection, the selection goes away. However, if we are
822 // editing, place the caret.
823 if (m_mouseDownWasSingleClickInSelection && m_selectionInitiationState != ExtendedSelection
824 && m_dragStartPos == event.event().position()
825 && m_frame->selection().isRange()
826 && event.event().button() != RightButton) {
827 VisibleSelection newSelection;
828 Node* node = event.targetNode();
829 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
830 if (node && node->renderer() && (caretBrowsing || node->rendererIsEditable())) {
831 VisiblePosition pos = VisiblePosition(node->renderer()->positionForPoint(event.localPoint()));
832 newSelection = VisibleSelection(pos);
835 setSelectionIfNeeded(m_frame->selection(), newSelection);
840 m_frame->selection().notifyRendererOfSelectionChange(UserTriggered);
842 m_frame->selection().selectFrameElementInParentIfFullySelected();
844 if (event.event().button() == MiddleButton && !event.isOverLink()) {
845 // Ignore handled, since we want to paste to where the caret was placed anyway.
846 handled = handlePasteGlobalSelection(event.event()) || handled;
854 void EventHandler::startPanScrolling(RenderObject* renderer)
856 if (!renderer->isBox())
858 AutoscrollController* controller = autoscrollController();
861 controller->startPanScrolling(toRenderBox(renderer), lastKnownMousePosition());
867 AutoscrollController* EventHandler::autoscrollController() const
869 if (Page* page = m_frame->page())
870 return &page->autoscrollController();
874 bool EventHandler::panScrollInProgress() const
876 return autoscrollController() && autoscrollController()->panScrollInProgress();
879 HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding)
881 // We always send hitTestResultAtPoint to the main frame if we have one,
882 // otherwise we might hit areas that are obscured by higher frames.
883 if (Page* page = m_frame->page()) {
884 Frame* mainFrame = page->mainFrame();
885 if (m_frame != mainFrame) {
886 FrameView* frameView = m_frame->view();
887 FrameView* mainView = mainFrame->view();
888 if (frameView && mainView) {
889 IntPoint mainFramePoint = mainView->rootViewToContents(frameView->contentsToRootView(roundedIntPoint(point)));
890 return mainFrame->eventHandler().hitTestResultAtPoint(mainFramePoint, hitType, padding);
895 HitTestResult result(point, padding.height(), padding.width(), padding.height(), padding.width());
897 // RenderView::hitTest causes a layout, and we don't want to hit that until the first
898 // layout because until then, there is nothing shown on the screen - the user can't
899 // have intentionally clicked on something belonging to this page. Furthermore,
900 // mousemove events before the first layout should not lead to a premature layout()
901 // happening, which could show a flash of white.
902 // See also the similar code in Document::prepareMouseEvent.
903 if (!m_frame->contentRenderer() || !m_frame->view() || !m_frame->view()->didFirstLayout())
906 // hitTestResultAtPoint is specifically used to hitTest into all frames, thus it always allows child frame content.
907 HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent);
908 m_frame->contentRenderer()->hitTest(request, result);
909 if (!request.readOnly())
910 m_frame->document()->updateHoverActiveState(request, result.innerElement());
912 if (request.disallowsShadowContent())
913 result.setToNodesInDocumentTreeScope();
918 void EventHandler::stopAutoscroll()
920 if (AutoscrollController* controller = autoscrollController())
921 controller->stopAutoscroll();
924 Node* EventHandler::mousePressNode() const
926 return m_mousePressNode.get();
929 bool EventHandler::scroll(ScrollDirection direction, ScrollGranularity granularity, Node* startNode, Node** stopNode, float delta, IntPoint absolutePoint)
934 Node* node = startNode;
937 node = m_frame->document()->focusedElement();
940 node = m_mousePressNode.get();
942 if (!node || !node->renderer())
945 RenderBox* curBox = node->renderer()->enclosingBox();
946 while (curBox && !curBox->isRenderView()) {
947 ScrollDirection physicalDirection = toPhysicalDirection(
948 direction, curBox->isHorizontalWritingMode(), curBox->style()->isFlippedBlocksWritingMode());
950 // If we're at the stopNode, we should try to scroll it but we shouldn't bubble past it
951 bool shouldStopBubbling = stopNode && *stopNode && curBox->node() == *stopNode;
952 bool didScroll = curBox->scroll(physicalDirection, granularity, delta);
954 if (didScroll && stopNode)
955 *stopNode = curBox->node();
957 if (didScroll || shouldStopBubbling) {
958 setFrameWasScrolledByUser();
962 // FIXME: This should probably move to a virtual method on RenderBox, something like
963 // RenderBox::scrollAncestor, and specialized for RenderFlowThread
964 curBox = curBox->containingBlock();
965 if (curBox && curBox->isRenderNamedFlowThread()) {
966 RenderBox* flowedBox = curBox;
968 if (RenderBox* startBox = node->renderBox())
969 flowedBox = startBox;
971 curBox = toRenderFlowThread(curBox)->regionFromAbsolutePointAndBox(absolutePoint, flowedBox);
978 bool EventHandler::bubblingScroll(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode)
980 // The layout needs to be up to date to determine if we can scroll. We may be
981 // here because of an onLoad event, in which case the final layout hasn't been performed yet.
982 m_frame->document()->updateLayoutIgnorePendingStylesheets();
983 if (scroll(direction, granularity, startingNode))
985 Frame* frame = m_frame;
986 FrameView* view = frame->view();
987 if (view && view->scroll(direction, granularity))
989 frame = frame->tree().parent();
992 return frame->eventHandler().bubblingScroll(direction, granularity, m_frame->ownerElement());
995 IntPoint EventHandler::lastKnownMousePosition() const
997 return m_lastKnownMousePosition;
1000 static Frame* subframeForTargetNode(Node* node)
1005 RenderObject* renderer = node->renderer();
1006 if (!renderer || !renderer->isWidget())
1009 Widget* widget = toRenderWidget(renderer)->widget();
1010 if (!widget || !widget->isFrameView())
1013 return &toFrameView(widget)->frame();
1016 static Frame* subframeForHitTestResult(const MouseEventWithHitTestResults& hitTestResult)
1018 if (!hitTestResult.isOverWidget())
1020 return subframeForTargetNode(hitTestResult.targetNode());
1023 static bool isSubmitImage(Node* node)
1025 return node && node->hasTagName(inputTag) && toHTMLInputElement(node)->isImageButton();
1028 // Returns true if the node's editable block is not current focused for editing
1029 static bool nodeIsNotBeingEdited(Node* node, Frame* frame)
1031 return frame->selection().rootEditableElement() != node->rootEditableElement();
1034 bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey)
1039 bool editable = node->rendererIsEditable();
1040 bool editableLinkEnabled = false;
1042 // If the link is editable, then we need to check the settings to see whether or not the link should be followed
1044 ASSERT(m_frame->settings());
1045 switch (m_frame->settings()->editableLinkBehavior()) {
1047 case EditableLinkDefaultBehavior:
1048 case EditableLinkAlwaysLive:
1049 editableLinkEnabled = true;
1052 case EditableLinkNeverLive:
1053 editableLinkEnabled = false;
1056 case EditableLinkLiveWhenNotFocused:
1057 editableLinkEnabled = nodeIsNotBeingEdited(node, m_frame) || shiftKey;
1060 case EditableLinkOnlyLiveWithShiftKey:
1061 editableLinkEnabled = shiftKey;
1066 return ((isOverLink || isSubmitImage(node)) && (!editable || editableLinkEnabled));
1069 void EventHandler::cursorUpdateTimerFired(Timer<EventHandler>*)
1072 ASSERT(m_frame->document());
1077 void EventHandler::updateCursor()
1079 if (m_mousePositionIsUnknown)
1082 FrameView* view = m_frame->view();
1083 if (!view || !view->shouldSetCursor())
1086 RenderView* renderView = view->renderView();
1094 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
1096 m_frame->document()->updateLayout();
1098 HitTestRequest request(HitTestRequest::ReadOnly);
1099 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
1100 renderView->hitTest(request, result);
1102 OptionalCursor optionalCursor = selectCursor(result, shiftKey);
1103 if (optionalCursor.isCursorChange()) {
1104 m_currentMouseCursor = optionalCursor.cursor();
1105 view->setCursor(m_currentMouseCursor);
1109 OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shiftKey)
1111 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode())
1112 return NoCursorChange;
1114 Page* page = m_frame->page();
1116 return NoCursorChange;
1118 if (panScrollInProgress())
1119 return NoCursorChange;
1122 Node* node = result.targetNode();
1124 return selectAutoCursor(result, node, iBeamCursor(), shiftKey);
1126 RenderObject* renderer = node->renderer();
1127 RenderStyle* style = renderer ? renderer->style() : 0;
1130 Cursor overrideCursor;
1131 switch (renderer->getCursor(roundedIntPoint(result.localPoint()), overrideCursor)) {
1132 case SetCursorBasedOnStyle:
1135 return overrideCursor;
1136 case DoNotSetCursor:
1137 return NoCursorChange;
1141 if (style && style->cursors()) {
1142 const CursorList* cursors = style->cursors();
1143 for (unsigned i = 0; i < cursors->size(); ++i) {
1144 StyleImage* styleImage = (*cursors)[i].image();
1147 ImageResource* cachedImage = styleImage->cachedImage();
1150 float scale = styleImage->imageScaleFactor();
1151 // Get hotspot and convert from logical pixels to physical pixels.
1152 IntPoint hotSpot = (*cursors)[i].hotSpot();
1153 hotSpot.scale(scale, scale);
1154 IntSize size = cachedImage->imageForRenderer(renderer)->size();
1155 if (cachedImage->errorOccurred())
1157 // Limit the size of cursors (in UI pixels) so that they cannot be
1158 // used to cover UI elements in chrome.
1159 size.scale(1 / scale);
1160 if (size.width() > maximumCursorSize || size.height() > maximumCursorSize)
1163 Image* image = cachedImage->imageForRenderer(renderer);
1164 // Ensure no overflow possible in calculations above.
1165 if (scale < minimumCursorScale)
1167 return Cursor(image, hotSpot, scale);
1171 switch (style ? style->cursor() : CURSOR_AUTO) {
1173 bool horizontalText = !style || style->isHorizontalWritingMode();
1174 const Cursor& iBeam = horizontalText ? iBeamCursor() : verticalTextCursor();
1175 return selectAutoCursor(result, node, iBeam, shiftKey);
1178 return crossCursor();
1179 case CURSOR_POINTER:
1180 return handCursor();
1182 return moveCursor();
1183 case CURSOR_ALL_SCROLL:
1184 return moveCursor();
1185 case CURSOR_E_RESIZE:
1186 return eastResizeCursor();
1187 case CURSOR_W_RESIZE:
1188 return westResizeCursor();
1189 case CURSOR_N_RESIZE:
1190 return northResizeCursor();
1191 case CURSOR_S_RESIZE:
1192 return southResizeCursor();
1193 case CURSOR_NE_RESIZE:
1194 return northEastResizeCursor();
1195 case CURSOR_SW_RESIZE:
1196 return southWestResizeCursor();
1197 case CURSOR_NW_RESIZE:
1198 return northWestResizeCursor();
1199 case CURSOR_SE_RESIZE:
1200 return southEastResizeCursor();
1201 case CURSOR_NS_RESIZE:
1202 return northSouthResizeCursor();
1203 case CURSOR_EW_RESIZE:
1204 return eastWestResizeCursor();
1205 case CURSOR_NESW_RESIZE:
1206 return northEastSouthWestResizeCursor();
1207 case CURSOR_NWSE_RESIZE:
1208 return northWestSouthEastResizeCursor();
1209 case CURSOR_COL_RESIZE:
1210 return columnResizeCursor();
1211 case CURSOR_ROW_RESIZE:
1212 return rowResizeCursor();
1214 return iBeamCursor();
1216 return waitCursor();
1218 return helpCursor();
1219 case CURSOR_VERTICAL_TEXT:
1220 return verticalTextCursor();
1222 return cellCursor();
1223 case CURSOR_CONTEXT_MENU:
1224 return contextMenuCursor();
1225 case CURSOR_PROGRESS:
1226 return progressCursor();
1227 case CURSOR_NO_DROP:
1228 return noDropCursor();
1230 return aliasCursor();
1232 return copyCursor();
1234 return noneCursor();
1235 case CURSOR_NOT_ALLOWED:
1236 return notAllowedCursor();
1237 case CURSOR_DEFAULT:
1238 return pointerCursor();
1239 case CURSOR_WEBKIT_ZOOM_IN:
1240 return zoomInCursor();
1241 case CURSOR_WEBKIT_ZOOM_OUT:
1242 return zoomOutCursor();
1243 case CURSOR_WEBKIT_GRAB:
1244 return grabCursor();
1245 case CURSOR_WEBKIT_GRABBING:
1246 return grabbingCursor();
1248 return pointerCursor();
1251 OptionalCursor EventHandler::selectAutoCursor(const HitTestResult& result, Node* node, const Cursor& iBeam, bool shiftKey)
1253 bool editable = (node && node->rendererIsEditable());
1255 if (useHandCursor(node, result.isOverLink(), shiftKey))
1256 return handCursor();
1258 bool inResizer = false;
1259 RenderObject* renderer = node ? node->renderer() : 0;
1261 if (RenderLayer* layer = renderer->enclosingLayer()) {
1262 if (m_frame->view())
1263 inResizer = layer->scrollableArea() && layer->scrollableArea()->isPointInResizeControl(result.roundedPointInMainFrame(), ResizerForPointer);
1267 // During selection, use an I-beam no matter what we're over.
1268 // If a drag may be starting or we're capturing mouse events for a particular node, don't treat this as a selection.
1269 if (m_mousePressed && m_mouseDownMayStartSelect
1270 && !m_mouseDownMayStartDrag
1271 && m_frame->selection().isCaretOrRange()
1272 && !m_capturingMouseEventsNode) {
1276 if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !result.scrollbar())
1278 return pointerCursor();
1281 static LayoutPoint documentPointForWindowPoint(Frame* frame, const IntPoint& windowPoint)
1283 FrameView* view = frame->view();
1284 // FIXME: Is it really OK to use the wrong coordinates here when view is 0?
1285 // Historically the code would just crash; this is clearly no worse than that.
1286 return view ? view->windowToContents(windowPoint) : windowPoint;
1289 bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent)
1291 RefPtr<FrameView> protector(m_frame->view());
1293 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent);
1294 if (defaultPrevented)
1297 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
1298 m_frame->tree().top()->eventHandler().m_lastMouseDownUserGestureToken = gestureIndicator.currentToken();
1300 cancelFakeMouseMoveEvent();
1301 if (m_eventHandlerWillResetCapturingMouseEventsNode)
1302 m_capturingMouseEventsNode = 0;
1303 m_mousePressed = true;
1304 m_capturesDragging = true;
1305 setLastKnownMousePosition(mouseEvent);
1306 m_mouseDownTimestamp = mouseEvent.timestamp();
1307 m_mouseDownMayStartDrag = false;
1308 m_mouseDownMayStartSelect = false;
1309 m_mouseDownMayStartAutoscroll = false;
1310 if (FrameView* view = m_frame->view())
1311 m_mouseDownPos = view->windowToContents(mouseEvent.position());
1316 m_mouseDownWasInSubframe = false;
1318 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent;
1319 if (mouseEvent.fromTouch())
1320 hitType |= HitTestRequest::ReadOnly;
1321 HitTestRequest request(hitType);
1322 // Save the document point we generate in case the window coordinate is invalidated by what happens
1323 // when we dispatch the event.
1324 LayoutPoint documentPoint = documentPointForWindowPoint(m_frame, mouseEvent.position());
1325 MouseEventWithHitTestResults mev = m_frame->document()->prepareMouseEvent(request, documentPoint, mouseEvent);
1327 if (!mev.targetNode()) {
1332 m_mousePressNode = mev.targetNode();
1334 RefPtr<Frame> subframe = subframeForHitTestResult(mev);
1335 if (subframe && passMousePressEventToSubframe(mev, subframe.get())) {
1336 // Start capturing future events for this frame. We only do this if we didn't clear
1337 // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop.
1338 m_capturesDragging = subframe->eventHandler().capturesDragging();
1339 if (m_mousePressed && m_capturesDragging) {
1340 m_capturingMouseEventsNode = mev.targetNode();
1341 m_eventHandlerWillResetCapturingMouseEventsNode = true;
1348 // We store whether pan scrolling is in progress before calling stopAutoscroll()
1349 // because it will set m_autoscrollType to NoAutoscroll on return.
1350 bool isPanScrollInProgress = panScrollInProgress();
1352 if (isPanScrollInProgress) {
1353 // We invalidate the click when exiting pan scrolling so that we don't inadvertently navigate
1354 // away from the current page (e.g. the click was on a hyperlink). See <rdar://problem/6095023>.
1360 m_clickCount = mouseEvent.clickCount();
1361 m_clickNode = mev.targetNode()->isTextNode() ? mev.targetNode()->parentOrShadowHostNode() : mev.targetNode();
1363 if (FrameView* view = m_frame->view()) {
1364 RenderLayer* layer = mev.targetNode()->renderer() ? mev.targetNode()->renderer()->enclosingLayer() : 0;
1365 IntPoint p = view->windowToContents(mouseEvent.position());
1366 if (layer && layer->scrollableArea() && layer->scrollableArea()->isPointInResizeControl(p, ResizerForPointer)) {
1367 m_resizeScrollableArea = layer->scrollableArea();
1368 m_resizeScrollableArea->setInResizeMode(true);
1369 m_offsetFromResizeCorner = m_resizeScrollableArea->offsetFromResizeCorner(p);
1375 m_frame->selection().setCaretBlinkingSuspended(true);
1377 bool swallowEvent = !dispatchMouseEvent(EventTypeNames::mousedown, mev.targetNode(), true, m_clickCount, mouseEvent, true);
1378 m_capturesDragging = !swallowEvent || mev.scrollbar();
1380 // If the hit testing originally determined the event was in a scrollbar, refetch the MouseEventWithHitTestResults
1381 // in case the scrollbar widget was destroyed when the mouse event was handled.
1382 if (mev.scrollbar()) {
1383 const bool wasLastScrollBar = mev.scrollbar() == m_lastScrollbarUnderMouse.get();
1384 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
1385 mev = m_frame->document()->prepareMouseEvent(request, documentPoint, mouseEvent);
1386 if (wasLastScrollBar && mev.scrollbar() != m_lastScrollbarUnderMouse.get())
1387 m_lastScrollbarUnderMouse = 0;
1391 // scrollbars should get events anyway, even disabled controls might be scrollable
1392 Scrollbar* scrollbar = mev.scrollbar();
1394 updateLastScrollbarUnderMouse(scrollbar, true);
1397 passMousePressEventToScrollbar(mev, scrollbar);
1399 if (shouldRefetchEventTarget(mev)) {
1400 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
1401 mev = m_frame->document()->prepareMouseEvent(request, documentPoint, mouseEvent);
1404 FrameView* view = m_frame->view();
1405 Scrollbar* scrollbar = view ? view->scrollbarAtPoint(mouseEvent.position()) : 0;
1407 scrollbar = mev.scrollbar();
1409 updateLastScrollbarUnderMouse(scrollbar, true);
1411 if (scrollbar && passMousePressEventToScrollbar(mev, scrollbar))
1412 swallowEvent = true;
1414 swallowEvent = handleMousePressEvent(mev);
1417 return swallowEvent;
1420 static RenderLayer* layerForNode(Node* node)
1425 RenderObject* renderer = node->renderer();
1429 RenderLayer* layer = renderer->enclosingLayer();
1436 ScrollableArea* EventHandler::associatedScrollableArea(const RenderLayer* layer) const
1438 ScrollableArea* layerScrollableArea = layer->scrollableArea();
1439 if (!layerScrollableArea)
1442 if (FrameView* frameView = m_frame->view()) {
1443 if (frameView->containsScrollableArea(layerScrollableArea))
1444 return layerScrollableArea;
1450 bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& event)
1452 RefPtr<FrameView> protector(m_frame->view());
1453 MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration);
1455 HitTestResult hoveredNode = HitTestResult(LayoutPoint());
1456 bool result = handleMouseMoveOrLeaveEvent(event, &hoveredNode);
1458 Page* page = m_frame->page();
1462 if (RenderLayer* layer = layerForNode(hoveredNode.innerNode())) {
1463 if (ScrollableArea* layerScrollableArea = associatedScrollableArea(layer))
1464 layerScrollableArea->mouseMovedInContentArea();
1467 if (FrameView* frameView = m_frame->view())
1468 frameView->mouseMovedInContentArea();
1470 hoveredNode.setToShadowHostIfInUserAgentShadowRoot();
1471 page->chrome().mouseDidMoveOverElement(hoveredNode, event.modifierFlags());
1472 page->chrome().setToolTip(hoveredNode);
1477 void EventHandler::handleMouseLeaveEvent(const PlatformMouseEvent& event)
1479 RefPtr<FrameView> protector(m_frame->view());
1480 handleMouseMoveOrLeaveEvent(event);
1483 static Cursor& syntheticTouchCursor()
1485 DEFINE_STATIC_LOCAL(Cursor, c, (Image::loadPlatformResource("syntheticTouchCursor").get(), IntPoint(10, 10)));
1489 bool EventHandler::handleMouseMoveOrLeaveEvent(const PlatformMouseEvent& mouseEvent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars)
1492 ASSERT(m_frame->view());
1494 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent);
1495 if (defaultPrevented) {
1496 m_frame->view()->setCursor(syntheticTouchCursor());
1500 setLastKnownMousePosition(mouseEvent);
1502 if (m_hoverTimer.isActive())
1503 m_hoverTimer.stop();
1505 m_cursorUpdateTimer.stop();
1507 cancelFakeMouseMoveEvent();
1510 toSVGDocument(m_frame->document())->updatePan(m_frame->view()->windowToContents(m_lastKnownMousePosition));
1514 if (m_frameSetBeingResized)
1515 return !dispatchMouseEvent(EventTypeNames::mousemove, m_frameSetBeingResized.get(), false, 0, mouseEvent, false);
1517 // Send events right to a scrollbar if the mouse is pressed.
1518 if (m_lastScrollbarUnderMouse && m_mousePressed) {
1519 m_lastScrollbarUnderMouse->mouseMoved(mouseEvent);
1523 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent;
1524 if (mouseEvent.fromTouch())
1525 hitType |= HitTestRequest::ReadOnly;
1528 hitType |= HitTestRequest::Active;
1529 else if (onlyUpdateScrollbars) {
1530 // Mouse events should be treated as "read-only" if we're updating only scrollbars. This
1531 // means that :hover and :active freeze in the state they were in, rather than updating
1532 // for nodes the mouse moves while the window is not key (which will be the case if
1533 // onlyUpdateScrollbars is true).
1534 hitType |= HitTestRequest::ReadOnly;
1537 // Treat any mouse move events as readonly if the user is currently touching the screen.
1539 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
1540 HitTestRequest request(hitType);
1541 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent);
1543 *hoveredNode = mev.hitTestResult();
1545 Scrollbar* scrollbar = 0;
1547 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode())
1548 m_resizeScrollableArea->resize(mouseEvent, m_offsetFromResizeCorner);
1550 if (FrameView* view = m_frame->view())
1551 scrollbar = view->scrollbarAtPoint(mouseEvent.position());
1554 scrollbar = mev.scrollbar();
1556 updateLastScrollbarUnderMouse(scrollbar, !m_mousePressed);
1557 if (onlyUpdateScrollbars)
1561 bool swallowEvent = false;
1562 RefPtr<Frame> newSubframe = m_capturingMouseEventsNode.get() ? subframeForTargetNode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev);
1564 // We want mouseouts to happen first, from the inside out. First send a move event to the last subframe so that it will fire mouseouts.
1565 if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree().isDescendantOf(m_frame) && m_lastMouseMoveEventSubframe != newSubframe)
1566 passMouseMoveEventToSubframe(mev, m_lastMouseMoveEventSubframe.get());
1569 // Update over/out state before passing the event to the subframe.
1570 updateMouseEventTargetNode(mev.targetNode(), mouseEvent, true);
1572 // Event dispatch in updateMouseEventTargetNode may have caused the subframe of the target
1573 // node to be detached from its FrameView, in which case the event should not be passed.
1574 if (newSubframe->view())
1575 swallowEvent |= passMouseMoveEventToSubframe(mev, newSubframe.get(), hoveredNode);
1577 if (scrollbar && !m_mousePressed)
1578 scrollbar->mouseMoved(mouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering.
1579 if (FrameView* view = m_frame->view()) {
1580 OptionalCursor optionalCursor = selectCursor(mev.hitTestResult(), mouseEvent.shiftKey());
1581 if (optionalCursor.isCursorChange()) {
1582 m_currentMouseCursor = optionalCursor.cursor();
1583 view->setCursor(m_currentMouseCursor);
1588 m_lastMouseMoveEventSubframe = newSubframe;
1593 swallowEvent = !dispatchMouseEvent(EventTypeNames::mousemove, mev.targetNode(), false, 0, mouseEvent, true);
1595 swallowEvent = handleMouseDraggedEvent(mev);
1597 return swallowEvent;
1600 void EventHandler::invalidateClick()
1606 static Node* parentForClickEvent(const Node& node)
1608 // IE doesn't dispatch click events for mousedown/mouseup events across form
1610 if (node.isHTMLElement() && toHTMLElement(node).isInteractiveContent())
1612 return node.parentOrShadowHostNode();
1615 bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent)
1617 RefPtr<FrameView> protector(m_frame->view());
1619 m_frame->selection().setCaretBlinkingSuspended(false);
1621 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent);
1622 if (defaultPrevented)
1625 OwnPtr<UserGestureIndicator> gestureIndicator;
1627 if (m_frame->tree().top()->eventHandler().m_lastMouseDownUserGestureToken)
1628 gestureIndicator = adoptPtr(new UserGestureIndicator(m_frame->tree().top()->eventHandler().m_lastMouseDownUserGestureToken.release()));
1630 gestureIndicator = adoptPtr(new UserGestureIndicator(DefinitelyProcessingUserGesture));
1633 if (Page* page = m_frame->page())
1634 page->autoscrollController().handleMouseReleaseForPanScrolling(m_frame, mouseEvent);
1637 m_mousePressed = false;
1638 setLastKnownMousePosition(mouseEvent);
1642 toSVGDocument(m_frame->document())->updatePan(m_frame->view()->windowToContents(m_lastKnownMousePosition));
1646 if (m_frameSetBeingResized)
1647 return !dispatchMouseEvent(EventTypeNames::mouseup, m_frameSetBeingResized.get(), true, m_clickCount, mouseEvent, false);
1649 if (m_lastScrollbarUnderMouse) {
1651 m_lastScrollbarUnderMouse->mouseUp(mouseEvent);
1652 bool cancelable = true;
1653 bool setUnder = false;
1654 return !dispatchMouseEvent(EventTypeNames::mouseup, m_lastNodeUnderMouse.get(), cancelable, m_clickCount, mouseEvent, setUnder);
1657 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Release | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent;
1658 if (mouseEvent.fromTouch())
1659 hitType |= HitTestRequest::ReadOnly;
1660 HitTestRequest request(hitType);
1661 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent);
1662 Frame* subframe = m_capturingMouseEventsNode.get() ? subframeForTargetNode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev);
1663 if (m_eventHandlerWillResetCapturingMouseEventsNode)
1664 m_capturingMouseEventsNode = 0;
1665 if (subframe && passMouseReleaseEventToSubframe(mev, subframe))
1668 bool swallowMouseUpEvent = !dispatchMouseEvent(EventTypeNames::mouseup, mev.targetNode(), true, m_clickCount, mouseEvent, false);
1670 bool contextMenuEvent = mouseEvent.button() == RightButton;
1672 // FIXME: The Mac port achieves the same behavior by checking whether the context menu is currently open in WebPage::mouseEvent(). Consider merging the implementations.
1673 if (mouseEvent.button() == LeftButton && mouseEvent.modifiers() & PlatformEvent::CtrlKey)
1674 contextMenuEvent = true;
1677 bool swallowClickEvent = false;
1678 if (m_clickCount > 0 && !contextMenuEvent && mev.targetNode() && m_clickNode) {
1679 if (Node* clickTargetNode = mev.targetNode()->commonAncestor(*m_clickNode, parentForClickEvent))
1680 swallowClickEvent = !dispatchMouseEvent(EventTypeNames::click, clickTargetNode, true, m_clickCount, mouseEvent, true);
1683 if (m_resizeScrollableArea) {
1684 m_resizeScrollableArea->setInResizeMode(false);
1685 m_resizeScrollableArea = 0;
1688 bool swallowMouseReleaseEvent = false;
1689 if (!swallowMouseUpEvent)
1690 swallowMouseReleaseEvent = handleMouseReleaseEvent(mev);
1694 return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
1697 bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& mouseEvent)
1699 // If the event was a middle click, attempt to copy global selection in after
1700 // the newly set caret position.
1702 // This code is called from either the mouse up or mouse down handling. There
1703 // is some debate about when the global selection is pasted:
1704 // xterm: pastes on up.
1705 // GTK: pastes on down.
1706 // Qt: pastes on up.
1707 // Firefox: pastes on up.
1708 // Chromium: pastes on up.
1710 // There is something of a webcompat angle to this well, as highlighted by
1711 // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on
1712 // down then the text is pasted just before the onclick handler runs and
1713 // clears the text box. So it's important this happens after the event
1714 // handlers have been fired.
1715 if (mouseEvent.type() != PlatformEvent::MouseReleased)
1718 if (!m_frame->page())
1720 Frame* focusFrame = m_frame->page()->focusController().focusedOrMainFrame();
1721 // Do not paste here if the focus was moved somewhere else.
1722 if (m_frame == focusFrame && m_frame->editor().behavior().supportsGlobalSelection())
1723 return m_frame->editor().command("PasteGlobalSelection").execute();
1729 bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Node* dragTarget, const PlatformMouseEvent& event, Clipboard* clipboard)
1731 FrameView* view = m_frame->view();
1733 // FIXME: We might want to dispatch a dragleave even if the view is gone.
1737 RefPtr<MouseEvent> me = MouseEvent::create(eventType,
1738 true, true, m_frame->document()->domWindow(),
1739 0, event.globalPosition().x(), event.globalPosition().y(), event.position().x(), event.position().y(),
1740 event.movementDelta().x(), event.movementDelta().y(),
1741 event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(),
1744 dragTarget->dispatchEvent(me.get(), IGNORE_EXCEPTION);
1745 return me->defaultPrevented();
1748 static bool targetIsFrame(Node* target, Frame*& frame)
1753 if (!target->hasTagName(frameTag) && !target->hasTagName(iframeTag))
1756 frame = toHTMLFrameElementBase(target)->contentFrame();
1760 static bool findDropZone(Node* target, Clipboard* clipboard)
1762 Element* element = target->isElementNode() ? toElement(target) : target->parentElement();
1763 for (; element; element = element->parentElement()) {
1764 bool matched = false;
1765 AtomicString dropZoneStr = element->fastGetAttribute(webkitdropzoneAttr);
1767 if (dropZoneStr.isEmpty())
1770 dropZoneStr = dropZoneStr.lower();
1772 SpaceSplitString keywords(dropZoneStr, false);
1773 if (keywords.isNull())
1776 DragOperation dragOperation = DragOperationNone;
1777 for (unsigned int i = 0; i < keywords.size(); i++) {
1778 DragOperation op = convertDropZoneOperationToDragOperation(keywords[i]);
1779 if (op != DragOperationNone) {
1780 if (dragOperation == DragOperationNone)
1783 matched = matched || clipboard->hasDropZoneType(keywords[i].string());
1785 if (matched && dragOperation != DragOperationNone)
1789 clipboard->setDropEffect(convertDragOperationToDropZoneOperation(dragOperation));
1796 bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard)
1798 bool accept = false;
1800 if (!m_frame->view())
1803 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
1804 MouseEventWithHitTestResults mev = prepareMouseEvent(request, event);
1806 // Drag events should never go to text nodes (following IE, and proper mouseover/out dispatch)
1807 RefPtr<Node> newTarget = mev.targetNode();
1808 if (newTarget && newTarget->isTextNode())
1809 newTarget = EventPath::parent(newTarget.get());
1811 if (AutoscrollController* controller = autoscrollController())
1812 controller->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp());
1814 if (m_dragTarget != newTarget) {
1815 // FIXME: this ordering was explicitly chosen to match WinIE. However,
1816 // it is sometimes incorrect when dragging within subframes, as seen with
1817 // LayoutTests/fast/events/drag-in-frames.html.
1819 // Moreover, this ordering conforms to section 7.9.4 of the HTML 5 spec. <http://dev.w3.org/html5/spec/Overview.html#drag-and-drop-processing-model>.
1821 if (targetIsFrame(newTarget.get(), targetFrame)) {
1823 accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard);
1824 } else if (newTarget) {
1825 // As per section 7.9.4 of the HTML 5 spec., we must always fire a drag event before firing a dragenter, dragleave, or dragover event.
1826 if (dragState().m_dragSrc) {
1827 // for now we don't care if event handler cancels default behavior, since there is none
1828 dispatchDragSrcEvent(EventTypeNames::drag, event);
1830 accept = dispatchDragEvent(EventTypeNames::dragenter, newTarget.get(), event, clipboard);
1832 accept = findDropZone(newTarget.get(), clipboard);
1835 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
1837 accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard);
1838 } else if (m_dragTarget)
1839 dispatchDragEvent(EventTypeNames::dragleave, m_dragTarget.get(), event, clipboard);
1842 // We do not explicitly call dispatchDragEvent here because it could ultimately result in the appearance that
1843 // two dragover events fired. So, we mark that we should only fire a dragover event on the next call to this function.
1844 m_shouldOnlyFireDragOverEvent = true;
1848 if (targetIsFrame(newTarget.get(), targetFrame)) {
1850 accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard);
1851 } else if (newTarget) {
1852 // Note, when dealing with sub-frames, we may need to fire only a dragover event as a drag event may have been fired earlier.
1853 if (!m_shouldOnlyFireDragOverEvent && dragState().m_dragSrc) {
1854 // for now we don't care if event handler cancels default behavior, since there is none
1855 dispatchDragSrcEvent(EventTypeNames::drag, event);
1857 accept = dispatchDragEvent(EventTypeNames::dragover, newTarget.get(), event, clipboard);
1859 accept = findDropZone(newTarget.get(), clipboard);
1860 m_shouldOnlyFireDragOverEvent = false;
1863 m_dragTarget = newTarget;
1868 void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard)
1871 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
1873 targetFrame->eventHandler().cancelDragAndDrop(event, clipboard);
1874 } else if (m_dragTarget.get()) {
1875 if (dragState().m_dragSrc)
1876 dispatchDragSrcEvent(EventTypeNames::drag, event);
1877 dispatchDragEvent(EventTypeNames::dragleave, m_dragTarget.get(), event, clipboard);
1882 bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard)
1885 bool preventedDefault = false;
1886 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
1888 preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, clipboard);
1889 } else if (m_dragTarget.get())
1890 preventedDefault = dispatchDragEvent(EventTypeNames::drop, m_dragTarget.get(), event, clipboard);
1892 return preventedDefault;
1895 void EventHandler::clearDragState()
1899 m_capturingMouseEventsNode = 0;
1900 m_shouldOnlyFireDragOverEvent = false;
1903 void EventHandler::setCapturingMouseEventsNode(PassRefPtr<Node> n)
1905 m_capturingMouseEventsNode = n;
1906 m_eventHandlerWillResetCapturingMouseEventsNode = false;
1909 MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mev)
1912 ASSERT(m_frame->document());
1914 return m_frame->document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mev.position()), mev);
1917 static inline SVGElementInstance* instanceAssociatedWithShadowTreeElement(Node* referenceNode)
1919 if (!referenceNode || !referenceNode->isSVGElement())
1922 ShadowRoot* shadowRoot = referenceNode->containingShadowRoot();
1926 Element* shadowTreeParentElement = shadowRoot->host();
1927 if (!shadowTreeParentElement || !shadowTreeParentElement->hasTagName(useTag))
1930 return toSVGUseElement(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode);
1933 void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& mouseEvent, bool fireMouseOverOut)
1935 Node* result = targetNode;
1937 // If we're capturing, we always go right to that node.
1938 if (m_capturingMouseEventsNode)
1939 result = m_capturingMouseEventsNode.get();
1941 // If the target node is a text node, dispatch on the parent node - rdar://4196646
1942 if (result && result->isTextNode())
1943 result = EventPath::parent(result);
1945 m_nodeUnderMouse = result;
1946 m_instanceUnderMouse = instanceAssociatedWithShadowTreeElement(result);
1948 // <use> shadow tree elements may have been recloned, update node under mouse in any case
1949 if (m_lastInstanceUnderMouse) {
1950 SVGElement* lastCorrespondingElement = m_lastInstanceUnderMouse->correspondingElement();
1951 SVGElement* lastCorrespondingUseElement = m_lastInstanceUnderMouse->correspondingUseElement();
1953 if (lastCorrespondingElement && lastCorrespondingUseElement) {
1954 HashSet<SVGElementInstance*> instances = lastCorrespondingElement->instancesForElement();
1956 // Locate the recloned shadow tree element for our corresponding instance
1957 HashSet<SVGElementInstance*>::iterator end = instances.end();
1958 for (HashSet<SVGElementInstance*>::iterator it = instances.begin(); it != end; ++it) {
1959 SVGElementInstance* instance = (*it);
1960 ASSERT(instance->correspondingElement() == lastCorrespondingElement);
1962 if (instance == m_lastInstanceUnderMouse)
1965 if (instance->correspondingUseElement() != lastCorrespondingUseElement)
1968 SVGElement* shadowTreeElement = instance->shadowTreeElement();
1969 if (!shadowTreeElement->inDocument() || m_lastNodeUnderMouse == shadowTreeElement)
1972 m_lastNodeUnderMouse = shadowTreeElement;
1973 m_lastInstanceUnderMouse = instance;
1979 // Fire mouseout/mouseover if the mouse has shifted to a different node.
1980 if (fireMouseOverOut) {
1981 RenderLayer* layerForLastNode = layerForNode(m_lastNodeUnderMouse.get());
1982 RenderLayer* layerForNodeUnderMouse = layerForNode(m_nodeUnderMouse.get());
1983 Page* page = m_frame->page();
1985 if (m_lastNodeUnderMouse && (!m_nodeUnderMouse || m_nodeUnderMouse->document() != m_frame->document())) {
1986 // The mouse has moved between frames.
1987 if (Frame* frame = m_lastNodeUnderMouse->document().frame()) {
1988 if (FrameView* frameView = frame->view())
1989 frameView->mouseExitedContentArea();
1991 } else if (page && (layerForLastNode && (!layerForNodeUnderMouse || layerForNodeUnderMouse != layerForLastNode))) {
1992 // The mouse has moved between layers.
1993 if (ScrollableArea* scrollableAreaForLastNode = associatedScrollableArea(layerForLastNode))
1994 scrollableAreaForLastNode->mouseExitedContentArea();
1997 if (m_nodeUnderMouse && (!m_lastNodeUnderMouse || m_lastNodeUnderMouse->document() != m_frame->document())) {
1998 // The mouse has moved between frames.
1999 if (Frame* frame = m_nodeUnderMouse->document().frame()) {
2000 if (FrameView* frameView = frame->view())
2001 frameView->mouseEnteredContentArea();
2003 } else if (page && (layerForNodeUnderMouse && (!layerForLastNode || layerForNodeUnderMouse != layerForLastNode))) {
2004 // The mouse has moved between layers.
2005 if (ScrollableArea* scrollableAreaForNodeUnderMouse = associatedScrollableArea(layerForNodeUnderMouse))
2006 scrollableAreaForNodeUnderMouse->mouseEnteredContentArea();
2009 if (m_lastNodeUnderMouse && m_lastNodeUnderMouse->document() != m_frame->document()) {
2010 m_lastNodeUnderMouse = 0;
2011 m_lastScrollbarUnderMouse = 0;
2012 m_lastInstanceUnderMouse = 0;
2015 if (m_lastNodeUnderMouse != m_nodeUnderMouse) {
2016 // send mouseout event to the old node
2017 if (m_lastNodeUnderMouse)
2018 m_lastNodeUnderMouse->dispatchMouseEvent(mouseEvent, EventTypeNames::mouseout, 0, m_nodeUnderMouse.get());
2019 // send mouseover event to the new node
2020 if (m_nodeUnderMouse)
2021 m_nodeUnderMouse->dispatchMouseEvent(mouseEvent, EventTypeNames::mouseover, 0, m_lastNodeUnderMouse.get());
2023 m_lastNodeUnderMouse = m_nodeUnderMouse;
2024 m_lastInstanceUnderMouse = instanceAssociatedWithShadowTreeElement(m_nodeUnderMouse.get());
2028 bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& mouseEvent, bool setUnder)
2030 updateMouseEventTargetNode(targetNode, mouseEvent, setUnder);
2032 bool swallowEvent = false;
2034 if (m_nodeUnderMouse)
2035 swallowEvent = !(m_nodeUnderMouse->dispatchMouseEvent(mouseEvent, eventType, clickCount));
2037 if (swallowEvent || eventType != EventTypeNames::mousedown)
2038 return !swallowEvent;
2040 // If clicking on a frame scrollbar, do not mess up with content focus.
2041 if (FrameView* view = m_frame->view()) {
2042 if (view->scrollbarAtPoint(mouseEvent.position()))
2046 // The layout needs to be up to date to determine if an element is focusable.
2047 m_frame->document()->updateLayoutIgnorePendingStylesheets();
2049 Element* element = 0;
2050 if (m_nodeUnderMouse)
2051 element = m_nodeUnderMouse->isElementNode() ? toElement(m_nodeUnderMouse) : m_nodeUnderMouse->parentOrShadowHostElement();
2052 for (; element; element = element->parentOrShadowHostElement()) {
2053 if (element->isFocusable() && element->focused())
2054 return !swallowEvent;
2055 if (element->isMouseFocusable())
2058 ASSERT(!element || element->isMouseFocusable());
2060 // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus
2061 // a node on mouse down if it's selected and inside a focused node. It will
2062 // be focused if the user does a mouseup over it, however, because the
2063 // mouseup will set a selection inside it, which will call
2064 // FrameSelection::setFocusedNodeIfNeeded.
2066 && m_frame->selection().isRange()
2067 && m_frame->selection().toNormalizedRange()->compareNode(element, IGNORE_EXCEPTION) == Range::NODE_INSIDE
2068 && element->isDescendantOf(m_frame->document()->focusedElement()))
2071 // Only change the focus when clicking scrollbars if it can transfered to a
2072 // mouse focusable node.
2073 if (!element && isInsideScrollbar(mouseEvent.position()))
2076 if (Page* page = m_frame->page()) {
2077 // If focus shift is blocked, we eat the event. Note we should never
2078 // clear swallowEvent if the page already set it (e.g., by canceling
2079 // default behavior).
2081 if (!page->focusController().setFocusedElement(element, m_frame, FocusDirectionMouse))
2082 swallowEvent = true;
2084 // We call setFocusedElement even with !element in order to blur
2085 // current focus element when a link is clicked; this is expected by
2086 // some sites that rely on onChange handlers running from form
2087 // fields before the button click is processed.
2088 if (!page->focusController().setFocusedElement(0, m_frame))
2089 swallowEvent = true;
2093 return !swallowEvent;
2096 bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const
2098 if (RenderView* renderView = m_frame->contentRenderer()) {
2099 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
2100 HitTestResult result(windowPoint);
2101 renderView->hitTest(request, result);
2102 return result.scrollbar();
2108 bool EventHandler::shouldTurnVerticalTicksIntoHorizontal(const HitTestResult& result, const PlatformWheelEvent& event) const
2110 #if OS(ANDROID) || OS(MACOSX) || OS(WIN)
2113 // GTK+ must scroll horizontally if the mouse pointer is on top of the
2114 // horizontal scrollbar while scrolling with the wheel.
2115 // This code comes from gtk/EventHandlerGtk.cpp.
2116 return !event.hasPreciseScrollingDeltas() && result.scrollbar() && result.scrollbar()->orientation() == HorizontalScrollbar;
2120 bool EventHandler::handleWheelEvent(const PlatformWheelEvent& e)
2122 #define RETURN_WHEEL_EVENT_HANDLED() \
2124 setFrameWasScrolledByUser(); \
2128 Document* doc = m_frame->document();
2130 if (!doc->renderer())
2133 RefPtr<FrameView> protector(m_frame->view());
2135 FrameView* view = m_frame->view();
2139 if (handleWheelEventAsEmulatedGesture(e))
2142 LayoutPoint vPoint = view->windowToContents(e.position());
2144 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
2145 HitTestResult result(vPoint);
2146 doc->renderView()->hitTest(request, result);
2148 Node* node = result.innerNode();
2149 // Wheel events should not dispatch to text nodes.
2150 if (node && node->isTextNode())
2151 node = EventPath::parent(node);
2154 if (e.useLatchedEventNode()) {
2155 if (!m_latchedWheelEventNode) {
2156 m_latchedWheelEventNode = node;
2157 m_widgetIsLatched = result.isOverWidget();
2159 node = m_latchedWheelEventNode.get();
2161 isOverWidget = m_widgetIsLatched;
2163 if (m_latchedWheelEventNode)
2164 m_latchedWheelEventNode = 0;
2165 if (m_previousWheelScrolledNode)
2166 m_previousWheelScrolledNode = 0;
2168 isOverWidget = result.isOverWidget();
2171 // FIXME: It should not be necessary to do this mutation here.
2172 // Instead, the handlers should know convert vertical scrolls
2174 PlatformWheelEvent event = e;
2175 if (m_baseEventType == PlatformEvent::NoType && shouldTurnVerticalTicksIntoHorizontal(result, e))
2176 event = event.copyTurningVerticalTicksIntoHorizontalTicks();
2179 // Figure out which view to send the event to.
2180 RenderObject* target = node->renderer();
2182 if (isOverWidget && target && target->isWidget()) {
2183 Widget* widget = toRenderWidget(target)->widget();
2184 if (widget && passWheelEventToWidget(e, widget))
2185 RETURN_WHEEL_EVENT_HANDLED();
2188 if (node && !node->dispatchWheelEvent(event))
2189 RETURN_WHEEL_EVENT_HANDLED();
2193 // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed.
2194 view = m_frame->view();
2195 if (!view || !view->wheelEvent(event))
2198 RETURN_WHEEL_EVENT_HANDLED();
2200 #undef RETURN_WHEEL_EVENT_HANDLED
2203 void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEvent)
2205 if (!startNode || !wheelEvent)
2208 Node* stopNode = m_previousWheelScrolledNode.get();
2209 ScrollGranularity granularity = wheelGranularityToScrollGranularity(wheelEvent->deltaMode());
2211 // Break up into two scrolls if we need to. Diagonal movement on
2212 // a MacBook pro is an example of a 2-dimensional mouse wheel event (where both deltaX and deltaY can be set).
2213 if (scroll(ScrollRight, granularity, startNode, &stopNode, wheelEvent->deltaX(), roundedIntPoint(wheelEvent->absoluteLocation())))
2214 wheelEvent->setDefaultHandled();
2216 if (scroll(ScrollDown, granularity, startNode, &stopNode, wheelEvent->deltaY(), roundedIntPoint(wheelEvent->absoluteLocation())))
2217 wheelEvent->setDefaultHandled();
2219 if (!m_latchedWheelEventNode)
2220 m_previousWheelScrolledNode = stopNode;
2223 bool EventHandler::handleGestureShowPress()
2225 m_lastShowPressTimestamp = WTF::currentTime();
2227 FrameView* view = m_frame->view();
2230 if (ScrollAnimator* scrollAnimator = view->existingScrollAnimator())
2231 scrollAnimator->cancelAnimations();
2232 const FrameView::ScrollableAreaSet* areas = view->scrollableAreas();
2235 for (FrameView::ScrollableAreaSet::const_iterator it = areas->begin(); it != areas->end(); ++it) {
2236 ScrollableArea* sa = *it;
2237 ScrollAnimator* animator = sa->scrollAnimator();
2239 animator->cancelAnimations();
2244 bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
2246 IntPoint adjustedPoint = gestureEvent.position();
2247 RefPtr<Frame> subframe = 0;
2248 switch (gestureEvent.type()) {
2249 case PlatformEvent::GestureScrollBegin:
2250 case PlatformEvent::GestureScrollUpdate:
2251 case PlatformEvent::GestureScrollUpdateWithoutPropagation:
2252 case PlatformEvent::GestureScrollEnd:
2253 case PlatformEvent::GestureFlingStart:
2254 // Handle directly in main frame
2257 case PlatformEvent::GestureTap:
2258 case PlatformEvent::GestureTapUnconfirmed:
2259 case PlatformEvent::GestureTapDown:
2260 case PlatformEvent::GestureShowPress:
2261 case PlatformEvent::GestureTapDownCancel:
2262 case PlatformEvent::GestureTwoFingerTap:
2263 case PlatformEvent::GestureLongPress:
2264 case PlatformEvent::GestureLongTap:
2265 case PlatformEvent::GesturePinchBegin:
2266 case PlatformEvent::GesturePinchEnd:
2267 case PlatformEvent::GesturePinchUpdate:
2268 adjustGesturePosition(gestureEvent, adjustedPoint);
2269 subframe = getSubFrameForGestureEvent(adjustedPoint, gestureEvent);
2271 return subframe->eventHandler().handleGestureEvent(gestureEvent);
2275 ASSERT_NOT_REACHED();
2278 Node* eventTarget = 0;
2279 Scrollbar* scrollbar = 0;
2280 if (gestureEvent.type() == PlatformEvent::GestureScrollEnd
2281 || gestureEvent.type() == PlatformEvent::GestureScrollUpdate
2282 || gestureEvent.type() == PlatformEvent::GestureScrollUpdateWithoutPropagation
2283 || gestureEvent.type() == PlatformEvent::GestureFlingStart) {
2284 scrollbar = m_scrollbarHandlingScrollGesture.get();
2285 eventTarget = m_scrollGestureHandlingNode.get();
2288 HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent;
2289 double activeInterval = 0;
2290 bool shouldKeepActiveForMinInterval = false;
2291 if (gestureEvent.type() == PlatformEvent::GestureShowPress
2292 || gestureEvent.type() == PlatformEvent::GestureTapUnconfirmed) {
2293 hitType |= HitTestRequest::Active;
2294 } else if (gestureEvent.type() == PlatformEvent::GestureTapDownCancel) {
2295 hitType |= HitTestRequest::Release;
2296 // A TapDownCancel received when no element is active shouldn't really be changing hover state.
2297 if (!m_frame->document()->activeHoverElement())
2298 hitType |= HitTestRequest::ReadOnly;
2299 } else if (gestureEvent.type() == PlatformEvent::GestureTap) {
2300 hitType |= HitTestRequest::Release;
2301 // If the Tap is received very shortly after ShowPress, we want to delay clearing
2302 // of the active state so that it's visible to the user for at least one frame.
2303 activeInterval = WTF::currentTime() - m_lastShowPressTimestamp;
2304 shouldKeepActiveForMinInterval = m_lastShowPressTimestamp && activeInterval < minimumActiveInterval;
2305 if (shouldKeepActiveForMinInterval)
2306 hitType |= HitTestRequest::ReadOnly;
2309 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
2311 if ((!scrollbar && !eventTarget) || !(hitType & HitTestRequest::ReadOnly)) {
2312 IntPoint hitTestPoint = m_frame->view()->windowToContents(adjustedPoint);
2313 HitTestResult result = hitTestResultAtPoint(hitTestPoint, hitType | HitTestRequest::AllowFrameScrollbars);
2315 if (shouldKeepActiveForMinInterval) {
2316 m_lastDeferredTapElement = result.innerElement();
2317 m_activeIntervalTimer.startOneShot(minimumActiveInterval - activeInterval);
2320 eventTarget = result.targetNode();
2322 FrameView* view = m_frame->view();
2323 scrollbar = view ? view->scrollbarAtPoint(gestureEvent.position()) : 0;
2326 scrollbar = result.scrollbar();
2330 bool eventSwallowed = scrollbar->gestureEvent(gestureEvent);
2331 if (gestureEvent.type() == PlatformEvent::GestureTapDown && eventSwallowed) {
2332 m_scrollbarHandlingScrollGesture = scrollbar;
2333 } else if (gestureEvent.type() == PlatformEvent::GestureScrollEnd
2334 || gestureEvent.type() == PlatformEvent::GestureFlingStart
2335 || !eventSwallowed) {
2336 m_scrollbarHandlingScrollGesture = 0;
2344 bool eventSwallowed = false;
2345 if (handleScrollGestureOnResizer(eventTarget, gestureEvent))
2346 eventSwallowed = true;
2348 eventSwallowed = eventTarget->dispatchGestureEvent(gestureEvent);
2349 if (gestureEvent.type() == PlatformEvent::GestureScrollBegin || gestureEvent.type() == PlatformEvent::GestureScrollEnd) {
2351 m_scrollGestureHandlingNode = eventTarget;
2358 // FIXME: A more general scroll system (https://bugs.webkit.org/show_bug.cgi?id=80596) will
2359 // eliminate the need for this.
2360 TemporaryChange<PlatformEvent::Type> baseEventType(m_baseEventType, gestureEvent.type());
2362 switch (gestureEvent.type()) {
2363 case PlatformEvent::GestureScrollBegin:
2364 return handleGestureScrollBegin(gestureEvent);
2365 case PlatformEvent::GestureScrollUpdate:
2366 case PlatformEvent::GestureScrollUpdateWithoutPropagation:
2367 return handleGestureScrollUpdate(gestureEvent);
2368 case PlatformEvent::GestureScrollEnd:
2369 return handleGestureScrollEnd(gestureEvent);
2370 case PlatformEvent::GestureTap:
2371 return handleGestureTap(gestureEvent, adjustedPoint);
2372 case PlatformEvent::GestureShowPress:
2373 return handleGestureShowPress();
2374 case PlatformEvent::GestureLongPress:
2375 return handleGestureLongPress(gestureEvent, adjustedPoint);
2376 case PlatformEvent::GestureLongTap:
2377 return handleGestureLongTap(gestureEvent, adjustedPoint);
2378 case PlatformEvent::GestureTwoFingerTap:
2379 return handleGestureTwoFingerTap(gestureEvent, adjustedPoint);
2380 case PlatformEvent::GestureTapDown:
2381 case PlatformEvent::GesturePinchBegin:
2382 case PlatformEvent::GesturePinchEnd:
2383 case PlatformEvent::GesturePinchUpdate:
2384 case PlatformEvent::GestureTapDownCancel:
2385 case PlatformEvent::GestureTapUnconfirmed:
2386 case PlatformEvent::GestureFlingStart:
2389 ASSERT_NOT_REACHED();
2395 bool EventHandler::handleGestureTap(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint)
2397 // 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.
2399 unsigned modifierFlags = 0;
2400 if (gestureEvent.altKey())
2401 modifierFlags |= PlatformEvent::AltKey;
2402 if (gestureEvent.ctrlKey())
2403 modifierFlags |= PlatformEvent::CtrlKey;
2404 if (gestureEvent.metaKey())
2405 modifierFlags |= PlatformEvent::MetaKey;
2406 if (gestureEvent.shiftKey())
2407 modifierFlags |= PlatformEvent::ShiftKey;
2408 PlatformEvent::Modifiers modifiers = static_cast<PlatformEvent::Modifiers>(modifierFlags);
2410 PlatformMouseEvent fakeMouseMove(adjustedPoint, gestureEvent.globalPosition(),
2411 NoButton, PlatformEvent::MouseMoved, /* clickCount */ 0,
2412 modifiers, PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
2413 handleMouseMoveEvent(fakeMouseMove);
2415 bool defaultPrevented = false;
2416 PlatformMouseEvent fakeMouseDown(adjustedPoint, gestureEvent.globalPosition(),
2417 LeftButton, PlatformEvent::MousePressed, gestureEvent.tapCount(),
2418 modifiers, PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
2419 defaultPrevented |= handleMousePressEvent(fakeMouseDown);
2421 PlatformMouseEvent fakeMouseUp(adjustedPoint, gestureEvent.globalPosition(),
2422 LeftButton, PlatformEvent::MouseReleased, gestureEvent.tapCount(),
2423 modifiers, PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
2424 defaultPrevented |= handleMouseReleaseEvent(fakeMouseUp);
2426 return defaultPrevented;
2429 bool EventHandler::handleGestureLongPress(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint)
2431 m_longTapShouldInvokeContextMenu = false;
2432 if (m_frame->settings() && m_frame->settings()->touchDragDropEnabled() && m_frame->view()) {
2433 PlatformMouseEvent mouseDownEvent(adjustedPoint, gestureEvent.globalPosition(), LeftButton, PlatformEvent::MousePressed, 1,
2434 gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), WTF::currentTime());
2435 m_mouseDown = mouseDownEvent;
2437 PlatformMouseEvent mouseDragEvent(adjustedPoint, gestureEvent.globalPosition(), LeftButton, PlatformEvent::MouseMoved, 1,
2438 gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), WTF::currentTime());
2439 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
2440 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseDragEvent);
2441 m_didStartDrag = false;
2442 m_mouseDownMayStartDrag = true;
2443 dragState().m_dragSrc = 0;
2444 m_mouseDownPos = m_frame->view()->windowToContents(mouseDragEvent.position());
2445 RefPtr<FrameView> protector(m_frame->view());
2446 handleDrag(mev, DontCheckDragHysteresis);
2447 if (m_didStartDrag) {
2448 m_longTapShouldInvokeContextMenu = true;
2453 bool shouldLongPressSelectWord = true;
2455 bool shouldLongPressSelectWord = m_frame->settings() && m_frame->settings()->touchEditingEnabled();
2457 if (shouldLongPressSelectWord) {
2458 IntPoint hitTestPoint = m_frame->view()->windowToContents(gestureEvent.position());
2459 HitTestResult result = hitTestResultAtPoint(hitTestPoint);
2460 Node* innerNode = result.targetNode();
2461 if (!result.isLiveLink() && innerNode && (innerNode->isContentEditable() || innerNode->isTextNode())) {
2462 selectClosestWordFromHitTestResult(result, DontAppendTrailingWhitespace);
2463 if (m_frame->selection().isRange()) {
2464 focusDocumentView();
2469 return sendContextMenuEventForGesture(gestureEvent);
2472 bool EventHandler::handleGestureLongTap(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint)
2475 if (m_longTapShouldInvokeContextMenu) {
2476 m_longTapShouldInvokeContextMenu = false;
2477 return sendContextMenuEventForGesture(gestureEvent);
2483 bool EventHandler::handleScrollGestureOnResizer(Node* eventTarget, const PlatformGestureEvent& gestureEvent) {
2484 if (gestureEvent.type() == PlatformEvent::GestureScrollBegin) {
2485 RenderLayer* layer = eventTarget->renderer() ? eventTarget->renderer()->enclosingLayer() : 0;
2486 IntPoint p = m_frame->view()->windowToContents(gestureEvent.position());
2487 if (layer && layer->scrollableArea() && layer->scrollableArea()->isPointInResizeControl(p, ResizerForTouch)) {
2488 m_resizeScrollableArea = layer->scrollableArea();
2489 m_resizeScrollableArea->setInResizeMode(true);
2490 m_offsetFromResizeCorner = m_resizeScrollableArea->offsetFromResizeCorner(p);
2493 } else if (gestureEvent.type() == PlatformEvent::GestureScrollUpdate ||
2494 gestureEvent.type() == PlatformEvent::GestureScrollUpdateWithoutPropagation) {
2495 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode()) {
2496 m_resizeScrollableArea->resize(gestureEvent, m_offsetFromResizeCorner);
2499 } else if (gestureEvent.type() == PlatformEvent::GestureScrollEnd) {
2500 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode()) {
2501 m_resizeScrollableArea->setInResizeMode(false);
2502 m_resizeScrollableArea = 0;
2510 bool EventHandler::handleGestureTwoFingerTap(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint)
2512 return sendContextMenuEventForGesture(gestureEvent);
2515 bool EventHandler::passGestureEventToWidget(const PlatformGestureEvent& gestureEvent, Widget* widget)
2520 if (!widget->isFrameView())
2523 return toFrameView(widget)->frame().eventHandler().handleGestureEvent(gestureEvent);
2526 bool EventHandler::passGestureEventToWidgetIfPossible(const PlatformGestureEvent& gestureEvent, RenderObject* renderer)
2528 if (m_lastHitTestResultOverWidget && renderer && renderer->isWidget()) {
2529 Widget* widget = toRenderWidget(renderer)->widget();
2530 return widget && passGestureEventToWidget(gestureEvent, widget);
2535 bool EventHandler::handleGestureScrollEnd(const PlatformGestureEvent& gestureEvent) {
2536 RefPtr<Node> node = m_scrollGestureHandlingNode;
2537 clearGestureScrollNodes();
2540 ASSERT(node->refCount() > 0);
2541 passGestureEventToWidgetIfPossible(gestureEvent, node->renderer());
2547 bool EventHandler::handleGestureScrollBegin(const PlatformGestureEvent& gestureEvent)
2549 Document* document = m_frame->document();
2550 if (!document->renderView())
2553 FrameView* view = m_frame->view();
2557 LayoutPoint viewPoint = view->windowToContents(gestureEvent.position());
2558 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
2559 HitTestResult result(viewPoint);
2560 document->renderView()->hitTest(request, result);
2562 m_lastHitTestResultOverWidget = result.isOverWidget();
2563 m_scrollGestureHandlingNode = result.innerNode();
2564 m_previousGestureScrolledNode = 0;
2566 // If there's no renderer on the node, send the event to the nearest ancestor with a renderer.
2567 // Needed for <option> and <optgroup> elements so we can touch scroll <select>s
2568 while (m_scrollGestureHandlingNode && !m_scrollGestureHandlingNode->renderer())
2569 m_scrollGestureHandlingNode = m_scrollGestureHandlingNode->parentOrShadowHostNode();
2571 if (!m_scrollGestureHandlingNode)
2574 passGestureEventToWidgetIfPossible(gestureEvent, m_scrollGestureHandlingNode->renderer());
2579 bool EventHandler::handleGestureScrollUpdate(const PlatformGestureEvent& gestureEvent)
2581 FloatSize delta(gestureEvent.deltaX(), gestureEvent.deltaY());
2585 const float scaleFactor = m_frame->pageZoomFactor();
2586 delta.scale(1 / scaleFactor, 1 / scaleFactor);
2588 Node* node = m_scrollGestureHandlingNode.get();
2590 return sendScrollEventToView(gestureEvent, delta);
2592 // Ignore this event if the targeted node does not have a valid renderer.
2593 RenderObject* renderer = node->renderer();
2597 RefPtr<FrameView> protector(m_frame->view());
2600 bool scrollShouldNotPropagate = gestureEvent.type() == PlatformEvent::GestureScrollUpdateWithoutPropagation;
2602 // Try to send the event to the correct view.
2603 if (passGestureEventToWidgetIfPossible(gestureEvent, renderer)) {
2604 if(scrollShouldNotPropagate)
2605 m_previousGestureScrolledNode = m_scrollGestureHandlingNode;
2610 if (scrollShouldNotPropagate)
2611 stopNode = m_previousGestureScrolledNode.get();
2613 // First try to scroll the closest scrollable RenderBox ancestor of |node|.
2614 ScrollGranularity granularity = ScrollByPixel;
2615 bool horizontalScroll = scroll(ScrollLeft, granularity, node, &stopNode, delta.width());
2616 bool verticalScroll = scroll(ScrollUp, granularity, node, &stopNode, delta.height());
2618 if (scrollShouldNotPropagate)
2619 m_previousGestureScrolledNode = stopNode;
2621 if (horizontalScroll || verticalScroll) {
2622 setFrameWasScrolledByUser();
2626 // Otherwise try to scroll the view.
2627 return sendScrollEventToView(gestureEvent, delta);
2630 bool EventHandler::sendScrollEventToView(const PlatformGestureEvent& gestureEvent, const FloatSize& scaledDelta)
2632 FrameView* view = m_frame->view();
2636 const float tickDivisor = static_cast<float>(WheelEvent::TickMultiplier);
2637 IntPoint point(gestureEvent.position().x(), gestureEvent.position().y());
2638 IntPoint globalPoint(gestureEvent.globalPosition().x(), gestureEvent.globalPosition().y());
2639 PlatformWheelEvent syntheticWheelEvent(point, globalPoint,
2640 scaledDelta.width(), scaledDelta.height(),
2641 scaledDelta.width() / tickDivisor, scaledDelta.height() / tickDivisor,
2642 ScrollByPixelWheelEvent,
2643 gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey());
2644 syntheticWheelEvent.setHasPreciseScrollingDeltas(true);
2646 bool scrolledFrame = view->wheelEvent(syntheticWheelEvent);
2648 setFrameWasScrolledByUser();
2650 return scrolledFrame;
2653 Frame* EventHandler::getSubFrameForGestureEvent(const IntPoint& touchAdjustedPoint, const PlatformGestureEvent& gestureEvent)
2655 PlatformMouseEvent mouseDown(touchAdjustedPoint, gestureEvent.globalPosition(), LeftButton, PlatformEvent::MousePressed, 1,
2656 gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp());
2657 HitTestRequest request(HitTestRequest::ReadOnly);
2658 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseDown);
2659 return subframeForHitTestResult(mev);
2662 void EventHandler::clearGestureScrollNodes()
2664 m_scrollGestureHandlingNode = 0;
2665 m_previousGestureScrolledNode = 0;
2668 bool EventHandler::isScrollbarHandlingGestures() const
2670 return m_scrollbarHandlingScrollGesture.get();
2673 bool EventHandler::shouldApplyTouchAdjustment(const PlatformGestureEvent& event) const
2675 if (m_frame->settings() && !m_frame->settings()->touchAdjustmentEnabled())
2677 return !event.area().isEmpty();
2681 bool EventHandler::bestClickableNodeForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntPoint& targetPoint, Node*& targetNode)
2683 IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter);
2684 HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active, touchRadius);
2686 // If the touch is over a scrollbar, don't adjust the touch point since touch adjustment only takes into account
2687 // DOM nodes so a touch over a scrollbar will be adjusted towards nearby nodes. This leads to things like textarea
2688 // scrollbars being untouchable.
2689 if (result.scrollbar())
2692 IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius);
2693 Vector<RefPtr<Node>, 11> nodes;
2694 copyToVector(result.rectBasedTestResult(), nodes);
2696 // FIXME: Should be able to handle targetNode being a shadow DOM node to avoid performing uncessary hit tests
2697 // in the case where further processing on the node is required. Returning the shadow ancestor prevents a
2698 // regression in touchadjustment/html-label.html. Some refinement is required to testing/internals to
2699 // handle targetNode being a shadow DOM node.
2701 // FIXME: the explicit Vector conversion copies into a temporary and is
2703 bool success = findBestClickableCandidate(targetNode, targetPoint, touchCenter, touchRect, Vector<RefPtr<Node> > (nodes));
2704 if (success && targetNode)
2705 targetNode = targetNode->deprecatedShadowAncestorNode();
2709 bool EventHandler::bestContextMenuNodeForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntPoint& targetPoint, Node*& targetNode)
2711 IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter);
2712 HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active, touchRadius);
2714 IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius);
2715 Vector<RefPtr<Node>, 11> nodes;
2716 copyToVector(result.rectBasedTestResult(), nodes);
2718 // FIXME: the explicit Vector conversion copies into a temporary and is
2720 return findBestContextMenuCandidate(targetNode, targetPoint, touchCenter, touchRect, Vector<RefPtr<Node> >(nodes));
2723 bool EventHandler::bestZoomableAreaForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntRect& targetArea, Node*& targetNode)
2725 IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter);
2726 HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent, touchRadius);
2728 IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius);
2729 Vector<RefPtr<Node>, 11> nodes;
2730 copyToVector(result.rectBasedTestResult(), nodes);
2732 // FIXME: the explicit Vector conversion copies into a temporary and is
2734 return findBestZoomableArea(targetNode, targetArea, touchCenter, touchRect, Vector<RefPtr<Node> >(nodes));
2737 bool EventHandler::adjustGesturePosition(const PlatformGestureEvent& gestureEvent, IntPoint& adjustedPoint)
2739 if (!shouldApplyTouchAdjustment(gestureEvent))
2742 Node* targetNode = 0;
2743 switch (gestureEvent.type()) {
2744 case PlatformEvent::GestureTap:
2745 case PlatformEvent::GestureTapUnconfirmed:
2746 case PlatformEvent::GestureTapDown:
2747 case PlatformEvent::GestureShowPress:
2748 bestClickableNodeForTouchPoint(gestureEvent.position(), IntSize(gestureEvent.area().width() / 2, gestureEvent.area().height() / 2), adjustedPoint, targetNode);
2750 case PlatformEvent::GestureLongPress:
2751 case PlatformEvent::GestureLongTap:
2752 case PlatformEvent::GestureTwoFingerTap:
2753 bestContextMenuNodeForTouchPoint(gestureEvent.position(), IntSize(gestureEvent.area().width() / 2, gestureEvent.area().height() / 2), adjustedPoint, targetNode);
2756 // FIXME: Implement handling for other types as needed.
2757 ASSERT_NOT_REACHED();
2762 bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event)
2764 Document* doc = m_frame->document();
2765 FrameView* v = m_frame->view();
2769 // Clear mouse press state to avoid initiating a drag while context menu is up.
2770 m_mousePressed = false;
2772 LayoutPoint viewportPos = v->windowToContents(event.position());
2773 HitTestRequest request(HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
2774 MouseEventWithHitTestResults mev = doc->prepareMouseEvent(request, viewportPos, event);
2776 if (!m_frame->selection().contains(viewportPos)
2778 // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse.
2779 // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items
2780 // available for text selections. But only if we're above text.
2781 && (m_frame->selection().isContentEditable() || (mev.targetNode() && mev.targetNode()->isTextNode()))) {
2782 m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
2784 if (mev.hitTestResult().isMisspelled())
2785 selectClosestMisspellingFromMouseEvent(mev);
2786 else if (m_frame->editor().behavior().shouldSelectOnContextualMenuClick())
2787 selectClosestWordOrLinkFromMouseEvent(mev);
2790 swallowEvent = !dispatchMouseEvent(EventTypeNames::contextmenu, mev.targetNode(), true, 0, event, false);
2792 return swallowEvent;
2795 bool EventHandler::sendContextMenuEventForKey()
2797 FrameView* view = m_frame->view();
2801 Document* doc = m_frame->document();
2805 // Clear mouse press state to avoid initiating a drag while context menu is up.
2806 m_mousePressed = false;
2808 static const int kContextMenuMargin = 1;
2811 int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
2813 int rightAligned = 0;
2817 Element* focusedElement = doc->focusedElement();
2818 FrameSelection& selection = m_frame->selection();
2819 Position start = selection.selection().start();
2821 if (start.deprecatedNode() && (selection.rootEditableElement() || selection.isRange())) {
2822 RefPtr<Range> selectionRange = selection.toNormalizedRange();
2823 IntRect firstRect = m_frame->editor().firstRectForRange(selectionRange.get());
2825 int x = rightAligned ? firstRect.maxX() : firstRect.x();
2826 // In a multiline edit, firstRect.maxY() would endup on the next line, so -1.
2827 int y = firstRect.maxY() ? firstRect.maxY() - 1 : 0;
2828 location = IntPoint(x, y);
2829 } else if (focusedElement) {
2830 RenderBoxModelObject* box = focusedElement->renderBoxModelObject();
2833 IntRect clippedRect = box->pixelSnappedAbsoluteClippedOverflowRect();
2834 location = IntPoint(clippedRect.x(), clippedRect.maxY() - 1);
2836 location = IntPoint(
2837 rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin,
2838 kContextMenuMargin);
2841 m_frame->view()->setCursor(pointerCursor());
2843 IntPoint position = view->contentsToRootView(location);
2844 IntPoint globalPosition = view->hostWindow()->rootViewToScreen(IntRect(position, IntSize())).location();
2846 Node* targetNode = doc->focusedElement();
2850 // Use the focused node as the target for hover and active.
2851 HitTestResult result(position);
2852 result.setInnerNode(targetNode);
2853 doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent, result.innerElement());
2855 // The contextmenu event is a mouse event even when invoked using the keyboard.
2856 // This is required for web compatibility.
2859 PlatformEvent::Type eventType = PlatformEvent::MouseReleased;
2861 PlatformEvent::Type eventType = PlatformEvent::MousePressed;
2864 PlatformMouseEvent mouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WTF::currentTime());
2866 return !dispatchMouseEvent(EventTypeNames::contextmenu, targetNode, true, 0, mouseEvent, false);
2869 bool EventHandler::sendContextMenuEventForGesture(const PlatformGestureEvent& event)
2872 PlatformEvent::Type eventType = PlatformEvent::MouseReleased;
2874 PlatformEvent::Type eventType = PlatformEvent::MousePressed;
2877 IntPoint adjustedPoint = event.position();
2878 adjustGesturePosition(event, adjustedPoint);
2879 PlatformMouseEvent mouseEvent(adjustedPoint, event.globalPosition(), RightButton, eventType, 1, false, false, false, false, WTF::currentTime());
2880 // To simulate right-click behavior, we send a right mouse down and then
2881 // context menu event.
2882 handleMousePressEvent(mouseEvent);
2883 return sendContextMenuEvent(mouseEvent);
2884 // We do not need to send a corresponding mouse release because in case of
2885 // right-click, the context menu takes capture and consumes all events.
2888 void EventHandler::scheduleHoverStateUpdate()
2890 if (!m_hoverTimer.isActive())
2891 m_hoverTimer.startOneShot(0);
2894 void EventHandler::scheduleCursorUpdate()
2896 if (!m_cursorUpdateTimer.isActive())
2897 m_cursorUpdateTimer.startOneShot(cursorUpdateInterval);
2900 void EventHandler::dispatchFakeMouseMoveEventSoon()
2905 if (m_mousePositionIsUnknown)
2908 Settings* settings = m_frame->settings();
2909 if (settings && !settings->deviceSupportsMouse())
2912 // If the content has ever taken longer than fakeMouseMoveShortInterval we
2913 // reschedule the timer and use a longer time. This will cause the content
2914 // to receive these moves only after the user is done scrolling, reducing
2915 // pauses during the scroll.
2916 if (m_maxMouseMovedDuration > fakeMouseMoveShortInterval) {
2917 if (m_fakeMouseMoveEventTimer.isActive())
2918 m_fakeMouseMoveEventTimer.stop();
2919 m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveLongInterval);
2921 if (!m_fakeMouseMoveEventTimer.isActive())
2922 m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveShortInterval);
2926 void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad)
2928 FrameView* view = m_frame->view();
2932 if (!quad.containsPoint(view->windowToContents(m_lastKnownMousePosition)))
2935 dispatchFakeMouseMoveEventSoon();
2938 void EventHandler::fakeMouseMoveEventTimerFired(Timer<EventHandler>* timer)
2940 ASSERT_UNUSED(timer, timer == &m_fakeMouseMoveEventTimer);
2941 ASSERT(!m_mousePressed);
2943 Settings* settings = m_frame->settings();
2944 if (settings && !settings->deviceSupportsMouse())
2947 FrameView* view = m_frame->view();
2951 if (!m_frame->page() || !m_frame->page()->focusController().isActive())
2954 // Don't dispatch a synthetic mouse move event if the mouse cursor is not visible to the user.
2955 if (!isCursorVisible())
2962 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
2963 PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, currentTime());
2964 handleMouseMoveEvent(fakeMouseMoveEvent);
2967 void EventHandler::cancelFakeMouseMoveEvent()
2969 m_fakeMouseMoveEventTimer.stop();
2972 bool EventHandler::isCursorVisible() const
2974 return m_frame->page()->isCursorVisible();
2977 void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet)
2979 m_frameSetBeingResized = frameSet;
2982 void EventHandler::resizeScrollableAreaDestroyed()
2984 ASSERT(m_resizeScrollableArea);
2985 m_resizeScrollableArea = 0;
2988 void EventHandler::hoverTimerFired(Timer<EventHandler>*)
2990 m_hoverTimer.stop();
2993 ASSERT(m_frame->document());
2995 if (RenderView* renderer = m_frame->contentRenderer()) {
2996 if (FrameView* view = m_frame->view()) {
2997 HitTestRequest request(HitTestRequest::Move | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
2998 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
2999 renderer->hitTest(request, result);
3000 m_frame->document()->updateHoverActiveState(request, result.innerElement());
3005 void EventHandler::activeIntervalTimerFired(Timer<EventHandler>*)
3007 m_activeIntervalTimer.stop();
3010 && m_frame->document()
3011 && m_lastDeferredTapElement) {
3012 // FIXME: Enable condition when http://crbug.com/226842 lands
3013 // m_lastDeferredTapElement.get() == m_frame->document()->activeElement()
3014 HitTestRequest request(HitTestRequest::TouchEvent | HitTestRequest::Release);
3015 m_frame->document()->updateHoverActiveState(request, m_lastDeferredTapElement.get());
3017 m_lastDeferredTapElement = 0;
3020 void EventHandler::notifyElementActivated()
3022 // Since another element has been set to active, stop current timer and clear reference.
3023 if (m_activeIntervalTimer.isActive())
3024 m_activeIntervalTimer.stop();
3025 m_lastDeferredTapElement = 0;
3028 bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& evt)
3030 // FIXME: Ignoring the state of Shift key is what neither IE nor Firefox do.
3031 // IE matches lower and upper case access keys regardless of Shift key state - but if both upper and
3032 // lower case variants are present in a document, the correct element is matched based on Shift key state.
3033 // Firefox only matches an access key if Shift is not pressed, and does that case-insensitively.
3034 ASSERT(!(accessKeyModifiers() & PlatformEvent::ShiftKey));
3035 if ((evt.modifiers() & ~PlatformEvent::ShiftKey) != accessKeyModifiers())
3037 String key = evt.unmodifiedText();
3038 Element* elem = m_frame->document()->getElementByAccessKey(key.lower());
3041 elem->accessKeyAction(false);
3045 bool EventHandler::isKeyEventAllowedInFullScreen(FullscreenElementStack* fullscreen, const PlatformKeyboardEvent& keyEvent) const
3047 if (fullscreen->webkitFullScreenKeyboardInputAllowed())
3050 if (keyEvent.type() == PlatformKeyboardEvent::Char) {
3051 if (keyEvent.text().length() != 1)
3053 UChar character = keyEvent.text()[0];
3054 return character == ' ';
3057 int keyCode = keyEvent.windowsVirtualKeyCode();
3058 return (keyCode >= VK_BACK && keyCode <= VK_CAPITAL)
3059 || (keyCode >= VK_SPACE && keyCode <= VK_DELETE)
3060 || (keyCode >= VK_OEM_1 && keyCode <= VK_OEM_PLUS)
3061 || (keyCode >= VK_MULTIPLY && keyCode <= VK_OEM_8);
3064 bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent)
3066 RefPtr<FrameView> protector(m_frame->view());
3068 if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(m_frame->document())) {
3069 if (fullscreen->webkitIsFullScreen() && !isKeyEventAllowedInFullScreen(fullscreen, initialKeyEvent))
3073 if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL)
3074 capsLockStateMayHaveChanged();
3077 if (panScrollInProgress()) {
3078 // If a key is pressed while the panScroll is in progress then we want to stop
3079 if (initialKeyEvent.type() == PlatformEvent::KeyDown || initialKeyEvent.type() == PlatformEvent::RawKeyDown)
3082 // If we were in panscroll mode, we swallow the key event
3087 // Check for cases where we are too early for events -- possible unmatched key up
3088 // from pressing return in the location bar.
3089 RefPtr<Node> node = eventTargetNodeForDocument(m_frame->document());
3093 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
3095 // In IE, access keys are special, they are handled after default keydown processing, but cannot be canceled - this is hard to match.
3096 // On Mac OS X, we process them before dispatching keydown, as the default keydown handler implements Emacs key bindings, which may conflict
3097 // with access keys. Then we dispatch keydown, but suppress its default handling.
3098 // On Windows, WebKit explicitly calls handleAccessKey() instead of dispatching a keypress event for WM_SYSCHAR messages.
3099 // Other platforms currently match either Mac or Windows behavior, depending on whether they send combined KeyDown events.
3100 bool matchedAnAccessKey = false;
3101 if (initialKeyEvent.type() == PlatformEvent::KeyDown)
3102 matchedAnAccessKey = handleAccessKey(initialKeyEvent);
3104 // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch.
3105 if (initialKeyEvent.type() == PlatformEvent::KeyUp || initialKeyEvent.type() == PlatformEvent::Char)
3106 return !node->dispatchKeyEvent(initialKeyEvent);
3108 PlatformKeyboardEvent keyDownEvent = initialKeyEvent;
3109 if (keyDownEvent.type() != PlatformEvent::RawKeyDown)
3110 keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown);
3111 RefPtr<KeyboardEvent> keydown = KeyboardEvent::create(keyDownEvent, m_frame->document()->domWindow());
3112 if (matchedAnAccessKey)
3113 keydown->setDefaultPrevented(true);
3114 keydown->setTarget(node);
3116 if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) {
3117 node->dispatchEvent(keydown, IGNORE_EXCEPTION);
3118 // If frame changed as a result of keydown dispatch, then return true to avoid sending a subsequent keypress message to the new frame.
3119 bool changedFocusedFrame = m_frame->page() && m_frame != m_frame->page()->focusController().focusedOrMainFrame();
3120 return keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3123 node->dispatchEvent(keydown, IGNORE_EXCEPTION);
3124 // If frame changed as a result of keydown dispatch, then return early to avoid sending a subsequent keypress message to the new frame.
3125 bool changedFocusedFrame = m_frame->page() && m_frame != m_frame->page()->focusController().focusedOrMainFrame();
3126 bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3128 return keydownResult;
3130 // Focus may have changed during keydown handling, so refetch node.
3131 // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original node.
3132 node = eventTargetNodeForDocument(m_frame->document());
3136 PlatformKeyboardEvent keyPressEvent = initialKeyEvent;
3137 keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Char);
3138 if (keyPressEvent.text().isEmpty())
3139 return keydownResult;
3140 RefPtr<KeyboardEvent> keypress = KeyboardEvent::create(keyPressEvent, m_frame->document()->domWindow());
3141 keypress->setTarget(node);
3143 keypress->setDefaultPrevented(true);
3144 node->dispatchEvent(keypress, IGNORE_EXCEPTION);
3146 return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled();
3149 static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier)
3151 DEFINE_STATIC_LOCAL(AtomicString, Down, ("Down", AtomicString::ConstructFromLiteral));
3152 DEFINE_STATIC_LOCAL(AtomicString, Up, ("Up", AtomicString::ConstructFromLiteral));
3153 DEFINE_STATIC_LOCAL(AtomicString, Left, ("Left", AtomicString::ConstructFromLiteral));
3154 DEFINE_STATIC_LOCAL(AtomicString, Right, ("Right", AtomicString::ConstructFromLiteral));
3156 FocusDirection retVal = FocusDirectionNone;
3158 if (keyIdentifier == Down)
3159 retVal = FocusDirectionDown;
3160 else if (keyIdentifier == Up)
3161 retVal = FocusDirectionUp;
3162 else if (keyIdentifier == Left)
3163 retVal = FocusDirectionLeft;
3164 else if (keyIdentifier == Right)
3165 retVal = FocusDirectionRight;
3170 void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event)
3172 if (event->type() == EventTypeNames::keydown) {
3173 m_frame->editor().handleKeyboardEvent(event);
3174 if (event->defaultHandled())
3176 if (event->keyIdentifier() == "U+0009")
3177 defaultTabEventHandler(event);
3178 else if (event->keyIdentifier() == "U+0008")
3179 defaultBackspaceEventHandler(event);
3180 else if (event->keyIdentifier() == "U+001B")
3181 defaultEscapeEventHandler(event);
3183 FocusDirection direction = focusDirectionForKey(AtomicString(event->keyIdentifier()));
3184 if (direction != FocusDirectionNone)
3185 defaultArrowEventHandler(direction, event);
3188 if (event->type() == EventTypeNames::keypress) {
3189 m_frame->editor().handleKeyboardEvent(event);
3190 if (event->defaultHandled())
3192 if (event->charCode() == ' ')
3193 defaultSpaceEventHandler(event);
3197 bool EventHandler::dragHysteresisExceeded(const IntPoint& floatDragViewportLocation) const
3199 FloatPoint dragViewportLocation(floatDragViewportLocation.x(), floatDragViewportLocation.y());
3200 return dragHysteresisExceeded(dragViewportLocation);
3203 bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation) const
3205 FrameView* view = m_frame->view();
3208 IntPoint dragLocation = view->windowToContents(flooredIntPoint(dragViewportLocation));
3209 IntSize delta = dragLocation - m_mouseDownPos;
3211 int threshold = GeneralDragHysteresis;
3212 switch (dragState().m_dragType) {
3213 case DragSourceActionSelection:
3214 threshold = TextDragHysteresis;
3216 case DragSourceActionImage:
3217 threshold = ImageDragHysteresis;
3219 case DragSourceActionLink:
3220 threshold = LinkDragHysteresis;
3222 case DragSourceActionDHTML:
3224 case DragSourceActionNone:
3225 ASSERT_NOT_REACHED();
3228 return abs(delta.width()) >= threshold || abs(delta.height()) >= threshold;
3231 void EventHandler::freeClipboard()
3233 if (dragState().m_dragClipboard)
3234 dragState().m_dragClipboard->setAccessPolicy(ClipboardNumb);
3237 void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation)
3239 // Send a hit test request so that RenderLayer gets a chance to update the :hover and :active pseudoclasses.
3240 HitTestRequest request(HitTestRequest::Release | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
3241 prepareMouseEvent(request, event);
3243 if (dragState().m_dragSrc) {
3244 dragState().m_dragClipboard->setDestinationOperation(operation);
3245 // for now we don't care if event handler cancels default behavior, since there is none
3246 dispatchDragSrcEvent(EventTypeNames::dragend, event);
3249 dragState().m_dragSrc = 0;
3250 // In case the drag was ended due to an escape key press we need to ensure
3251 // that consecutive mousemove events don't reinitiate the drag and drop.
3252 m_mouseDownMayStartDrag = false;
3255 void EventHandler::updateDragStateAfterEditDragIfNeeded(Element* rootEditableElement)
3257 // If inserting the dragged contents removed the drag source, we still want to fire dragend at the root editble element.
3258 if (dragState().m_dragSrc && !dragState().m_dragSrc->inDocument())
3259 dragState().m_dragSrc = rootEditableElement;
3262 // returns if we should continue "default processing", i.e., whether eventhandler canceled
3263 bool EventHandler::dispatchDragSrcEvent(const AtomicString& eventType, const PlatformMouseEvent& event)
3265 return !dispatchDragEvent(eventType, dragState().m_dragSrc.get(), event, dragState().m_dragClipboard.get());
3268 bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis)
3270 ASSERT(event.event().type() == PlatformEvent::MouseMoved);
3271 // Callers must protect the reference to FrameView, since this function may dispatch DOM
3272 // events, causing page/FrameView to go away.
3274 ASSERT(m_frame->view());
3275 if (!m_frame->page())
3278 if (event.event().button() != LeftButton || event.event().type() != PlatformEvent::MouseMoved) {
3279 // If we allowed the other side of the bridge to handle a drag
3280 // last time, then m_mousePressed might still be set. So we
3281 // clear it now to make sure the next move after a drag
3282 // doesn't look like a drag.
3283 m_mousePressed = false;
3287 if (m_mouseDownMayStartDrag) {
3288 HitTestRequest request(HitTestRequest::ReadOnly);
3289 HitTestResult result(m_mouseDownPos);
3290 m_frame->contentRenderer()->hitTest(request, result);
3291 Node* node = result.innerNode();
3293 DragController::SelectionDragPolicy selectionDragPolicy = event.event().timestamp() - m_mouseDownTimestamp < TextDragDelay
3294 ? DragController::DelayedSelectionDragResolution
3295 : DragController::ImmediateSelectionDragResolution;
3296 dragState().m_dragSrc = m_frame->page()->dragController().draggableNode(m_frame, node, m_mouseDownPos, selectionDragPolicy, dragState().m_dragType);
3298 dragState().m_dragSrc = 0;
3301 if (!dragState().m_dragSrc)
3302 m_mouseDownMayStartDrag = false; // no element is draggable
3305 if (!m_mouseDownMayStartDrag)
3306 return !mouseDownMayStartSelect() && !m_mouseDownMayStartAutoscroll;
3308 // We are starting a text/image/url drag, so the cursor should be an arrow
3309 // FIXME <rdar://7577595>: Custom cursors aren't supported during drag and drop (default to pointer).
3310 m_frame->view()->setCursor(pointerCursor());
3312 if (checkDragHysteresis == ShouldCheckDragHysteresis && !dragHysteresisExceeded(event.event().position()))
3315 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
3318 if (!tryStartDrag(event)) {
3319 // Something failed to start the drag, clean up.
3321 dragState().m_dragSrc = 0;
3324 m_mouseDownMayStartDrag = false;
3325 // Whether or not the drag actually started, no more default handling (like selection).
3329 bool EventHandler::tryStartDrag(const MouseEventWithHitTestResults& event)
3331 freeClipboard(); // would only happen if we missed a dragEnd. Do it anyway, just
3332 // to make sure it gets numbified
3333 dragState().m_dragClipboard = createDraggingClipboard();
3335 // Check to see if this a DOM based drag, if it is get the DOM specified drag
3337 if (dragState().m_dragType == DragSourceActionDHTML) {
3338 if (RenderObject* renderer = dragState().m_dragSrc->renderer()) {
3339 // FIXME: This doesn't work correctly with transforms.
3340 FloatPoint absPos = renderer->localToAbsolute();
3341 IntSize delta = m_mouseDownPos - roundedIntPoint(absPos);
3342 dragState().m_dragClipboard->setDragImageElement(dragState().m_dragSrc.get(), IntPoint(delta));
3344 // The renderer has disappeared, this can happen if the onStartDrag handler has hidden
3345 // the element in some way. In this case we just kill the drag.
3350 DragController& dragController = m_frame->page()->dragController();
3351 if (!dragController.populateDragClipboard(m_frame, dragState(), m_mouseDownPos))
3353 m_mouseDownMayStartDrag = dispatchDragSrcEvent(EventTypeNames::dragstart, m_mouseDown)
3354 && !m_frame->selection().isInPasswordField();
3356 // Invalidate clipboard here against anymore pasteboard writing for security. The drag
3357 // image can still be changed as we drag, but not the pasteboard data.
3358 dragState().m_dragClipboard->setAccessPolicy(ClipboardImageWritable);
3360 if (m_mouseDownMayStartDrag) {
3361 // Dispatching the event could cause Page to go away. Make sure it's still valid before trying to use DragController.
3362 m_didStartDrag = m_frame->page() && dragController.startDrag(m_frame, dragState(), event.event(), m_mouseDownPos);
3363 // FIXME: This seems pretty useless now. The gesture code uses this as a signal for
3364 // whether or not the drag started, but perhaps it can simply use the return value from
3365 // handleDrag(), even though it doesn't mean exactly the same thing.
3368 // Drag was canned at the last minute - we owe m_dragSrc a DRAGEND event
3369 dispatchDragSrcEvent(EventTypeNames::dragend, event.event());
3375 bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEvent, TextEventInputType inputType)
3377 // Platforms should differentiate real commands like selectAll from text input in disguise (like insertNewline),
3378 // and avoid dispatching text input events from keydown default handlers.
3379 ASSERT(!underlyingEvent || !underlyingEvent->isKeyboardEvent() || toKeyboardEvent(underlyingEvent)->type() == EventTypeNames::keypress);
3384 EventTarget* target;
3385 if (underlyingEvent)
3386 target = underlyingEvent->target();
3388 target = eventTargetNodeForDocument(m_frame->document());
3392 RefPtr<TextEvent> event = TextEvent::create(m_frame->domWindow(), text, inputType);
3393 event->setUnderlyingEvent(underlyingEvent);
3395 target->dispatchEvent(event, IGNORE_EXCEPTION);
3396 return event->defaultHandled();
3399 void EventHandler::defaultTextInputEventHandler(TextEvent* event)
3401 if (m_frame->editor().handleTextEvent(event))
3402 event->setDefaultHandled();
3405 void EventHandler::defaultSpaceEventHandler(KeyboardEvent* event)
3407 ASSERT(event->type() == EventTypeNames::keypress);
3409 if (event->ctrlKey() || event->metaKey() || event->altKey() || event->altGraphKey())
3412 ScrollDirection direction = event->shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward;
3413 if (scroll(direction, ScrollByPage)) {
3414 event->setDefaultHandled();
3418 FrameView* view = m_frame->view();
3422 if (view->scroll(direction, ScrollByPage))
3423 event->setDefaultHandled();
3426 void EventHandler::defaultBackspaceEventHandler(KeyboardEvent* event)
3428 ASSERT(event->type() == EventTypeNames::keydown);
3430 if (event->ctrlKey() || event->metaKey() || event->altKey() || event->altGraphKey())
3433 if (!m_frame->editor().behavior().shouldNavigateBackOnBackspace())
3436 Page* page = m_frame->page();
3439 bool handledEvent = page->mainFrame()->loader().client()->navigateBackForward(event->shiftKey() ? 1 : -1);
3441 event->setDefaultHandled();
3444 void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, KeyboardEvent* event)
3446 ASSERT(event->type() == EventTypeNames::keydown);
3448 if (event->ctrlKey() || event->metaKey() || event->altGraphKey() || event->shiftKey())
3451 Page* page = m_frame->page();
3455 if (!isSpatialNavigationEnabled(m_frame))
3458 // Arrows and other possible directional navigation keys can be used in design
3460 if (m_frame->document()->inDesignMode())
3463 if (page->focusController().advanceFocus(focusDirection))
3464 event->setDefaultHandled();
3467 void EventHandler::defaultTabEventHandler(KeyboardEvent* event)
3469 ASSERT(event->type() == EventTypeNames::keydown);
3471 // We should only advance focus on tabs if no special modifier keys are held down.
3472 if (event->ctrlKey() || event->metaKey() || event->altGraphKey())
3475 Page* page = m_frame->page();
3478 if (!page->tabKeyCyclesThroughElements())
3481 FocusDirection focusDirection = event->shiftKey() ? FocusDirectionBackward : FocusDirectionForward;
3483 // Tabs can be used in design mode editing.
3484 if (m_frame->document()->inDesignMode())
3487 if (page->focusController().advanceFocus(focusDirection))
3488 event->setDefaultHandled();
3491 void EventHandler::defaultEscapeEventHandler(KeyboardEvent* event)
3493 if (HTMLDialogElement* dialog = m_frame->document()->activeModalDialog())
3494 dialog->dispatchEvent(Event::createCancelable(EventTypeNames::cancel));
3497 void EventHandler::capsLockStateMayHaveChanged()
3499 if (Element* element = m_frame->document()->focusedElement()) {
3500 if (RenderObject* r = element->renderer()) {
3501 if (r->isTextField())
3502 toRenderTextControlSingleLine(r)->capsLockStateMayHaveChanged();
3507 void EventHandler::setFrameWasScrolledByUser()
3509 if (FrameView* view = m_frame->view())
3510 view->setWasScrolledByUser(true);
3513 bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mev, Scrollbar* scrollbar)
3515 if (!scrollbar || !scrollbar->enabled())
3517 setFrameWasScrolledByUser();
3518 scrollbar->mouseDown(mev.event());
3522 // If scrollbar (under mouse) is different from last, send a mouse exited. Set
3523 // last to scrollbar if setLast is true; else set last to 0.
3524 void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, bool setLast)
3526 if (m_lastScrollbarUnderMouse != scrollbar) {
3527 // Send mouse exited to the old scrollbar.
3528 if (m_lastScrollbarUnderMouse)
3529 m_lastScrollbarUnderMouse->mouseExited();
3531 // Send mouse entered if we're setting a new scrollbar.
3532 if (scrollbar && setLast)
3533 scrollbar->mouseEntered();
3535 m_lastScrollbarUnderMouse = setLast ? scrollbar : 0;
3539 static const AtomicString& eventNameForTouchPointState(PlatformTouchPoint::State state)
3542 case PlatformTouchPoint::TouchReleased:
3543 return EventTypeNames::touchend;
3544 case PlatformTouchPoint::TouchCancelled:
3545 return EventTypeNames::touchcancel;
3546 case PlatformTouchPoint::TouchPressed:
3547 return EventTypeNames::touchstart;
3548 case PlatformTouchPoint::TouchMoved:
3549 return EventTypeNames::touchmove;
3550 case PlatformTouchPoint::TouchStationary:
3551 // TouchStationary state is not converted to touch events, so fall through to assert.
3553 ASSERT_NOT_REACHED();
3558 HitTestResult EventHandler::hitTestResultInFrame(Frame* frame, const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType)
3560 HitTestResult result(point);
3562 if (!frame || !frame->contentRenderer())
3564 if (frame->view()) {
3565 IntRect rect = frame->view()->visibleContentRect();
3566 if (!rect.contains(roundedIntPoint(point)))
3569 frame->contentRenderer()->hitTest(HitTestRequest(hitType), result);
3573 bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
3575 // First build up the lists to use for the 'touches', 'targetTouches' and 'changedTouches' attributes
3576 // in the JS event. See http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/
3577 // for an overview of how these lists fit together.
3579 // Holds the complete set of touches on the screen and will be used as the 'touches' list in the JS event.
3580 RefPtr<TouchList> touches = TouchList::create();
3582 // A different view on the 'touches' list above, filtered and grouped by event target. Used for the
3583 // 'targetTouches' list in the JS event.
3584 typedef HashMap<EventTarget*, RefPtr<TouchList> > TargetTouchesMap;
3585 TargetTouchesMap touchesByTarget;
3587 // Array of touches per state, used to assemble the 'changedTouches' list in the JS event.
3588 typedef HashSet<RefPtr<EventTarget> > EventTargetSet;
3590 // The touches corresponding to the particular change state this struct instance represents.
3591 RefPtr<TouchList> m_touches;
3592 // Set of targets involved in m_touches.
3593 EventTargetSet m_targets;
3594 } changedTouches[PlatformTouchPoint::TouchStateEnd];
3596 const Vector<PlatformTouchPoint>& points = event.touchPoints();
3598 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
3601 bool freshTouchEvents = true;
3602 bool allTouchReleased = true;
3603 for (i = 0; i < points.size(); ++i) {
3604 const PlatformTouchPoint& point = points[i];
3605 if (point.state() != PlatformTouchPoint::TouchPressed)
3606 freshTouchEvents = false;
3607 if (point.state() != PlatformTouchPoint::TouchReleased && point.state() != PlatformTouchPoint::TouchCancelled)
3608 allTouchReleased = false;
3611 for (i = 0; i < points.size(); ++i) {
3612 const PlatformTouchPoint& point = points[i];
3613 PlatformTouchPoint::State pointState = point.state();
3614 LayoutPoint pagePoint = documentPointForWindowPoint(m_frame, point.pos());
3616 // Gesture events trigger the active state, not touch events,
3617 // so touch event hit tests can always be read only.
3618 HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent | HitTestRequest::ReadOnly;
3619 // The HitTestRequest types used for mouse events map quite adequately
3620 // to touch events. Note that in addition to meaning that the hit test
3621 // should affect the active state of the current node if necessary,
3622 // HitTestRequest::Active signifies that the hit test is taking place
3623 // with the mouse (or finger in this case) being pressed.
3624 switch (pointState) {
3625 case PlatformTouchPoint::TouchPressed:
3626 hitType |= HitTestRequest::Active;
3628 case PlatformTouchPoint::TouchMoved:
3629 hitType |= HitTestRequest::Active | HitTestRequest::Move;
3631 case PlatformTouchPoint::TouchReleased:
3632 case PlatformTouchPoint::TouchCancelled:
3633 hitType |= HitTestRequest::Release;
3635 case PlatformTouchPoint::TouchStationary:
3636 hitType |= HitTestRequest::Active;
3639 ASSERT_NOT_REACHED();
3643 // Increment the platform touch id by 1 to avoid storing a key of 0 in the hashmap.
3644 unsigned touchPointTargetKey = point.id() + 1;
3645 RefPtr<EventTarget> touchTarget;
3646 if (pointState == PlatformTouchPoint::TouchPressed) {
3647 HitTestResult result;
3648 if (freshTouchEvents) {
3649 result = hitTestResultAtPoint(pagePoint, hitType);
3650 m_originatingTouchPointTargetKey = touchPointTargetKey;
3651 } else if (m_originatingTouchPointDocument.get() && m_originatingTouchPointDocument->frame()) {
3652 LayoutPoint pagePointInOriginatingDocument = documentPointForWindowPoint(m_originatingTouchPointDocument->frame(), point.pos());
3653 result = hitTestResultInFrame(m_originatingTouchPointDocument->frame(), pagePointInOriginatingDocument, hitType);
3657 Node* node = result.innerNode();
3661 // Touch events should not go to text nodes
3662 if (node->isTextNode())
3663 node = EventPath::parent(node);
3665 Document& doc = node->document();
3666 // Record the originating touch document even if it does not have a touch listener.
3667 if (freshTouchEvents) {
3668 m_originatingTouchPointDocument = &doc;
3669 freshTouchEvents = false;
3671 if (!doc.hasTouchEventHandlers())
3673 m_originatingTouchPointTargets.set(touchPointTargetKey, node);
3676 // FIXME(rbyers): Should really be doing a second hit test that ignores inline elements - crbug.com/319479.
3677 TouchAction effectiveTouchAction = computeEffectiveTouchAction(*node);
3678 if (effectiveTouchAction != TouchActionAuto)
3679 m_frame->page()->chrome().client().setTouchAction(effectiveTouchAction);
3681 } else if (pointState == PlatformTouchPoint::TouchReleased || pointState == PlatformTouchPoint::TouchCancelled) {
3682 // The target should be the original target for this touch, so get it from the hashmap. As it's a release or cancel
3683 // we also remove it from the map.
3684 touchTarget = m_originatingTouchPointTargets.take(touchPointTargetKey);
3686 // No hittest is performed on move or stationary, since the target is not allowed to change anyway.
3687 touchTarget = m_originatingTouchPointTargets.get(touchPointTargetKey);
3689 if (!touchTarget.get())
3691 Document& doc = touchTarget->toNode()->document();
3692 if (!doc.hasTouchEventHandlers())
3694 Frame* targetFrame = doc.frame();
3698 if (m_frame != targetFrame) {
3699 // pagePoint should always be relative to the target elements containing frame.
3700 pagePoint = documentPointForWindowPoint(targetFrame, point.pos());
3703 float scaleFactor = targetFrame->pageZoomFactor();
3705 int adjustedPageX = lroundf(pagePoint.x() / scaleFactor);
3706 int adjustedPageY = lroundf(pagePoint.y() / scaleFactor);
3707 int adjustedRadiusX = lroundf(point.radiusX() / scaleFactor);
3708 int adjustedRadiusY = lroundf(point.radiusY() / scaleFactor);
3710 RefPtr<Touch> touch = Touch::create(targetFrame, touchTarget.get(), point.id(),
3711 point.screenPos().x(), point.screenPos().y(),
3712 adjustedPageX, adjustedPageY,
3713 adjustedRadiusX, adjustedRadiusY,
3714 point.rotationAngle(), point.force());
3716 // Ensure this target's touch list exists, even if it ends up empty, so it can always be passed to TouchEvent::Create below.
3717 TargetTouchesMap::iterator targetTouchesIterator = touchesByTarget.find(touchTarget.get());
3718 if (targetTouchesIterator == touchesByTarget.end())
3719 targetTouchesIterator = touchesByTarget.set(touchTarget.get(), TouchList::create()).iterator;
3721 // touches and targetTouches should only contain information about touches still on the screen, so if this point is
3722 // released or cancelled it will only appear in the changedTouches list.
3723 if (pointState != PlatformTouchPoint::TouchReleased && pointState != PlatformTouchPoint::TouchCancelled) {
3724 touches->append(touch);
3725 targetTouchesIterator->value->append(touch);
3728 // Now build up the correct list for changedTouches.
3729 // Note that any touches that are in the TouchStationary state (e.g. if
3730 // the user had several points touched but did not move them all) should
3731 // never be in the changedTouches list so we do not handle them explicitly here.
3732 // See https://bugs.webkit.org/show_bug.cgi?id=37609 for further discussion
3733 // about the TouchStationary state.
3734 if (pointState != PlatformTouchPoint::TouchStationary) {
3735 ASSERT(pointState < PlatformTouchPoint::TouchStateEnd);
3736 if (!changedTouches[pointState].m_touches)
3737 changedTouches[pointState].m_touches = TouchList::create();
3738 changedTouches[pointState].m_touches->append(touch);
3739 changedTouches[pointState].m_targets.add(touchTarget);
3742 m_touchPressed = touches->length() > 0;
3743 if (allTouchReleased)
3744 m_originatingTouchPointDocument.clear();
3746 // Now iterate the changedTouches list and m_targets within it, sending events to the targets as required.
3747 bool swallowedEvent = false;
3748 RefPtr<TouchList> emptyList = TouchList::create();
3749 for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; ++state) {
3750 if (!changedTouches[state].m_touches)
3753 // When sending a touch cancel event, use empty touches and targetTouches lists.
3754 bool isTouchCancelEvent = (state == PlatformTouchPoint::TouchCancelled);
3755 RefPtr<TouchList>& effectiveTouches(isTouchCancelEvent ? emptyList : touches);
3756 const AtomicString& stateName(eventNameForTouchPointState(static_cast<PlatformTouchPoint::State>(state)));
3757 const EventTargetSet& targetsForState = changedTouches[state].m_targets;
3759 for (EventTargetSet::const_iterator it = targetsForState.begin(); it != targetsForState.end(); ++it) {
3760 EventTarget* touchEventTarget = it->get();
3761 RefPtr<TouchList> targetTouches(isTouchCancelEvent ? emptyList : touchesByTarget.get(touchEventTarget));
3762 ASSERT(targetTouches);
3764 RefPtr<TouchEvent> touchEvent =
3765 TouchEvent::create(effectiveTouches.get(), targetTouches.get(), changedTouches[state].m_touches.get(),
3766 stateName, touchEventTarget->toNode()->document().domWindow(),
3767 0, 0, 0, 0, event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey());
3768 touchEventTarget->toNode()->dispatchTouchEvent(touchEvent.get());
3769 swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled();
3773 return swallowedEvent;
3776 bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent& event)
3778 if (!m_frame || !m_frame->settings() || !m_frame->settings()->touchEventEmulationEnabled())
3781 PlatformEvent::Type eventType = event.type();
3782 if (eventType != PlatformEvent::MouseMoved && eventType != PlatformEvent::MousePressed && eventType != PlatformEvent::MouseReleased)
3785 HitTestRequest request(HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
3786 MouseEventWithHitTestResults mev = prepareMouseEvent(request, event);
3787 if (mev.scrollbar() || subframeForHitTestResult(mev))
3790 // The order is important. This check should follow the subframe test: http://webkit.org/b/111292.
3791 if (eventType == PlatformEvent::MouseMoved && event.button() == NoButton)
3794 SyntheticSingleTouchEvent touchEvent(event);
3795 if (handleTouchEvent(touchEvent))
3798 return handleMouseEventAsEmulatedGesture(event);
3801 bool EventHandler::handleMouseEventAsEmulatedGesture(const PlatformMouseEvent& event)
3803 PlatformEvent::Type eventType = event.type();
3804 if (event.button() != LeftButton || !m_frame->isMainFrame())
3807 // Simulate pinch / scroll gesture.
3808 const IntPoint& position = event.position();
3809 bool swallowEvent = false;
3811 FrameView* view = m_frame->view();
3812 if (event.shiftKey()) {
3813 // Shift pressed - consider it gesture.
3814 swallowEvent = true;
3815 Page* page = m_frame->page();
3816 float pageScaleFactor = page->pageScaleFactor();
3817 switch (eventType) {
3818 case PlatformEvent::MousePressed:
3819 m_lastSyntheticPinchAnchorCss = adoptPtr(new IntPoint(view->scrollPosition() + position));
3820 m_lastSyntheticPinchAnchorDip = adoptPtr(new IntPoint(position));
3821 m_lastSyntheticPinchAnchorDip->scale(pageScaleFactor, pageScaleFactor);
3822 m_syntheticPageScaleFactor = pageScaleFactor;
3824 case PlatformEvent::MouseMoved:
3826 if (!m_lastSyntheticPinchAnchorCss)
3829 float dy = m_lastSyntheticPinchAnchorDip->y() - position.y() * pageScaleFactor;
3830 float magnifyDelta = exp(dy * 0.002f);
3831 float newPageScaleFactor = m_syntheticPageScaleFactor * magnifyDelta;
3833 IntPoint anchorCss(*m_lastSyntheticPinchAnchorDip.get());
3834 anchorCss.scale(1.f / newPageScaleFactor, 1.f / newPageScaleFactor);
3835 page->inspectorController().requestPageScaleFactor(newPageScaleFactor, *m_lastSyntheticPinchAnchorCss.get() - toIntSize(anchorCss));
3838 case PlatformEvent::MouseReleased:
3839 m_lastSyntheticPinchAnchorCss.clear();
3840 m_lastSyntheticPinchAnchorDip.clear();
3845 switch (eventType) {
3846 case PlatformEvent::MouseMoved:
3848 // Always consume move events.
3849 swallowEvent = true;
3850 int dx = m_lastSyntheticPanLocation ? position.x() - m_lastSyntheticPanLocation->x() : 0;
3851 int dy = m_lastSyntheticPanLocation ? position.y() - m_lastSyntheticPanLocation->y() : 0;
3853 view->scrollBy(IntSize(-dx, -dy));
3854 // Mouse dragged - consider it gesture.
3855 m_lastSyntheticPanLocation = adoptPtr(new IntPoint(position));
3858 case PlatformEvent::MouseReleased:
3859 // There was a drag -> gesture.
3860 swallowEvent = !!m_lastSyntheticPanLocation;
3861 m_lastSyntheticPanLocation.clear();
3867 return swallowEvent;
3870 bool EventHandler::handleWheelEventAsEmulatedGesture(const PlatformWheelEvent& event)
3872 if (!m_frame || !m_frame->settings() || !m_frame->settings()->touchEventEmulationEnabled())
3875 // Only convert vertical wheel w/ shift into pinch for touch-enabled device convenience.
3876 if (!event.shiftKey() || !event.deltaY())
3879 Page* page = m_frame->page();
3880 FrameView* view = m_frame->view();
3881 float pageScaleFactor = page->pageScaleFactor();
3882 IntPoint anchorBeforeCss(view->scrollPosition() + event.position());
3883 IntPoint anchorBeforeDip(event.position());
3884 anchorBeforeDip.scale(pageScaleFactor, pageScaleFactor);
3886 float magnifyDelta = exp(event.deltaY() * 0.002f);
3887 float newPageScaleFactor = pageScaleFactor * magnifyDelta;
3889 IntPoint anchorAfterCss(anchorBeforeDip);
3890 anchorAfterCss.scale(1.f / newPageScaleFactor, 1.f / newPageScaleFactor);
3891 page->inspectorController().requestPageScaleFactor(newPageScaleFactor, anchorBeforeCss - toIntSize(anchorAfterCss));
3895 TouchAction EventHandler::intersectTouchAction(TouchAction action1, TouchAction action2)
3897 if (action1 == TouchActionNone || action2 == TouchActionNone)
3898 return TouchActionNone;
3899 if (action1 == TouchActionAuto)
3901 if (action2 == TouchActionAuto)
3903 if (!(action1 & action2))
3904 return TouchActionNone;
3905 return action1 & action2;
3908 TouchAction EventHandler::computeEffectiveTouchAction(const Node& node)
3910 // Optimization to minimize risk of this new feature (behavior should be identical
3911 // since there's no way to get non-default touch-action values).
3912 if (!RuntimeEnabledFeatures::cssTouchActionEnabled())
3913 return TouchActionAuto;
3915 // Start by permitting all actions, then walk the block level elements from
3916 // the target node up to the nearest scrollable ancestor and exclude any
3917 // prohibited actions. For now this is trivial, but when we add more types
3918 // of actions it'll get a little more complex.
3919 TouchAction effectiveTouchAction = TouchActionAuto;
3920 for (const Node* curNode = &node; curNode; curNode = NodeRenderingTraversal::parent(curNode)) {
3921 // The spec says only block and SVG elements get touch-action.
3922 // FIXME(rbyers): Add correct support for SVG, crbug.com/247396.
3923 if (RenderObject* renderer = curNode->renderer()) {
3924 if (renderer->isRenderBlockFlow()) {
3925 TouchAction action = renderer->style()->touchAction();
3926 effectiveTouchAction = intersectTouchAction(action, effectiveTouchAction);
3927 if (effectiveTouchAction == TouchActionNone)
3931 // If we've reached an ancestor that supports a touch action, search no further.
3932 if (renderer->isBox() && toRenderBox(renderer)->scrollsOverflow())
3936 return effectiveTouchAction;
3939 void EventHandler::setLastKnownMousePosition(const PlatformMouseEvent& event)
3941 m_mousePositionIsUnknown = false;
3942 m_lastKnownMousePosition = event.position();
3943 m_lastKnownMouseGlobalPosition = event.globalPosition();
3946 bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
3948 // If we're clicking into a frame that is selected, the frame will appear
3949 // greyed out even though we're clicking on the selection. This looks
3950 // really strange (having the whole frame be greyed out), so we deselect the
3952 IntPoint p = m_frame->view()->windowToContents(mev.event().position());
3953 if (m_frame->selection().contains(p)) {
3954 VisiblePosition visiblePos(
3955 mev.targetNode()->renderer()->positionForPoint(mev.localPoint()));
3956 VisibleSelection newSelection(visiblePos);
3957 m_frame->selection().setSelection(newSelection);
3960 subframe->eventHandler().handleMousePressEvent(mev.event());
3964 bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode)
3966 if (m_mouseDownMayStartDrag && !m_mouseDownWasInSubframe)
3968 subframe->eventHandler().handleMouseMoveOrLeaveEvent(mev.event(), hoveredNode);
3972 bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
3974 subframe->eventHandler().handleMouseReleaseEvent(mev.event());
3978 bool EventHandler::passWheelEventToWidget(const PlatformWheelEvent& wheelEvent, Widget* widget)
3980 // We can sometimes get a null widget! EventHandlerMac handles a null
3981 // widget by returning false, so we do the same.
3985 // If not a FrameView, then probably a plugin widget. Those will receive
3986 // the event via an EventTargetNode dispatch when this returns false.
3987 if (!widget->isFrameView())
3990 return toFrameView(widget)->frame().eventHandler().handleWheelEvent(wheelEvent);
3993 bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event)
3995 // Figure out which view to send the event to.
3996 if (!event.targetNode() || !event.targetNode()->renderer() || !event.targetNode()->renderer()->isWidget())
4001 PassRefPtr<Clipboard> EventHandler::createDraggingClipboard() const
4003 return Clipboard::create(Clipboard::DragAndDrop, ClipboardWritable, DataObject::create());
4006 void EventHandler::focusDocumentView()
4008 Page* page = m_frame->page();
4011 page->focusController().setFocusedFrame(m_frame);
4014 unsigned EventHandler::accessKeyModifiers()
4017 return PlatformEvent::CtrlKey | PlatformEvent::AltKey;
4019 return PlatformEvent::AltKey;
4023 } // namespace WebCore