1 /*
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)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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.
14 *
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.
26 */
27
28 #include "config.h"
29 #include "core/page/EventHandler.h"
30
31 #include "HTMLNames.h"
32 #include "RuntimeEnabledFeatures.h"
33 #include "SVGNames.h"
34 #include "bindings/v8/ExceptionStatePlaceholder.h"
35 #include "core/dom/Clipboard.h"
36 #include "core/dom/Document.h"
37 #include "core/dom/DocumentMarkerController.h"
38 #include "core/dom/FullscreenElementStack.h"
39 #include "core/dom/NodeRenderingTraversal.h"
40 #include "core/dom/TouchList.h"
41 #include "core/dom/shadow/ShadowRoot.h"
42 #include "core/editing/Editor.h"
43 #include "core/editing/FrameSelection.h"
44 #include "core/editing/TextIterator.h"
45 #include "core/editing/htmlediting.h"
46 #include "core/events/DOMWindowEventQueue.h"
47 #include "core/events/EventPath.h"
48 #include "core/events/KeyboardEvent.h"
49 #include "core/events/MouseEvent.h"
50 #include "core/events/TextEvent.h"
51 #include "core/events/ThreadLocalEventNames.h"
52 #include "core/events/TouchEvent.h"
53 #include "core/events/WheelEvent.h"
54 #include "core/fetch/ImageResource.h"
55 #include "core/html/HTMLDialogElement.h"
56 #include "core/html/HTMLFrameElementBase.h"
57 #include "core/html/HTMLFrameSetElement.h"
58 #include "core/html/HTMLInputElement.h"
59 #include "core/loader/FrameLoader.h"
60 #include "core/loader/FrameLoaderClient.h"
61 #include "core/page/AutoscrollController.h"
62 #include "core/page/BackForwardClient.h"
63 #include "core/page/Chrome.h"
64 #include "core/page/ChromeClient.h"
65 #include "core/page/DragController.h"
66 #include "core/page/DragState.h"
67 #include "core/page/EditorClient.h"
68 #include "core/page/FocusController.h"
69 #include "core/frame/Frame.h"
70 #include "core/page/FrameTree.h"
71 #include "core/frame/FrameView.h"
72 #include "core/inspector/InspectorController.h"
73 #include "core/page/MouseEventWithHitTestResults.h"
74 #include "core/page/Page.h"
75 #include "core/frame/Settings.h"
76 #include "core/page/SpatialNavigation.h"
77 #include "core/page/TouchAdjustment.h"
78 #include "core/platform/chromium/ChromiumDataObject.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"
105
106 namespace WebCore {
107
108 using namespace HTMLNames;
109 using namespace SVGNames;
110
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;
118
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;
124
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;
128
129 static const int maximumCursorSize = 128;
130
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;
135
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;
139
140 #if OS(MACOSX)
141 static const double TextDragDelay = 0.15;
142 #else
143 static const double TextDragDelay = 0.0;
144 #endif
145
146 enum NoCursorChangeType { NoCursorChange };
147
148 class OptionalCursor {
149 public:
OptionalCursor(NoCursorChangeType)150 OptionalCursor(NoCursorChangeType) : m_isCursorChange(false) { }
OptionalCursor(const Cursor & cursor)151 OptionalCursor(const Cursor& cursor) : m_isCursorChange(true), m_cursor(cursor) { }
152
isCursorChange() const153 bool isCursorChange() const { return m_isCursorChange; }
cursor() const154 const Cursor& cursor() const { ASSERT(m_isCursorChange); return m_cursor; }
155
156 private:
157 bool m_isCursorChange;
158 Cursor m_cursor;
159 };
160
161 class MaximumDurationTracker {
162 public:
MaximumDurationTracker(double * maxDuration)163 explicit MaximumDurationTracker(double *maxDuration)
164 : m_maxDuration(maxDuration)
165 , m_start(monotonicallyIncreasingTime())
166 {
167 }
168
~MaximumDurationTracker()169 ~MaximumDurationTracker()
170 {
171 *m_maxDuration = max(*m_maxDuration, monotonicallyIncreasingTime() - m_start);
172 }
173
174 private:
175 double* m_maxDuration;
176 double m_start;
177 };
178
179 class SyntheticTouchPoint : public PlatformTouchPoint {
180 public:
181
182 // The default values are based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
SyntheticTouchPoint(const PlatformMouseEvent & event)183 explicit SyntheticTouchPoint(const PlatformMouseEvent& event)
184 {
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;
190
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;
198
199 PlatformEvent::Type type = event.type();
200 ASSERT(type == PlatformEvent::MouseMoved || type == PlatformEvent::MousePressed || type == PlatformEvent::MouseReleased);
201
202 switch (type) {
203 case PlatformEvent::MouseMoved:
204 m_state = TouchMoved;
205 break;
206 case PlatformEvent::MousePressed:
207 m_state = TouchPressed;
208 break;
209 case PlatformEvent::MouseReleased:
210 m_state = TouchReleased;
211 break;
212 default:
213 ASSERT_NOT_REACHED();
214 break;
215 }
216 }
217 };
218
219 class SyntheticSingleTouchEvent : public PlatformTouchEvent {
220 public:
SyntheticSingleTouchEvent(const PlatformMouseEvent & event)221 explicit SyntheticSingleTouchEvent(const PlatformMouseEvent& event)
222 {
223 switch (event.type()) {
224 case PlatformEvent::MouseMoved:
225 m_type = TouchMove;
226 break;
227 case PlatformEvent::MousePressed:
228 m_type = TouchStart;
229 break;
230 case PlatformEvent::MouseReleased:
231 m_type = TouchEnd;
232 break;
233 default:
234 ASSERT_NOT_REACHED();
235 m_type = NoType;
236 break;
237 }
238 m_timestamp = event.timestamp();
239 m_modifiers = event.modifiers();
240 m_touchPoints.append(SyntheticTouchPoint(event));
241 }
242 };
243
wheelGranularityToScrollGranularity(unsigned deltaMode)244 static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned deltaMode)
245 {
246 switch (deltaMode) {
247 case WheelEvent::DOM_DELTA_PAGE:
248 return ScrollByPage;
249 case WheelEvent::DOM_DELTA_LINE:
250 return ScrollByLine;
251 case WheelEvent::DOM_DELTA_PIXEL:
252 return ScrollByPixel;
253 default:
254 return ScrollByPixel;
255 }
256 }
257
scrollNode(float delta,ScrollGranularity granularity,ScrollDirection direction,Node * node,Node ** stopNode,IntPoint absolutePoint=IntPoint ())258 static inline bool scrollNode(float delta, ScrollGranularity granularity, ScrollDirection direction, Node* node, Node** stopNode, IntPoint absolutePoint = IntPoint())
259 {
260 if (!delta)
261 return false;
262 if (!node->renderer())
263 return false;
264
265 RenderBox* curBox = node->renderer()->enclosingBox();
266
267 while (curBox && !curBox->isRenderView()) {
268 ScrollDirection physicalDirection = toPhysicalDirection(
269 direction, curBox->isHorizontalWritingMode(), curBox->style()->isFlippedBlocksWritingMode());
270
271 if (curBox->scroll(physicalDirection, granularity, delta)) {
272 if (stopNode)
273 *stopNode = curBox->node();
274 return true;
275 }
276
277 if (stopNode && *stopNode && curBox->node() == *stopNode)
278 return true;
279
280 // FIXME: This should probably move to a virtual method on RenderBox, something like RenderBox::scrollAncestor, and specialized for RenderFlowThread
281 curBox = curBox->containingBlock();
282 if (curBox && curBox->isRenderNamedFlowThread()) {
283 RenderBox* flowedBox = curBox;
284
285 if (RenderBox* startBox = node->renderBox())
286 flowedBox = startBox;
287
288 curBox = toRenderFlowThread(curBox)->regionFromAbsolutePointAndBox(absolutePoint, flowedBox);
289 }
290 }
291
292 return false;
293 }
294
295 // Refetch the event target node if it is removed or currently is the shadow node inside an <input> element.
296 // If a mouse event handler changes the input element type to one that has a widget associated,
297 // we'd like to EventHandler::handleMousePressEvent to pass the event to the widget and thus the
298 // event target node can't still be the shadow node.
shouldRefetchEventTarget(const MouseEventWithHitTestResults & mev)299 static inline bool shouldRefetchEventTarget(const MouseEventWithHitTestResults& mev)
300 {
301 Node* targetNode = mev.targetNode();
302 if (!targetNode || !targetNode->parentNode())
303 return true;
304 return targetNode->isShadowRoot() && toShadowRoot(targetNode)->host()->hasTagName(inputTag);
305 }
306
EventHandler(Frame * frame)307 EventHandler::EventHandler(Frame* frame)
308 : m_frame(frame)
309 , m_mousePressed(false)
310 , m_capturesDragging(false)
311 , m_mouseDownMayStartSelect(false)
312 , m_mouseDownMayStartDrag(false)
313 , m_mouseDownWasSingleClickInSelection(false)
314 , m_selectionInitiationState(HaveNotStartedSelection)
315 , m_panScrollButtonPressed(false)
316 , m_hoverTimer(this, &EventHandler::hoverTimerFired)
317 , m_cursorUpdateTimer(this, &EventHandler::cursorUpdateTimerFired)
318 , m_mouseDownMayStartAutoscroll(false)
319 , m_mouseDownWasInSubframe(false)
320 , m_fakeMouseMoveEventTimer(this, &EventHandler::fakeMouseMoveEventTimerFired)
321 , m_svgPan(false)
322 , m_resizeScrollableArea(0)
323 , m_eventHandlerWillResetCapturingMouseEventsNode(0)
324 , m_clickCount(0)
325 , m_shouldOnlyFireDragOverEvent(false)
326 , m_mousePositionIsUnknown(true)
327 , m_mouseDownTimestamp(0)
328 , m_widgetIsLatched(false)
329 , m_originatingTouchPointTargetKey(0)
330 , m_touchPressed(false)
331 , m_scrollGestureHandlingNode(0)
332 , m_lastHitTestResultOverWidget(false)
333 , m_maxMouseMovedDuration(0)
334 , m_baseEventType(PlatformEvent::NoType)
335 , m_didStartDrag(false)
336 , m_longTapShouldInvokeContextMenu(false)
337 , m_syntheticPageScaleFactor(0)
338 , m_activeIntervalTimer(this, &EventHandler::activeIntervalTimerFired)
339 , m_lastShowPressTimestamp(0)
340 {
341 }
342
~EventHandler()343 EventHandler::~EventHandler()
344 {
345 ASSERT(!m_fakeMouseMoveEventTimer.isActive());
346 }
347
dragState()348 DragState& EventHandler::dragState()
349 {
350 DEFINE_STATIC_LOCAL(DragState, state, ());
351 return state;
352 }
353
clear()354 void EventHandler::clear()
355 {
356 m_hoverTimer.stop();
357 m_cursorUpdateTimer.stop();
358 m_fakeMouseMoveEventTimer.stop();
359 m_activeIntervalTimer.stop();
360 m_resizeScrollableArea = 0;
361 m_nodeUnderMouse = 0;
362 m_lastNodeUnderMouse = 0;
363 m_instanceUnderMouse = 0;
364 m_lastInstanceUnderMouse = 0;
365 m_lastMouseMoveEventSubframe = 0;
366 m_lastScrollbarUnderMouse = 0;
367 m_clickCount = 0;
368 m_clickNode = 0;
369 m_frameSetBeingResized = 0;
370 m_dragTarget = 0;
371 m_shouldOnlyFireDragOverEvent = false;
372 m_mousePositionIsUnknown = true;
373 m_lastKnownMousePosition = IntPoint();
374 m_lastKnownMouseGlobalPosition = IntPoint();
375 m_lastMouseDownUserGestureToken.clear();
376 m_mousePressNode = 0;
377 m_mousePressed = false;
378 m_capturesDragging = false;
379 m_capturingMouseEventsNode = 0;
380 m_latchedWheelEventNode = 0;
381 m_previousWheelScrolledNode = 0;
382 m_originatingTouchPointTargets.clear();
383 m_originatingTouchPointDocument.clear();
384 m_originatingTouchPointTargetKey = 0;
385 m_scrollGestureHandlingNode = 0;
386 m_lastHitTestResultOverWidget = false;
387 m_previousGestureScrolledNode = 0;
388 m_scrollbarHandlingScrollGesture = 0;
389 m_maxMouseMovedDuration = 0;
390 m_baseEventType = PlatformEvent::NoType;
391 m_didStartDrag = false;
392 m_touchPressed = false;
393 m_mouseDownMayStartSelect = false;
394 m_mouseDownMayStartDrag = false;
395 m_lastShowPressTimestamp = 0;
396 m_lastDeferredTapElement = 0;
397 }
398
nodeWillBeRemoved(Node & nodeToBeRemoved)399 void EventHandler::nodeWillBeRemoved(Node& nodeToBeRemoved)
400 {
401 if (!nodeToBeRemoved.containsIncludingShadowDOM(m_clickNode.get()))
402 return;
403 if (nodeToBeRemoved.isInShadowTree()) {
404 m_clickNode = nodeToBeRemoved.parentOrShadowHostNode();
405 } else {
406 // We don't dispatch click events if the mousedown node is removed
407 // before a mouseup event. It is compatible with IE and Firefox.
408 m_clickNode = 0;
409 }
410 }
411
setSelectionIfNeeded(FrameSelection & selection,const VisibleSelection & newSelection)412 static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& newSelection)
413 {
414 if (selection.selection() != newSelection)
415 selection.setSelection(newSelection);
416 }
417
dispatchSelectStart(Node * node)418 static inline bool dispatchSelectStart(Node* node)
419 {
420 if (!node || !node->renderer())
421 return true;
422
423 return node->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart));
424 }
425
expandSelectionToRespectUserSelectAll(Node * targetNode,const VisibleSelection & selection)426 static VisibleSelection expandSelectionToRespectUserSelectAll(Node* targetNode, const VisibleSelection& selection)
427 {
428 Node* rootUserSelectAll = Position::rootUserSelectAllForNode(targetNode);
429 if (!rootUserSelectAll)
430 return selection;
431
432 VisibleSelection newSelection(selection);
433 newSelection.setBase(positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary));
434 newSelection.setExtent(positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary));
435
436 return newSelection;
437 }
438
updateSelectionForMouseDownDispatchingSelectStart(Node * targetNode,const VisibleSelection & selection,TextGranularity granularity)439 bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& selection, TextGranularity granularity)
440 {
441 if (Position::nodeIsUserSelectNone(targetNode))
442 return false;
443
444 if (!dispatchSelectStart(targetNode))
445 return false;
446
447 if (selection.isRange())
448 m_selectionInitiationState = ExtendedSelection;
449 else {
450 granularity = CharacterGranularity;
451 m_selectionInitiationState = PlacedCaret;
452 }
453
454 m_frame->selection().setNonDirectionalSelectionIfNeeded(selection, granularity);
455
456 return true;
457 }
458
selectClosestWordFromHitTestResult(const HitTestResult & result,AppendTrailingWhitespace appendTrailingWhitespace)459 void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& result, AppendTrailingWhitespace appendTrailingWhitespace)
460 {
461 Node* innerNode = result.targetNode();
462 VisibleSelection newSelection;
463
464 if (innerNode && innerNode->renderer()) {
465 VisiblePosition pos(innerNode->renderer()->positionForPoint(result.localPoint()));
466 if (pos.isNotNull()) {
467 newSelection = VisibleSelection(pos);
468 newSelection.expandUsingGranularity(WordGranularity);
469 }
470
471 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
472 newSelection.appendTrailingWhitespace();
473
474 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
475 }
476 }
477
selectClosestMisspellingFromHitTestResult(const HitTestResult & result,AppendTrailingWhitespace appendTrailingWhitespace)478 void EventHandler::selectClosestMisspellingFromHitTestResult(const HitTestResult& result, AppendTrailingWhitespace appendTrailingWhitespace)
479 {
480 Node* innerNode = result.targetNode();
481 VisibleSelection newSelection;
482
483 if (innerNode && innerNode->renderer()) {
484 VisiblePosition pos(innerNode->renderer()->positionForPoint(result.localPoint()));
485 Position start = pos.deepEquivalent();
486 Position end = pos.deepEquivalent();
487 if (pos.isNotNull()) {
488 Vector<DocumentMarker*> markers = innerNode->document().markers()->markersInRange(makeRange(pos, pos).get(), DocumentMarker::MisspellingMarkers());
489 if (markers.size() == 1) {
490 start.moveToOffset(markers[0]->startOffset());
491 end.moveToOffset(markers[0]->endOffset());
492 newSelection = VisibleSelection(start, end);
493 }
494 }
495
496 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
497 newSelection.appendTrailingWhitespace();
498
499 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
500 }
501 }
502
selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults & result)503 void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& result)
504 {
505 if (m_mouseDownMayStartSelect) {
506 selectClosestWordFromHitTestResult(result.hitTestResult(),
507 (result.event().clickCount() == 2 && m_frame->editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace);
508 }
509 }
510
selectClosestMisspellingFromMouseEvent(const MouseEventWithHitTestResults & result)511 void EventHandler::selectClosestMisspellingFromMouseEvent(const MouseEventWithHitTestResults& result)
512 {
513 if (m_mouseDownMayStartSelect) {
514 selectClosestMisspellingFromHitTestResult(result.hitTestResult(),
515 (result.event().clickCount() == 2 && m_frame->editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace);
516 }
517 }
518
selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults & result)519 void EventHandler::selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result)
520 {
521 if (!result.hitTestResult().isLiveLink())
522 return selectClosestWordFromMouseEvent(result);
523
524 Node* innerNode = result.targetNode();
525
526 if (innerNode && innerNode->renderer() && m_mouseDownMayStartSelect) {
527 VisibleSelection newSelection;
528 Element* URLElement = result.hitTestResult().URLElement();
529 VisiblePosition pos(innerNode->renderer()->positionForPoint(result.localPoint()));
530 if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(URLElement))
531 newSelection = VisibleSelection::selectionFromContentsOfNode(URLElement);
532
533 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
534 }
535 }
536
handleMousePressEventDoubleClick(const MouseEventWithHitTestResults & event)537 bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestResults& event)
538 {
539 if (event.event().button() != LeftButton)
540 return false;
541
542 if (m_frame->selection().isRange()) {
543 // A double-click when range is already selected
544 // should not change the selection. So, do not call
545 // selectClosestWordFromMouseEvent, but do set
546 // m_beganSelectingText to prevent handleMouseReleaseEvent
547 // from setting caret selection.
548 m_selectionInitiationState = ExtendedSelection;
549 } else {
550 selectClosestWordFromMouseEvent(event);
551 }
552 return true;
553 }
554
handleMousePressEventTripleClick(const MouseEventWithHitTestResults & event)555 bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestResults& event)
556 {
557 if (event.event().button() != LeftButton)
558 return false;
559
560 Node* innerNode = event.targetNode();
561 if (!(innerNode && innerNode->renderer() && m_mouseDownMayStartSelect))
562 return false;
563
564 VisibleSelection newSelection;
565 VisiblePosition pos(innerNode->renderer()->positionForPoint(event.localPoint()));
566 if (pos.isNotNull()) {
567 newSelection = VisibleSelection(pos);
568 newSelection.expandUsingGranularity(ParagraphGranularity);
569 }
570
571 return updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), ParagraphGranularity);
572 }
573
textDistance(const Position & start,const Position & end)574 static int textDistance(const Position& start, const Position& end)
575 {
576 RefPtr<Range> range = Range::create(*start.document(), start, end);
577 return TextIterator::rangeLength(range.get(), true);
578 }
579
handleMousePressEventSingleClick(const MouseEventWithHitTestResults & event)580 bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestResults& event)
581 {
582 m_frame->document()->updateLayoutIgnorePendingStylesheets();
583 Node* innerNode = event.targetNode();
584 if (!(innerNode && innerNode->renderer() && m_mouseDownMayStartSelect))
585 return false;
586
587 // Extend the selection if the Shift key is down, unless the click is in a link.
588 bool extendSelection = event.event().shiftKey() && !event.isOverLink();
589
590 // Don't restart the selection when the mouse is pressed on an
591 // existing selection so we can allow for text dragging.
592 if (FrameView* view = m_frame->view()) {
593 LayoutPoint vPoint = view->windowToContents(event.event().position());
594 if (!extendSelection && m_frame->selection().contains(vPoint)) {
595 m_mouseDownWasSingleClickInSelection = true;
596 return false;
597 }
598 }
599
600 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(event.localPoint()));
601 if (visiblePos.isNull())
602 visiblePos = VisiblePosition(firstPositionInOrBeforeNode(innerNode), DOWNSTREAM);
603 Position pos = visiblePos.deepEquivalent();
604
605 VisibleSelection newSelection = m_frame->selection().selection();
606 TextGranularity granularity = CharacterGranularity;
607
608 if (extendSelection && newSelection.isCaretOrRange()) {
609 VisibleSelection selectionInUserSelectAll = expandSelectionToRespectUserSelectAll(innerNode, VisibleSelection(pos));
610 if (selectionInUserSelectAll.isRange()) {
611 if (comparePositions(selectionInUserSelectAll.start(), newSelection.start()) < 0)
612 pos = selectionInUserSelectAll.start();
613 else if (comparePositions(newSelection.end(), selectionInUserSelectAll.end()) < 0)
614 pos = selectionInUserSelectAll.end();
615 }
616
617 if (!m_frame->editor().behavior().shouldConsiderSelectionAsDirectional()) {
618 // See <rdar://problem/3668157> REGRESSION (Mail): shift-click deselects when selection
619 // was created right-to-left
620 Position start = newSelection.start();
621 Position end = newSelection.end();
622 int distanceToStart = textDistance(start, pos);
623 int distanceToEnd = textDistance(pos, end);
624 if (distanceToStart <= distanceToEnd)
625 newSelection = VisibleSelection(end, pos);
626 else
627 newSelection = VisibleSelection(start, pos);
628 } else
629 newSelection.setExtent(pos);
630
631 if (m_frame->selection().granularity() != CharacterGranularity) {
632 granularity = m_frame->selection().granularity();
633 newSelection.expandUsingGranularity(m_frame->selection().granularity());
634 }
635 } else
636 newSelection = expandSelectionToRespectUserSelectAll(innerNode, visiblePos);
637
638 bool handled = updateSelectionForMouseDownDispatchingSelectStart(innerNode, newSelection, granularity);
639 return handled;
640 }
641
canMouseDownStartSelect(Node * node)642 static inline bool canMouseDownStartSelect(Node* node)
643 {
644 if (!node || !node->renderer())
645 return true;
646
647 if (!node->canStartSelection())
648 return false;
649
650 return true;
651 }
652
handleMousePressEvent(const MouseEventWithHitTestResults & event)653 bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& event)
654 {
655 // Reset drag state.
656 dragState().m_dragSrc = 0;
657
658 cancelFakeMouseMoveEvent();
659
660 m_frame->document()->updateLayoutIgnorePendingStylesheets();
661
662 if (ScrollView* scrollView = m_frame->view()) {
663 if (scrollView->isPointInScrollbarCorner(event.event().position()))
664 return false;
665 }
666
667 bool singleClick = event.event().clickCount() <= 1;
668
669 // If we got the event back, that must mean it wasn't prevented,
670 // so it's allowed to start a drag or selection if it wasn't in a scrollbar.
671 m_mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode()) && !event.scrollbar();
672
673 m_mouseDownMayStartDrag = singleClick;
674
675 m_mouseDownWasSingleClickInSelection = false;
676
677 m_mouseDown = event.event();
678
679 if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event))
680 return true;
681
682 if (m_frame->document()->isSVGDocument()
683 && toSVGDocument(m_frame->document())->zoomAndPanEnabled()) {
684 if (event.event().shiftKey() && singleClick) {
685 m_svgPan = true;
686 toSVGDocument(m_frame->document())->startPan(m_frame->view()->windowToContents(event.event().position()));
687 return true;
688 }
689 }
690
691 // We don't do this at the start of mouse down handling,
692 // because we don't want to do it until we know we didn't hit a widget.
693 if (singleClick)
694 focusDocumentView();
695
696 Node* innerNode = event.targetNode();
697
698 m_mousePressNode = innerNode;
699 m_dragStartPos = event.event().position();
700
701 bool swallowEvent = false;
702 m_mousePressed = true;
703 m_selectionInitiationState = HaveNotStartedSelection;
704
705 if (event.event().clickCount() == 2)
706 swallowEvent = handleMousePressEventDoubleClick(event);
707 else if (event.event().clickCount() >= 3)
708 swallowEvent = handleMousePressEventTripleClick(event);
709 else
710 swallowEvent = handleMousePressEventSingleClick(event);
711
712 m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect
713 || (m_mousePressNode && m_mousePressNode->renderBox() && m_mousePressNode->renderBox()->canBeProgramaticallyScrolled());
714
715 return swallowEvent;
716 }
717
handleMouseDraggedEvent(const MouseEventWithHitTestResults & event)718 bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event)
719 {
720 if (!m_mousePressed)
721 return false;
722
723 if (handleDrag(event, ShouldCheckDragHysteresis))
724 return true;
725
726 Node* targetNode = event.targetNode();
727 if (event.event().button() != LeftButton || !targetNode)
728 return false;
729
730 RenderObject* renderer = targetNode->renderer();
731 if (!renderer) {
732 Node* parent = EventPath::parent(targetNode);
733 if (!parent)
734 return false;
735
736 renderer = parent->renderer();
737 if (!renderer || !renderer->isListBox())
738 return false;
739 }
740
741 m_mouseDownMayStartDrag = false;
742
743 if (m_mouseDownMayStartAutoscroll && !panScrollInProgress()) {
744 if (AutoscrollController* controller = autoscrollController()) {
745 controller->startAutoscrollForSelection(renderer);
746 m_mouseDownMayStartAutoscroll = false;
747 }
748 }
749
750 if (m_selectionInitiationState != ExtendedSelection) {
751 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
752 HitTestResult result(m_mouseDownPos);
753 m_frame->document()->renderView()->hitTest(request, result);
754
755 updateSelectionForMouseDrag(result);
756 }
757 updateSelectionForMouseDrag(event.hitTestResult());
758 return true;
759 }
760
updateSelectionForMouseDrag()761 void EventHandler::updateSelectionForMouseDrag()
762 {
763 FrameView* view = m_frame->view();
764 if (!view)
765 return;
766 RenderView* renderer = m_frame->contentRenderer();
767 if (!renderer)
768 return;
769
770 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
771 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
772 renderer->hitTest(request, result);
773 updateSelectionForMouseDrag(result);
774 }
775
updateSelectionForMouseDrag(const HitTestResult & hitTestResult)776 void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResult)
777 {
778 if (!m_mouseDownMayStartSelect)
779 return;
780
781 Node* target = hitTestResult.targetNode();
782 if (!target)
783 return;
784
785 VisiblePosition targetPosition = m_frame->selection().selection().visiblePositionRespectingEditingBoundary(hitTestResult.localPoint(), target);
786 // Don't modify the selection if we're not on a node.
787 if (targetPosition.isNull())
788 return;
789
790 // Restart the selection if this is the first mouse move. This work is usually
791 // done in handleMousePressEvent, but not if the mouse press was on an existing selection.
792 VisibleSelection newSelection = m_frame->selection().selection();
793
794 // Special case to limit selection to the containing block for SVG text.
795 // FIXME: Isn't there a better non-SVG-specific way to do this?
796 if (Node* selectionBaseNode = newSelection.base().deprecatedNode())
797 if (RenderObject* selectionBaseRenderer = selectionBaseNode->renderer())
798 if (selectionBaseRenderer->isSVGText())
799 if (target->renderer()->containingBlock() != selectionBaseRenderer->containingBlock())
800 return;
801
802 if (m_selectionInitiationState == HaveNotStartedSelection && !dispatchSelectStart(target))
803 return;
804
805 if (m_selectionInitiationState != ExtendedSelection) {
806 // Always extend selection here because it's caused by a mouse drag
807 m_selectionInitiationState = ExtendedSelection;
808 newSelection = VisibleSelection(targetPosition);
809 }
810
811 if (RuntimeEnabledFeatures::userSelectAllEnabled()) {
812 Node* rootUserSelectAllForMousePressNode = Position::rootUserSelectAllForNode(m_mousePressNode.get());
813 if (rootUserSelectAllForMousePressNode && rootUserSelectAllForMousePressNode == Position::rootUserSelectAllForNode(target)) {
814 newSelection.setBase(positionBeforeNode(rootUserSelectAllForMousePressNode).upstream(CanCrossEditingBoundary));
815 newSelection.setExtent(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary));
816 } else {
817 // Reset base for user select all when base is inside user-select-all area and extent < base.
818 if (rootUserSelectAllForMousePressNode && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint()), m_mousePressNode->renderer()->positionForPoint(m_dragStartPos)) < 0)
819 newSelection.setBase(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary));
820
821 Node* rootUserSelectAllForTarget = Position::rootUserSelectAllForNode(target);
822 if (rootUserSelectAllForTarget && m_mousePressNode->renderer() && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint()), m_mousePressNode->renderer()->positionForPoint(m_dragStartPos)) < 0)
823 newSelection.setExtent(positionBeforeNode(rootUserSelectAllForTarget).upstream(CanCrossEditingBoundary));
824 else if (rootUserSelectAllForTarget && m_mousePressNode->renderer())
825 newSelection.setExtent(positionAfterNode(rootUserSelectAllForTarget).downstream(CanCrossEditingBoundary));
826 else
827 newSelection.setExtent(targetPosition);
828 }
829 } else {
830 newSelection.setExtent(targetPosition);
831 }
832
833 if (m_frame->selection().granularity() != CharacterGranularity)
834 newSelection.expandUsingGranularity(m_frame->selection().granularity());
835
836 m_frame->selection().setNonDirectionalSelectionIfNeeded(newSelection, m_frame->selection().granularity(),
837 FrameSelection::AdjustEndpointsAtBidiBoundary);
838 }
839
handleMouseReleaseEvent(const MouseEventWithHitTestResults & event)840 bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
841 {
842 AutoscrollController* controller = autoscrollController();
843 if (controller && controller->autoscrollInProgress())
844 stopAutoscroll();
845
846 // Used to prevent mouseMoveEvent from initiating a drag before
847 // the mouse is pressed again.
848 m_mousePressed = false;
849 m_capturesDragging = false;
850 m_mouseDownMayStartDrag = false;
851 m_mouseDownMayStartSelect = false;
852 m_mouseDownMayStartAutoscroll = false;
853 m_mouseDownWasInSubframe = false;
854
855 bool handled = false;
856
857 // Clear the selection if the mouse didn't move after the last mouse
858 // press and it's not a context menu click. We do this so when clicking
859 // on the selection, the selection goes away. However, if we are
860 // editing, place the caret.
861 if (m_mouseDownWasSingleClickInSelection && m_selectionInitiationState != ExtendedSelection
862 && m_dragStartPos == event.event().position()
863 && m_frame->selection().isRange()
864 && event.event().button() != RightButton) {
865 VisibleSelection newSelection;
866 Node* node = event.targetNode();
867 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
868 if (node && node->renderer() && (caretBrowsing || node->rendererIsEditable())) {
869 VisiblePosition pos = VisiblePosition(node->renderer()->positionForPoint(event.localPoint()));
870 newSelection = VisibleSelection(pos);
871 }
872
873 setSelectionIfNeeded(m_frame->selection(), newSelection);
874
875 handled = true;
876 }
877
878 m_frame->selection().notifyRendererOfSelectionChange(UserTriggered);
879
880 m_frame->selection().selectFrameElementInParentIfFullySelected();
881
882 if (event.event().button() == MiddleButton && !event.isOverLink()) {
883 // Ignore handled, since we want to paste to where the caret was placed anyway.
884 handled = handlePasteGlobalSelection(event.event()) || handled;
885 }
886
887 return handled;
888 }
889
890 #if OS(WIN)
891
startPanScrolling(RenderObject * renderer)892 void EventHandler::startPanScrolling(RenderObject* renderer)
893 {
894 if (!renderer->isBox())
895 return;
896 AutoscrollController* controller = autoscrollController();
897 if (!controller)
898 return;
899 controller->startPanScrolling(toRenderBox(renderer), lastKnownMousePosition());
900 invalidateClick();
901 }
902
903 #endif // OS(WIN)
904
autoscrollController() const905 AutoscrollController* EventHandler::autoscrollController() const
906 {
907 if (Page* page = m_frame->page())
908 return &page->autoscrollController();
909 return 0;
910 }
911
panScrollInProgress() const912 bool EventHandler::panScrollInProgress() const
913 {
914 return autoscrollController() && autoscrollController()->panScrollInProgress();
915 }
916
hitTestResultAtPoint(const LayoutPoint & point,HitTestRequest::HitTestRequestType hitType,const LayoutSize & padding)917 HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding)
918 {
919 // We always send hitTestResultAtPoint to the main frame if we have one,
920 // otherwise we might hit areas that are obscured by higher frames.
921 if (Page* page = m_frame->page()) {
922 Frame* mainFrame = page->mainFrame();
923 if (m_frame != mainFrame) {
924 FrameView* frameView = m_frame->view();
925 FrameView* mainView = mainFrame->view();
926 if (frameView && mainView) {
927 IntPoint mainFramePoint = mainView->rootViewToContents(frameView->contentsToRootView(roundedIntPoint(point)));
928 return mainFrame->eventHandler().hitTestResultAtPoint(mainFramePoint, hitType, padding);
929 }
930 }
931 }
932
933 HitTestResult result(point, padding.height(), padding.width(), padding.height(), padding.width());
934
935 // RenderView::hitTest causes a layout, and we don't want to hit that until the first
936 // layout because until then, there is nothing shown on the screen - the user can't
937 // have intentionally clicked on something belonging to this page. Furthermore,
938 // mousemove events before the first layout should not lead to a premature layout()
939 // happening, which could show a flash of white.
940 // See also the similar code in Document::prepareMouseEvent.
941 if (!m_frame->contentRenderer() || !m_frame->view() || !m_frame->view()->didFirstLayout())
942 return result;
943
944 // hitTestResultAtPoint is specifically used to hitTest into all frames, thus it always allows child frame content.
945 HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent);
946 m_frame->contentRenderer()->hitTest(request, result);
947 if (!request.readOnly())
948 m_frame->document()->updateHoverActiveState(request, result.innerElement());
949
950 if (request.disallowsShadowContent())
951 result.setToNodesInDocumentTreeScope();
952
953 return result;
954 }
955
stopAutoscroll()956 void EventHandler::stopAutoscroll()
957 {
958 if (AutoscrollController* controller = autoscrollController())
959 controller->stopAutoscroll();
960 }
961
mousePressNode() const962 Node* EventHandler::mousePressNode() const
963 {
964 return m_mousePressNode.get();
965 }
966
scrollOverflow(ScrollDirection direction,ScrollGranularity granularity,Node * startingNode)967 bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode)
968 {
969 Node* node = startingNode;
970
971 if (!node)
972 node = m_frame->document()->focusedElement();
973
974 if (!node)
975 node = m_mousePressNode.get();
976
977 if (node) {
978 RenderObject* r = node->renderer();
979 if (r && !r->isListBox() && scrollNode(1.0f, granularity, direction, node, 0)) {
980 setFrameWasScrolledByUser();
981 return true;
982 }
983 }
984
985 return false;
986 }
987
scrollRecursively(ScrollDirection direction,ScrollGranularity granularity,Node * startingNode)988 bool EventHandler::scrollRecursively(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode)
989 {
990 // The layout needs to be up to date to determine if we can scroll. We may be
991 // here because of an onLoad event, in which case the final layout hasn't been performed yet.
992 m_frame->document()->updateLayoutIgnorePendingStylesheets();
993 if (scrollOverflow(direction, granularity, startingNode))
994 return true;
995 Frame* frame = m_frame;
996 FrameView* view = frame->view();
997 if (view && view->scroll(direction, granularity))
998 return true;
999 frame = frame->tree().parent();
1000 if (!frame)
1001 return false;
1002 return frame->eventHandler().scrollRecursively(direction, granularity, m_frame->ownerElement());
1003 }
1004
lastKnownMousePosition() const1005 IntPoint EventHandler::lastKnownMousePosition() const
1006 {
1007 return m_lastKnownMousePosition;
1008 }
1009
subframeForTargetNode(Node * node)1010 static Frame* subframeForTargetNode(Node* node)
1011 {
1012 if (!node)
1013 return 0;
1014
1015 RenderObject* renderer = node->renderer();
1016 if (!renderer || !renderer->isWidget())
1017 return 0;
1018
1019 Widget* widget = toRenderWidget(renderer)->widget();
1020 if (!widget || !widget->isFrameView())
1021 return 0;
1022
1023 return &toFrameView(widget)->frame();
1024 }
1025
subframeForHitTestResult(const MouseEventWithHitTestResults & hitTestResult)1026 static Frame* subframeForHitTestResult(const MouseEventWithHitTestResults& hitTestResult)
1027 {
1028 if (!hitTestResult.isOverWidget())
1029 return 0;
1030 return subframeForTargetNode(hitTestResult.targetNode());
1031 }
1032
isSubmitImage(Node * node)1033 static bool isSubmitImage(Node* node)
1034 {
1035 return node && node->hasTagName(inputTag) && toHTMLInputElement(node)->isImageButton();
1036 }
1037
1038 // Returns true if the node's editable block is not current focused for editing
nodeIsNotBeingEdited(Node * node,Frame * frame)1039 static bool nodeIsNotBeingEdited(Node* node, Frame* frame)
1040 {
1041 return frame->selection().rootEditableElement() != node->rootEditableElement();
1042 }
1043
useHandCursor(Node * node,bool isOverLink,bool shiftKey)1044 bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey)
1045 {
1046 if (!node)
1047 return false;
1048
1049 bool editable = node->rendererIsEditable();
1050 bool editableLinkEnabled = false;
1051
1052 // If the link is editable, then we need to check the settings to see whether or not the link should be followed
1053 if (editable) {
1054 ASSERT(m_frame->settings());
1055 switch (m_frame->settings()->editableLinkBehavior()) {
1056 default:
1057 case EditableLinkDefaultBehavior:
1058 case EditableLinkAlwaysLive:
1059 editableLinkEnabled = true;
1060 break;
1061
1062 case EditableLinkNeverLive:
1063 editableLinkEnabled = false;
1064 break;
1065
1066 case EditableLinkLiveWhenNotFocused:
1067 editableLinkEnabled = nodeIsNotBeingEdited(node, m_frame) || shiftKey;
1068 break;
1069
1070 case EditableLinkOnlyLiveWithShiftKey:
1071 editableLinkEnabled = shiftKey;
1072 break;
1073 }
1074 }
1075
1076 return ((isOverLink || isSubmitImage(node)) && (!editable || editableLinkEnabled));
1077 }
1078
cursorUpdateTimerFired(Timer<EventHandler> *)1079 void EventHandler::cursorUpdateTimerFired(Timer<EventHandler>*)
1080 {
1081 ASSERT(m_frame);
1082 ASSERT(m_frame->document());
1083
1084 updateCursor();
1085 }
1086
updateCursor()1087 void EventHandler::updateCursor()
1088 {
1089 if (m_mousePositionIsUnknown)
1090 return;
1091
1092 FrameView* view = m_frame->view();
1093 if (!view || !view->shouldSetCursor())
1094 return;
1095
1096 RenderView* renderView = view->renderView();
1097 if (!renderView)
1098 return;
1099
1100 bool shiftKey;
1101 bool ctrlKey;
1102 bool altKey;
1103 bool metaKey;
1104 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
1105
1106 m_frame->document()->updateLayout();
1107
1108 HitTestRequest request(HitTestRequest::ReadOnly);
1109 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
1110 renderView->hitTest(request, result);
1111
1112 OptionalCursor optionalCursor = selectCursor(result, shiftKey);
1113 if (optionalCursor.isCursorChange()) {
1114 m_currentMouseCursor = optionalCursor.cursor();
1115 view->setCursor(m_currentMouseCursor);
1116 }
1117 }
1118
selectCursor(const HitTestResult & result,bool shiftKey)1119 OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shiftKey)
1120 {
1121 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode())
1122 return NoCursorChange;
1123
1124 Page* page = m_frame->page();
1125 if (!page)
1126 return NoCursorChange;
1127 #if OS(WIN)
1128 if (panScrollInProgress())
1129 return NoCursorChange;
1130 #endif
1131
1132 Node* node = result.targetNode();
1133 if (!node)
1134 return selectAutoCursor(result, node, iBeamCursor(), shiftKey);
1135
1136 RenderObject* renderer = node->renderer();
1137 RenderStyle* style = renderer ? renderer->style() : 0;
1138
1139 if (renderer) {
1140 Cursor overrideCursor;
1141 switch (renderer->getCursor(roundedIntPoint(result.localPoint()), overrideCursor)) {
1142 case SetCursorBasedOnStyle:
1143 break;
1144 case SetCursor:
1145 return overrideCursor;
1146 case DoNotSetCursor:
1147 return NoCursorChange;
1148 }
1149 }
1150
1151 if (style && style->cursors()) {
1152 const CursorList* cursors = style->cursors();
1153 for (unsigned i = 0; i < cursors->size(); ++i) {
1154 StyleImage* styleImage = (*cursors)[i].image();
1155 if (!styleImage)
1156 continue;
1157 ImageResource* cachedImage = styleImage->cachedImage();
1158 if (!cachedImage)
1159 continue;
1160 float scale = styleImage->imageScaleFactor();
1161 // Get hotspot and convert from logical pixels to physical pixels.
1162 IntPoint hotSpot = (*cursors)[i].hotSpot();
1163 hotSpot.scale(scale, scale);
1164 IntSize size = cachedImage->imageForRenderer(renderer)->size();
1165 if (cachedImage->errorOccurred())
1166 continue;
1167 // Limit the size of cursors (in UI pixels) so that they cannot be
1168 // used to cover UI elements in chrome.
1169 size.scale(1 / scale);
1170 if (size.width() > maximumCursorSize || size.height() > maximumCursorSize)
1171 continue;
1172
1173 Image* image = cachedImage->imageForRenderer(renderer);
1174 // Ensure no overflow possible in calculations above.
1175 if (scale < minimumCursorScale)
1176 continue;
1177 return Cursor(image, hotSpot, scale);
1178 }
1179 }
1180
1181 switch (style ? style->cursor() : CURSOR_AUTO) {
1182 case CURSOR_AUTO: {
1183 bool horizontalText = !style || style->isHorizontalWritingMode();
1184 const Cursor& iBeam = horizontalText ? iBeamCursor() : verticalTextCursor();
1185 return selectAutoCursor(result, node, iBeam, shiftKey);
1186 }
1187 case CURSOR_CROSS:
1188 return crossCursor();
1189 case CURSOR_POINTER:
1190 return handCursor();
1191 case CURSOR_MOVE:
1192 return moveCursor();
1193 case CURSOR_ALL_SCROLL:
1194 return moveCursor();
1195 case CURSOR_E_RESIZE:
1196 return eastResizeCursor();
1197 case CURSOR_W_RESIZE:
1198 return westResizeCursor();
1199 case CURSOR_N_RESIZE:
1200 return northResizeCursor();
1201 case CURSOR_S_RESIZE:
1202 return southResizeCursor();
1203 case CURSOR_NE_RESIZE:
1204 return northEastResizeCursor();
1205 case CURSOR_SW_RESIZE:
1206 return southWestResizeCursor();
1207 case CURSOR_NW_RESIZE:
1208 return northWestResizeCursor();
1209 case CURSOR_SE_RESIZE:
1210 return southEastResizeCursor();
1211 case CURSOR_NS_RESIZE:
1212 return northSouthResizeCursor();
1213 case CURSOR_EW_RESIZE:
1214 return eastWestResizeCursor();
1215 case CURSOR_NESW_RESIZE:
1216 return northEastSouthWestResizeCursor();
1217 case CURSOR_NWSE_RESIZE:
1218 return northWestSouthEastResizeCursor();
1219 case CURSOR_COL_RESIZE:
1220 return columnResizeCursor();
1221 case CURSOR_ROW_RESIZE:
1222 return rowResizeCursor();
1223 case CURSOR_TEXT:
1224 return iBeamCursor();
1225 case CURSOR_WAIT:
1226 return waitCursor();
1227 case CURSOR_HELP:
1228 return helpCursor();
1229 case CURSOR_VERTICAL_TEXT:
1230 return verticalTextCursor();
1231 case CURSOR_CELL:
1232 return cellCursor();
1233 case CURSOR_CONTEXT_MENU:
1234 return contextMenuCursor();
1235 case CURSOR_PROGRESS:
1236 return progressCursor();
1237 case CURSOR_NO_DROP:
1238 return noDropCursor();
1239 case CURSOR_ALIAS:
1240 return aliasCursor();
1241 case CURSOR_COPY:
1242 return copyCursor();
1243 case CURSOR_NONE:
1244 return noneCursor();
1245 case CURSOR_NOT_ALLOWED:
1246 return notAllowedCursor();
1247 case CURSOR_DEFAULT:
1248 return pointerCursor();
1249 case CURSOR_WEBKIT_ZOOM_IN:
1250 return zoomInCursor();
1251 case CURSOR_WEBKIT_ZOOM_OUT:
1252 return zoomOutCursor();
1253 case CURSOR_WEBKIT_GRAB:
1254 return grabCursor();
1255 case CURSOR_WEBKIT_GRABBING:
1256 return grabbingCursor();
1257 }
1258 return pointerCursor();
1259 }
1260
selectAutoCursor(const HitTestResult & result,Node * node,const Cursor & iBeam,bool shiftKey)1261 OptionalCursor EventHandler::selectAutoCursor(const HitTestResult& result, Node* node, const Cursor& iBeam, bool shiftKey)
1262 {
1263 bool editable = (node && node->rendererIsEditable());
1264
1265 if (useHandCursor(node, result.isOverLink(), shiftKey))
1266 return handCursor();
1267
1268 bool inResizer = false;
1269 RenderObject* renderer = node ? node->renderer() : 0;
1270 if (renderer) {
1271 if (RenderLayer* layer = renderer->enclosingLayer()) {
1272 if (m_frame->view())
1273 inResizer = layer->scrollableArea() && layer->scrollableArea()->isPointInResizeControl(result.roundedPointInMainFrame(), ResizerForPointer);
1274 }
1275 }
1276
1277 // During selection, use an I-beam no matter what we're over.
1278 // If a drag may be starting or we're capturing mouse events for a particular node, don't treat this as a selection.
1279 if (m_mousePressed && m_mouseDownMayStartSelect
1280 && !m_mouseDownMayStartDrag
1281 && m_frame->selection().isCaretOrRange()
1282 && !m_capturingMouseEventsNode) {
1283 return iBeam;
1284 }
1285
1286 if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !result.scrollbar())
1287 return iBeam;
1288 return pointerCursor();
1289 }
1290
documentPointForWindowPoint(Frame * frame,const IntPoint & windowPoint)1291 static LayoutPoint documentPointForWindowPoint(Frame* frame, const IntPoint& windowPoint)
1292 {
1293 FrameView* view = frame->view();
1294 // FIXME: Is it really OK to use the wrong coordinates here when view is 0?
1295 // Historically the code would just crash; this is clearly no worse than that.
1296 return view ? view->windowToContents(windowPoint) : windowPoint;
1297 }
1298
handleMousePressEvent(const PlatformMouseEvent & mouseEvent)1299 bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent)
1300 {
1301 RefPtr<FrameView> protector(m_frame->view());
1302
1303 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent);
1304 if (defaultPrevented)
1305 return true;
1306
1307 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
1308 m_frame->tree().top()->eventHandler().m_lastMouseDownUserGestureToken = gestureIndicator.currentToken();
1309
1310 cancelFakeMouseMoveEvent();
1311 if (m_eventHandlerWillResetCapturingMouseEventsNode)
1312 m_capturingMouseEventsNode = 0;
1313 m_mousePressed = true;
1314 m_capturesDragging = true;
1315 setLastKnownMousePosition(mouseEvent);
1316 m_mouseDownTimestamp = mouseEvent.timestamp();
1317 m_mouseDownMayStartDrag = false;
1318 m_mouseDownMayStartSelect = false;
1319 m_mouseDownMayStartAutoscroll = false;
1320 if (FrameView* view = m_frame->view())
1321 m_mouseDownPos = view->windowToContents(mouseEvent.position());
1322 else {
1323 invalidateClick();
1324 return false;
1325 }
1326 m_mouseDownWasInSubframe = false;
1327
1328 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent;
1329 if (mouseEvent.fromTouch())
1330 hitType |= HitTestRequest::ReadOnly;
1331 HitTestRequest request(hitType);
1332 // Save the document point we generate in case the window coordinate is invalidated by what happens
1333 // when we dispatch the event.
1334 LayoutPoint documentPoint = documentPointForWindowPoint(m_frame, mouseEvent.position());
1335 MouseEventWithHitTestResults mev = m_frame->document()->prepareMouseEvent(request, documentPoint, mouseEvent);
1336
1337 if (!mev.targetNode()) {
1338 invalidateClick();
1339 return false;
1340 }
1341
1342 m_mousePressNode = mev.targetNode();
1343
1344 RefPtr<Frame> subframe = subframeForHitTestResult(mev);
1345 if (subframe && passMousePressEventToSubframe(mev, subframe.get())) {
1346 // Start capturing future events for this frame. We only do this if we didn't clear
1347 // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop.
1348 m_capturesDragging = subframe->eventHandler().capturesDragging();
1349 if (m_mousePressed && m_capturesDragging) {
1350 m_capturingMouseEventsNode = mev.targetNode();
1351 m_eventHandlerWillResetCapturingMouseEventsNode = true;
1352 }
1353 invalidateClick();
1354 return true;
1355 }
1356
1357 #if OS(WIN)
1358 // We store whether pan scrolling is in progress before calling stopAutoscroll()
1359 // because it will set m_autoscrollType to NoAutoscroll on return.
1360 bool isPanScrollInProgress = panScrollInProgress();
1361 stopAutoscroll();
1362 if (isPanScrollInProgress) {
1363 // We invalidate the click when exiting pan scrolling so that we don't inadvertently navigate
1364 // away from the current page (e.g. the click was on a hyperlink). See <rdar://problem/6095023>.
1365 invalidateClick();
1366 return true;
1367 }
1368 #endif
1369
1370 m_clickCount = mouseEvent.clickCount();
1371 m_clickNode = mev.targetNode()->isTextNode() ? mev.targetNode()->parentOrShadowHostNode() : mev.targetNode();
1372
1373 if (FrameView* view = m_frame->view()) {
1374 RenderLayer* layer = mev.targetNode()->renderer() ? mev.targetNode()->renderer()->enclosingLayer() : 0;
1375 IntPoint p = view->windowToContents(mouseEvent.position());
1376 if (layer && layer->scrollableArea() && layer->scrollableArea()->isPointInResizeControl(p, ResizerForPointer)) {
1377 m_resizeScrollableArea = layer->scrollableArea();
1378 m_resizeScrollableArea->setInResizeMode(true);
1379 m_offsetFromResizeCorner = m_resizeScrollableArea->offsetFromResizeCorner(p);
1380 invalidateClick();
1381 return true;
1382 }
1383 }
1384
1385 m_frame->selection().setCaretBlinkingSuspended(true);
1386
1387 bool swallowEvent = !dispatchMouseEvent(EventTypeNames::mousedown, mev.targetNode(), true, m_clickCount, mouseEvent, true);
1388 m_capturesDragging = !swallowEvent || mev.scrollbar();
1389
1390 // If the hit testing originally determined the event was in a scrollbar, refetch the MouseEventWithHitTestResults
1391 // in case the scrollbar widget was destroyed when the mouse event was handled.
1392 if (mev.scrollbar()) {
1393 const bool wasLastScrollBar = mev.scrollbar() == m_lastScrollbarUnderMouse.get();
1394 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
1395 mev = m_frame->document()->prepareMouseEvent(request, documentPoint, mouseEvent);
1396 if (wasLastScrollBar && mev.scrollbar() != m_lastScrollbarUnderMouse.get())
1397 m_lastScrollbarUnderMouse = 0;
1398 }
1399
1400 if (swallowEvent) {
1401 // scrollbars should get events anyway, even disabled controls might be scrollable
1402 Scrollbar* scrollbar = mev.scrollbar();
1403
1404 updateLastScrollbarUnderMouse(scrollbar, true);
1405
1406 if (scrollbar)
1407 passMousePressEventToScrollbar(mev, scrollbar);
1408 } else {
1409 if (shouldRefetchEventTarget(mev)) {
1410 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
1411 mev = m_frame->document()->prepareMouseEvent(request, documentPoint, mouseEvent);
1412 }
1413
1414 FrameView* view = m_frame->view();
1415 Scrollbar* scrollbar = view ? view->scrollbarAtPoint(mouseEvent.position()) : 0;
1416 if (!scrollbar)
1417 scrollbar = mev.scrollbar();
1418
1419 updateLastScrollbarUnderMouse(scrollbar, true);
1420
1421 if (scrollbar && passMousePressEventToScrollbar(mev, scrollbar))
1422 swallowEvent = true;
1423 else
1424 swallowEvent = handleMousePressEvent(mev);
1425 }
1426
1427 return swallowEvent;
1428 }
1429
layerForNode(Node * node)1430 static RenderLayer* layerForNode(Node* node)
1431 {
1432 if (!node)
1433 return 0;
1434
1435 RenderObject* renderer = node->renderer();
1436 if (!renderer)
1437 return 0;
1438
1439 RenderLayer* layer = renderer->enclosingLayer();
1440 if (!layer)
1441 return 0;
1442
1443 return layer;
1444 }
1445
associatedScrollableArea(const RenderLayer * layer) const1446 ScrollableArea* EventHandler::associatedScrollableArea(const RenderLayer* layer) const
1447 {
1448 ScrollableArea* layerScrollableArea = layer->scrollableArea();
1449 if (!layerScrollableArea)
1450 return 0;
1451
1452 if (FrameView* frameView = m_frame->view()) {
1453 if (frameView->containsScrollableArea(layerScrollableArea))
1454 return layerScrollableArea;
1455 }
1456
1457 return 0;
1458 }
1459
handleMouseMoveEvent(const PlatformMouseEvent & event)1460 bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& event)
1461 {
1462 RefPtr<FrameView> protector(m_frame->view());
1463 MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration);
1464
1465 HitTestResult hoveredNode = HitTestResult(LayoutPoint());
1466 bool result = handleMouseMoveOrLeaveEvent(event, &hoveredNode);
1467
1468 Page* page = m_frame->page();
1469 if (!page)
1470 return result;
1471
1472 if (RenderLayer* layer = layerForNode(hoveredNode.innerNode())) {
1473 if (ScrollableArea* layerScrollableArea = associatedScrollableArea(layer))
1474 layerScrollableArea->mouseMovedInContentArea();
1475 }
1476
1477 if (FrameView* frameView = m_frame->view())
1478 frameView->mouseMovedInContentArea();
1479
1480 hoveredNode.setToShadowHostIfInUserAgentShadowRoot();
1481 page->chrome().mouseDidMoveOverElement(hoveredNode, event.modifierFlags());
1482 page->chrome().setToolTip(hoveredNode);
1483
1484 return result;
1485 }
1486
handleMouseLeaveEvent(const PlatformMouseEvent & event)1487 void EventHandler::handleMouseLeaveEvent(const PlatformMouseEvent& event)
1488 {
1489 RefPtr<FrameView> protector(m_frame->view());
1490 handleMouseMoveOrLeaveEvent(event);
1491 }
1492
syntheticTouchCursor()1493 static Cursor& syntheticTouchCursor()
1494 {
1495 DEFINE_STATIC_LOCAL(Cursor, c, (Image::loadPlatformResource("syntheticTouchCursor").get(), IntPoint(10, 10)));
1496 return c;
1497 }
1498
handleMouseMoveOrLeaveEvent(const PlatformMouseEvent & mouseEvent,HitTestResult * hoveredNode,bool onlyUpdateScrollbars)1499 bool EventHandler::handleMouseMoveOrLeaveEvent(const PlatformMouseEvent& mouseEvent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars)
1500 {
1501 ASSERT(m_frame);
1502 ASSERT(m_frame->view());
1503
1504 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent);
1505 if (defaultPrevented) {
1506 m_frame->view()->setCursor(syntheticTouchCursor());
1507 return true;
1508 }
1509
1510 setLastKnownMousePosition(mouseEvent);
1511
1512 if (m_hoverTimer.isActive())
1513 m_hoverTimer.stop();
1514
1515 m_cursorUpdateTimer.stop();
1516
1517 cancelFakeMouseMoveEvent();
1518
1519 if (m_svgPan) {
1520 toSVGDocument(m_frame->document())->updatePan(m_frame->view()->windowToContents(m_lastKnownMousePosition));
1521 return true;
1522 }
1523
1524 if (m_frameSetBeingResized)
1525 return !dispatchMouseEvent(EventTypeNames::mousemove, m_frameSetBeingResized.get(), false, 0, mouseEvent, false);
1526
1527 // Send events right to a scrollbar if the mouse is pressed.
1528 if (m_lastScrollbarUnderMouse && m_mousePressed) {
1529 m_lastScrollbarUnderMouse->mouseMoved(mouseEvent);
1530 return true;
1531 }
1532
1533 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent;
1534 if (mouseEvent.fromTouch())
1535 hitType |= HitTestRequest::ReadOnly;
1536
1537 if (m_mousePressed)
1538 hitType |= HitTestRequest::Active;
1539 else if (onlyUpdateScrollbars) {
1540 // Mouse events should be treated as "read-only" if we're updating only scrollbars. This
1541 // means that :hover and :active freeze in the state they were in, rather than updating
1542 // for nodes the mouse moves while the window is not key (which will be the case if
1543 // onlyUpdateScrollbars is true).
1544 hitType |= HitTestRequest::ReadOnly;
1545 }
1546
1547 // Treat any mouse move events as readonly if the user is currently touching the screen.
1548 if (m_touchPressed)
1549 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
1550 HitTestRequest request(hitType);
1551 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent);
1552 if (hoveredNode)
1553 *hoveredNode = mev.hitTestResult();
1554
1555 Scrollbar* scrollbar = 0;
1556
1557 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode())
1558 m_resizeScrollableArea->resize(mouseEvent, m_offsetFromResizeCorner);
1559 else {
1560 if (FrameView* view = m_frame->view())
1561 scrollbar = view->scrollbarAtPoint(mouseEvent.position());
1562
1563 if (!scrollbar)
1564 scrollbar = mev.scrollbar();
1565
1566 updateLastScrollbarUnderMouse(scrollbar, !m_mousePressed);
1567 if (onlyUpdateScrollbars)
1568 return true;
1569 }
1570
1571 bool swallowEvent = false;
1572 RefPtr<Frame> newSubframe = m_capturingMouseEventsNode.get() ? subframeForTargetNode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev);
1573
1574 // 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.
1575 if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree().isDescendantOf(m_frame) && m_lastMouseMoveEventSubframe != newSubframe)
1576 passMouseMoveEventToSubframe(mev, m_lastMouseMoveEventSubframe.get());
1577
1578 if (newSubframe) {
1579 // Update over/out state before passing the event to the subframe.
1580 updateMouseEventTargetNode(mev.targetNode(), mouseEvent, true);
1581
1582 // Event dispatch in updateMouseEventTargetNode may have caused the subframe of the target
1583 // node to be detached from its FrameView, in which case the event should not be passed.
1584 if (newSubframe->view())
1585 swallowEvent |= passMouseMoveEventToSubframe(mev, newSubframe.get(), hoveredNode);
1586 } else {
1587 if (scrollbar && !m_mousePressed)
1588 scrollbar->mouseMoved(mouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering.
1589 if (FrameView* view = m_frame->view()) {
1590 OptionalCursor optionalCursor = selectCursor(mev.hitTestResult(), mouseEvent.shiftKey());
1591 if (optionalCursor.isCursorChange()) {
1592 m_currentMouseCursor = optionalCursor.cursor();
1593 view->setCursor(m_currentMouseCursor);
1594 }
1595 }
1596 }
1597
1598 m_lastMouseMoveEventSubframe = newSubframe;
1599
1600 if (swallowEvent)
1601 return true;
1602
1603 swallowEvent = !dispatchMouseEvent(EventTypeNames::mousemove, mev.targetNode(), false, 0, mouseEvent, true);
1604 if (!swallowEvent)
1605 swallowEvent = handleMouseDraggedEvent(mev);
1606
1607 return swallowEvent;
1608 }
1609
invalidateClick()1610 void EventHandler::invalidateClick()
1611 {
1612 m_clickCount = 0;
1613 m_clickNode = 0;
1614 }
1615
parentForClickEvent(const Node & node)1616 static Node* parentForClickEvent(const Node& node)
1617 {
1618 // IE doesn't dispatch click events for mousedown/mouseup events across form
1619 // controls.
1620 if (node.isHTMLElement() && toHTMLElement(node).isInteractiveContent())
1621 return 0;
1622 return node.parentOrShadowHostNode();
1623 }
1624
handleMouseReleaseEvent(const PlatformMouseEvent & mouseEvent)1625 bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent)
1626 {
1627 RefPtr<FrameView> protector(m_frame->view());
1628
1629 m_frame->selection().setCaretBlinkingSuspended(false);
1630
1631 bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent);
1632 if (defaultPrevented)
1633 return true;
1634
1635 OwnPtr<UserGestureIndicator> gestureIndicator;
1636
1637 if (m_frame->tree().top()->eventHandler().m_lastMouseDownUserGestureToken)
1638 gestureIndicator = adoptPtr(new UserGestureIndicator(m_frame->tree().top()->eventHandler().m_lastMouseDownUserGestureToken.release()));
1639 else
1640 gestureIndicator = adoptPtr(new UserGestureIndicator(DefinitelyProcessingUserGesture));
1641
1642 #if OS(WIN)
1643 if (Page* page = m_frame->page())
1644 page->autoscrollController().handleMouseReleaseForPanScrolling(m_frame, mouseEvent);
1645 #endif
1646
1647 m_mousePressed = false;
1648 setLastKnownMousePosition(mouseEvent);
1649
1650 if (m_svgPan) {
1651 m_svgPan = false;
1652 toSVGDocument(m_frame->document())->updatePan(m_frame->view()->windowToContents(m_lastKnownMousePosition));
1653 return true;
1654 }
1655
1656 if (m_frameSetBeingResized)
1657 return !dispatchMouseEvent(EventTypeNames::mouseup, m_frameSetBeingResized.get(), true, m_clickCount, mouseEvent, false);
1658
1659 if (m_lastScrollbarUnderMouse) {
1660 invalidateClick();
1661 m_lastScrollbarUnderMouse->mouseUp(mouseEvent);
1662 bool cancelable = true;
1663 bool setUnder = false;
1664 return !dispatchMouseEvent(EventTypeNames::mouseup, m_lastNodeUnderMouse.get(), cancelable, m_clickCount, mouseEvent, setUnder);
1665 }
1666
1667 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Release | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent;
1668 if (mouseEvent.fromTouch())
1669 hitType |= HitTestRequest::ReadOnly;
1670 HitTestRequest request(hitType);
1671 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent);
1672 Frame* subframe = m_capturingMouseEventsNode.get() ? subframeForTargetNode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev);
1673 if (m_eventHandlerWillResetCapturingMouseEventsNode)
1674 m_capturingMouseEventsNode = 0;
1675 if (subframe && passMouseReleaseEventToSubframe(mev, subframe))
1676 return true;
1677
1678 bool swallowMouseUpEvent = !dispatchMouseEvent(EventTypeNames::mouseup, mev.targetNode(), true, m_clickCount, mouseEvent, false);
1679
1680 bool contextMenuEvent = mouseEvent.button() == RightButton;
1681 #if OS(MACOSX)
1682 // FIXME: The Mac port achieves the same behavior by checking whether the context menu is currently open in WebPage::mouseEvent(). Consider merging the implementations.
1683 if (mouseEvent.button() == LeftButton && mouseEvent.modifiers() & PlatformEvent::CtrlKey)
1684 contextMenuEvent = true;
1685 #endif
1686
1687 bool swallowClickEvent = false;
1688 if (m_clickCount > 0 && !contextMenuEvent && mev.targetNode() && m_clickNode) {
1689 if (Node* clickTargetNode = mev.targetNode()->commonAncestor(*m_clickNode, parentForClickEvent))
1690 swallowClickEvent = !dispatchMouseEvent(EventTypeNames::click, clickTargetNode, true, m_clickCount, mouseEvent, true);
1691 }
1692
1693 if (m_resizeScrollableArea) {
1694 m_resizeScrollableArea->setInResizeMode(false);
1695 m_resizeScrollableArea = 0;
1696 }
1697
1698 bool swallowMouseReleaseEvent = false;
1699 if (!swallowMouseUpEvent)
1700 swallowMouseReleaseEvent = handleMouseReleaseEvent(mev);
1701
1702 invalidateClick();
1703
1704 return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
1705 }
1706
handlePasteGlobalSelection(const PlatformMouseEvent & mouseEvent)1707 bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& mouseEvent)
1708 {
1709 // If the event was a middle click, attempt to copy global selection in after
1710 // the newly set caret position.
1711 //
1712 // This code is called from either the mouse up or mouse down handling. There
1713 // is some debate about when the global selection is pasted:
1714 // xterm: pastes on up.
1715 // GTK: pastes on down.
1716 // Qt: pastes on up.
1717 // Firefox: pastes on up.
1718 // Chromium: pastes on up.
1719 //
1720 // There is something of a webcompat angle to this well, as highlighted by
1721 // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on
1722 // down then the text is pasted just before the onclick handler runs and
1723 // clears the text box. So it's important this happens after the event
1724 // handlers have been fired.
1725 if (mouseEvent.type() != PlatformEvent::MouseReleased)
1726 return false;
1727
1728 if (!m_frame->page())
1729 return false;
1730 Frame* focusFrame = m_frame->page()->focusController().focusedOrMainFrame();
1731 // Do not paste here if the focus was moved somewhere else.
1732 if (m_frame == focusFrame && m_frame->editor().behavior().supportsGlobalSelection())
1733 return m_frame->editor().command("PasteGlobalSelection").execute();
1734
1735 return false;
1736 }
1737
1738
dispatchDragEvent(const AtomicString & eventType,Node * dragTarget,const PlatformMouseEvent & event,Clipboard * clipboard)1739 bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Node* dragTarget, const PlatformMouseEvent& event, Clipboard* clipboard)
1740 {
1741 FrameView* view = m_frame->view();
1742
1743 // FIXME: We might want to dispatch a dragleave even if the view is gone.
1744 if (!view)
1745 return false;
1746
1747 view->resetDeferredRepaintDelay();
1748 RefPtr<MouseEvent> me = MouseEvent::create(eventType,
1749 true, true, m_frame->document()->domWindow(),
1750 0, event.globalPosition().x(), event.globalPosition().y(), event.position().x(), event.position().y(),
1751 event.movementDelta().x(), event.movementDelta().y(),
1752 event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(),
1753 0, 0, clipboard);
1754
1755 dragTarget->dispatchEvent(me.get(), IGNORE_EXCEPTION);
1756 return me->defaultPrevented();
1757 }
1758
targetIsFrame(Node * target,Frame * & frame)1759 static bool targetIsFrame(Node* target, Frame*& frame)
1760 {
1761 if (!target)
1762 return false;
1763
1764 if (!target->hasTagName(frameTag) && !target->hasTagName(iframeTag))
1765 return false;
1766
1767 frame = toHTMLFrameElementBase(target)->contentFrame();
1768 return true;
1769 }
1770
findDropZone(Node * target,Clipboard * clipboard)1771 static bool findDropZone(Node* target, Clipboard* clipboard)
1772 {
1773 Element* element = target->isElementNode() ? toElement(target) : target->parentElement();
1774 for (; element; element = element->parentElement()) {
1775 bool matched = false;
1776 AtomicString dropZoneStr = element->fastGetAttribute(webkitdropzoneAttr);
1777
1778 if (dropZoneStr.isEmpty())
1779 continue;
1780
1781 dropZoneStr = dropZoneStr.lower();
1782
1783 SpaceSplitString keywords(dropZoneStr, false);
1784 if (keywords.isNull())
1785 continue;
1786
1787 DragOperation dragOperation = DragOperationNone;
1788 for (unsigned int i = 0; i < keywords.size(); i++) {
1789 DragOperation op = convertDropZoneOperationToDragOperation(keywords[i]);
1790 if (op != DragOperationNone) {
1791 if (dragOperation == DragOperationNone)
1792 dragOperation = op;
1793 } else
1794 matched = matched || clipboard->hasDropZoneType(keywords[i].string());
1795
1796 if (matched && dragOperation != DragOperationNone)
1797 break;
1798 }
1799 if (matched) {
1800 clipboard->setDropEffect(convertDragOperationToDropZoneOperation(dragOperation));
1801 return true;
1802 }
1803 }
1804 return false;
1805 }
1806
updateDragAndDrop(const PlatformMouseEvent & event,Clipboard * clipboard)1807 bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard)
1808 {
1809 bool accept = false;
1810
1811 if (!m_frame->view())
1812 return false;
1813
1814 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
1815 MouseEventWithHitTestResults mev = prepareMouseEvent(request, event);
1816
1817 // Drag events should never go to text nodes (following IE, and proper mouseover/out dispatch)
1818 RefPtr<Node> newTarget = mev.targetNode();
1819 if (newTarget && newTarget->isTextNode())
1820 newTarget = EventPath::parent(newTarget.get());
1821
1822 if (AutoscrollController* controller = autoscrollController())
1823 controller->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp());
1824
1825 if (m_dragTarget != newTarget) {
1826 // FIXME: this ordering was explicitly chosen to match WinIE. However,
1827 // it is sometimes incorrect when dragging within subframes, as seen with
1828 // LayoutTests/fast/events/drag-in-frames.html.
1829 //
1830 // 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>.
1831 Frame* targetFrame;
1832 if (targetIsFrame(newTarget.get(), targetFrame)) {
1833 if (targetFrame)
1834 accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard);
1835 } else if (newTarget) {
1836 // 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.
1837 if (dragState().m_dragSrc) {
1838 // for now we don't care if event handler cancels default behavior, since there is none
1839 dispatchDragSrcEvent(EventTypeNames::drag, event);
1840 }
1841 accept = dispatchDragEvent(EventTypeNames::dragenter, newTarget.get(), event, clipboard);
1842 if (!accept)
1843 accept = findDropZone(newTarget.get(), clipboard);
1844 }
1845
1846 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
1847 if (targetFrame)
1848 accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard);
1849 } else if (m_dragTarget)
1850 dispatchDragEvent(EventTypeNames::dragleave, m_dragTarget.get(), event, clipboard);
1851
1852 if (newTarget) {
1853 // We do not explicitly call dispatchDragEvent here because it could ultimately result in the appearance that
1854 // two dragover events fired. So, we mark that we should only fire a dragover event on the next call to this function.
1855 m_shouldOnlyFireDragOverEvent = true;
1856 }
1857 } else {
1858 Frame* targetFrame;
1859 if (targetIsFrame(newTarget.get(), targetFrame)) {
1860 if (targetFrame)
1861 accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard);
1862 } else if (newTarget) {
1863 // Note, when dealing with sub-frames, we may need to fire only a dragover event as a drag event may have been fired earlier.
1864 if (!m_shouldOnlyFireDragOverEvent && dragState().m_dragSrc) {
1865 // for now we don't care if event handler cancels default behavior, since there is none
1866 dispatchDragSrcEvent(EventTypeNames::drag, event);
1867 }
1868 accept = dispatchDragEvent(EventTypeNames::dragover, newTarget.get(), event, clipboard);
1869 if (!accept)
1870 accept = findDropZone(newTarget.get(), clipboard);
1871 m_shouldOnlyFireDragOverEvent = false;
1872 }
1873 }
1874 m_dragTarget = newTarget;
1875
1876 return accept;
1877 }
1878
cancelDragAndDrop(const PlatformMouseEvent & event,Clipboard * clipboard)1879 void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard)
1880 {
1881 Frame* targetFrame;
1882 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
1883 if (targetFrame)
1884 targetFrame->eventHandler().cancelDragAndDrop(event, clipboard);
1885 } else if (m_dragTarget.get()) {
1886 if (dragState().m_dragSrc)
1887 dispatchDragSrcEvent(EventTypeNames::drag, event);
1888 dispatchDragEvent(EventTypeNames::dragleave, m_dragTarget.get(), event, clipboard);
1889 }
1890 clearDragState();
1891 }
1892
performDragAndDrop(const PlatformMouseEvent & event,Clipboard * clipboard)1893 bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard)
1894 {
1895 Frame* targetFrame;
1896 bool preventedDefault = false;
1897 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
1898 if (targetFrame)
1899 preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, clipboard);
1900 } else if (m_dragTarget.get())
1901 preventedDefault = dispatchDragEvent(EventTypeNames::drop, m_dragTarget.get(), event, clipboard);
1902 clearDragState();
1903 return preventedDefault;
1904 }
1905
clearDragState()1906 void EventHandler::clearDragState()
1907 {
1908 stopAutoscroll();
1909 m_dragTarget = 0;
1910 m_capturingMouseEventsNode = 0;
1911 m_shouldOnlyFireDragOverEvent = false;
1912 }
1913
setCapturingMouseEventsNode(PassRefPtr<Node> n)1914 void EventHandler::setCapturingMouseEventsNode(PassRefPtr<Node> n)
1915 {
1916 m_capturingMouseEventsNode = n;
1917 m_eventHandlerWillResetCapturingMouseEventsNode = false;
1918 }
1919
prepareMouseEvent(const HitTestRequest & request,const PlatformMouseEvent & mev)1920 MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mev)
1921 {
1922 ASSERT(m_frame);
1923 ASSERT(m_frame->document());
1924
1925 return m_frame->document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mev.position()), mev);
1926 }
1927
instanceAssociatedWithShadowTreeElement(Node * referenceNode)1928 static inline SVGElementInstance* instanceAssociatedWithShadowTreeElement(Node* referenceNode)
1929 {
1930 if (!referenceNode || !referenceNode->isSVGElement())
1931 return 0;
1932
1933 ShadowRoot* shadowRoot = referenceNode->containingShadowRoot();
1934 if (!shadowRoot)
1935 return 0;
1936
1937 Element* shadowTreeParentElement = shadowRoot->host();
1938 if (!shadowTreeParentElement || !shadowTreeParentElement->hasTagName(useTag))
1939 return 0;
1940
1941 return toSVGUseElement(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode);
1942 }
1943
updateMouseEventTargetNode(Node * targetNode,const PlatformMouseEvent & mouseEvent,bool fireMouseOverOut)1944 void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& mouseEvent, bool fireMouseOverOut)
1945 {
1946 Node* result = targetNode;
1947
1948 // If we're capturing, we always go right to that node.
1949 if (m_capturingMouseEventsNode)
1950 result = m_capturingMouseEventsNode.get();
1951 else {
1952 // If the target node is a text node, dispatch on the parent node - rdar://4196646
1953 if (result && result->isTextNode())
1954 result = EventPath::parent(result);
1955 }
1956 m_nodeUnderMouse = result;
1957 m_instanceUnderMouse = instanceAssociatedWithShadowTreeElement(result);
1958
1959 // <use> shadow tree elements may have been recloned, update node under mouse in any case
1960 if (m_lastInstanceUnderMouse) {
1961 SVGElement* lastCorrespondingElement = m_lastInstanceUnderMouse->correspondingElement();
1962 SVGElement* lastCorrespondingUseElement = m_lastInstanceUnderMouse->correspondingUseElement();
1963
1964 if (lastCorrespondingElement && lastCorrespondingUseElement) {
1965 HashSet<SVGElementInstance*> instances = lastCorrespondingElement->instancesForElement();
1966
1967 // Locate the recloned shadow tree element for our corresponding instance
1968 HashSet<SVGElementInstance*>::iterator end = instances.end();
1969 for (HashSet<SVGElementInstance*>::iterator it = instances.begin(); it != end; ++it) {
1970 SVGElementInstance* instance = (*it);
1971 ASSERT(instance->correspondingElement() == lastCorrespondingElement);
1972
1973 if (instance == m_lastInstanceUnderMouse)
1974 continue;
1975
1976 if (instance->correspondingUseElement() != lastCorrespondingUseElement)
1977 continue;
1978
1979 SVGElement* shadowTreeElement = instance->shadowTreeElement();
1980 if (!shadowTreeElement->inDocument() || m_lastNodeUnderMouse == shadowTreeElement)
1981 continue;
1982
1983 m_lastNodeUnderMouse = shadowTreeElement;
1984 m_lastInstanceUnderMouse = instance;
1985 break;
1986 }
1987 }
1988 }
1989
1990 // Fire mouseout/mouseover if the mouse has shifted to a different node.
1991 if (fireMouseOverOut) {
1992 RenderLayer* layerForLastNode = layerForNode(m_lastNodeUnderMouse.get());
1993 RenderLayer* layerForNodeUnderMouse = layerForNode(m_nodeUnderMouse.get());
1994 Page* page = m_frame->page();
1995
1996 if (m_lastNodeUnderMouse && (!m_nodeUnderMouse || m_nodeUnderMouse->document() != m_frame->document())) {
1997 // The mouse has moved between frames.
1998 if (Frame* frame = m_lastNodeUnderMouse->document().frame()) {
1999 if (FrameView* frameView = frame->view())
2000 frameView->mouseExitedContentArea();
2001 }
2002 } else if (page && (layerForLastNode && (!layerForNodeUnderMouse || layerForNodeUnderMouse != layerForLastNode))) {
2003 // The mouse has moved between layers.
2004 if (ScrollableArea* scrollableAreaForLastNode = associatedScrollableArea(layerForLastNode))
2005 scrollableAreaForLastNode->mouseExitedContentArea();
2006 }
2007
2008 if (m_nodeUnderMouse && (!m_lastNodeUnderMouse || m_lastNodeUnderMouse->document() != m_frame->document())) {
2009 // The mouse has moved between frames.
2010 if (Frame* frame = m_nodeUnderMouse->document().frame()) {
2011 if (FrameView* frameView = frame->view())
2012 frameView->mouseEnteredContentArea();
2013 }
2014 } else if (page && (layerForNodeUnderMouse && (!layerForLastNode || layerForNodeUnderMouse != layerForLastNode))) {
2015 // The mouse has moved between layers.
2016 if (ScrollableArea* scrollableAreaForNodeUnderMouse = associatedScrollableArea(layerForNodeUnderMouse))
2017 scrollableAreaForNodeUnderMouse->mouseEnteredContentArea();
2018 }
2019
2020 if (m_lastNodeUnderMouse && m_lastNodeUnderMouse->document() != m_frame->document()) {
2021 m_lastNodeUnderMouse = 0;
2022 m_lastScrollbarUnderMouse = 0;
2023 m_lastInstanceUnderMouse = 0;
2024 }
2025
2026 if (m_lastNodeUnderMouse != m_nodeUnderMouse) {
2027 // send mouseout event to the old node
2028 if (m_lastNodeUnderMouse)
2029 m_lastNodeUnderMouse->dispatchMouseEvent(mouseEvent, EventTypeNames::mouseout, 0, m_nodeUnderMouse.get());
2030 // send mouseover event to the new node
2031 if (m_nodeUnderMouse)
2032 m_nodeUnderMouse->dispatchMouseEvent(mouseEvent, EventTypeNames::mouseover, 0, m_lastNodeUnderMouse.get());
2033 }
2034 m_lastNodeUnderMouse = m_nodeUnderMouse;
2035 m_lastInstanceUnderMouse = instanceAssociatedWithShadowTreeElement(m_nodeUnderMouse.get());
2036 }
2037 }
2038
dispatchMouseEvent(const AtomicString & eventType,Node * targetNode,bool,int clickCount,const PlatformMouseEvent & mouseEvent,bool setUnder)2039 bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& mouseEvent, bool setUnder)
2040 {
2041 if (FrameView* view = m_frame->view())
2042 view->resetDeferredRepaintDelay();
2043
2044 updateMouseEventTargetNode(targetNode, mouseEvent, setUnder);
2045
2046 bool swallowEvent = false;
2047
2048 if (m_nodeUnderMouse)
2049 swallowEvent = !(m_nodeUnderMouse->dispatchMouseEvent(mouseEvent, eventType, clickCount));
2050
2051 if (swallowEvent || eventType != EventTypeNames::mousedown)
2052 return !swallowEvent;
2053
2054 // If clicking on a frame scrollbar, do not mess up with content focus.
2055 if (FrameView* view = m_frame->view()) {
2056 if (view->scrollbarAtPoint(mouseEvent.position()))
2057 return true;
2058 }
2059
2060 // The layout needs to be up to date to determine if an element is focusable.
2061 m_frame->document()->updateLayoutIgnorePendingStylesheets();
2062
2063 Element* element = 0;
2064 if (m_nodeUnderMouse)
2065 element = m_nodeUnderMouse->isElementNode() ? toElement(m_nodeUnderMouse) : m_nodeUnderMouse->parentOrShadowHostElement();
2066 for (; element; element = element->parentOrShadowHostElement()) {
2067 if (element->isFocusable() && element->focused())
2068 return !swallowEvent;
2069 if (element->isMouseFocusable())
2070 break;
2071 }
2072 ASSERT(!element || element->isMouseFocusable());
2073
2074 // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus
2075 // a node on mouse down if it's selected and inside a focused node. It will
2076 // be focused if the user does a mouseup over it, however, because the
2077 // mouseup will set a selection inside it, which will call
2078 // FrameSelection::setFocusedNodeIfNeeded.
2079 if (element
2080 && m_frame->selection().isRange()
2081 && m_frame->selection().toNormalizedRange()->compareNode(element, IGNORE_EXCEPTION) == Range::NODE_INSIDE
2082 && element->isDescendantOf(m_frame->document()->focusedElement()))
2083 return true;
2084
2085 // Only change the focus when clicking scrollbars if it can transfered to a
2086 // mouse focusable node.
2087 if (!element && isInsideScrollbar(mouseEvent.position()))
2088 return false;
2089
2090 if (Page* page = m_frame->page()) {
2091 // If focus shift is blocked, we eat the event. Note we should never
2092 // clear swallowEvent if the page already set it (e.g., by canceling
2093 // default behavior).
2094 if (element) {
2095 if (!page->focusController().setFocusedElement(element, m_frame, FocusDirectionMouse))
2096 swallowEvent = true;
2097 } else {
2098 // We call setFocusedElement even with !element in order to blur
2099 // current focus element when a link is clicked; this is expected by
2100 // some sites that rely on onChange handlers running from form
2101 // fields before the button click is processed.
2102 if (!page->focusController().setFocusedElement(0, m_frame))
2103 swallowEvent = true;
2104 }
2105 }
2106
2107 return !swallowEvent;
2108 }
2109
isInsideScrollbar(const IntPoint & windowPoint) const2110 bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const
2111 {
2112 if (RenderView* renderView = m_frame->contentRenderer()) {
2113 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
2114 HitTestResult result(windowPoint);
2115 renderView->hitTest(request, result);
2116 return result.scrollbar();
2117 }
2118
2119 return false;
2120 }
2121
shouldTurnVerticalTicksIntoHorizontal(const HitTestResult & result,const PlatformWheelEvent & event) const2122 bool EventHandler::shouldTurnVerticalTicksIntoHorizontal(const HitTestResult& result, const PlatformWheelEvent& event) const
2123 {
2124 #if OS(ANDROID) || OS(MACOSX) || OS(WIN)
2125 return false;
2126 #else
2127 // GTK+ must scroll horizontally if the mouse pointer is on top of the
2128 // horizontal scrollbar while scrolling with the wheel.
2129 // This code comes from gtk/EventHandlerGtk.cpp.
2130 return !event.hasPreciseScrollingDeltas() && result.scrollbar() && result.scrollbar()->orientation() == HorizontalScrollbar;
2131 #endif
2132 }
2133
handleWheelEvent(const PlatformWheelEvent & e)2134 bool EventHandler::handleWheelEvent(const PlatformWheelEvent& e)
2135 {
2136 #define RETURN_WHEEL_EVENT_HANDLED() \
2137 { \
2138 setFrameWasScrolledByUser(); \
2139 return true; \
2140 }
2141
2142 Document* doc = m_frame->document();
2143
2144 if (!doc->renderer())
2145 return false;
2146
2147 RefPtr<FrameView> protector(m_frame->view());
2148
2149 FrameView* view = m_frame->view();
2150 if (!view)
2151 return false;
2152
2153 if (handleWheelEventAsEmulatedGesture(e))
2154 return true;
2155
2156 LayoutPoint vPoint = view->windowToContents(e.position());
2157
2158 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
2159 HitTestResult result(vPoint);
2160 doc->renderView()->hitTest(request, result);
2161
2162 Node* node = result.innerNode();
2163 // Wheel events should not dispatch to text nodes.
2164 if (node && node->isTextNode())
2165 node = EventPath::parent(node);
2166
2167 bool isOverWidget;
2168 if (e.useLatchedEventNode()) {
2169 if (!m_latchedWheelEventNode) {
2170 m_latchedWheelEventNode = node;
2171 m_widgetIsLatched = result.isOverWidget();
2172 } else
2173 node = m_latchedWheelEventNode.get();
2174
2175 isOverWidget = m_widgetIsLatched;
2176 } else {
2177 if (m_latchedWheelEventNode)
2178 m_latchedWheelEventNode = 0;
2179 if (m_previousWheelScrolledNode)
2180 m_previousWheelScrolledNode = 0;
2181
2182 isOverWidget = result.isOverWidget();
2183 }
2184
2185 // FIXME: It should not be necessary to do this mutation here.
2186 // Instead, the handlers should know convert vertical scrolls
2187 // appropriately.
2188 PlatformWheelEvent event = e;
2189 if (m_baseEventType == PlatformEvent::NoType && shouldTurnVerticalTicksIntoHorizontal(result, e))
2190 event = event.copyTurningVerticalTicksIntoHorizontalTicks();
2191
2192 if (node) {
2193 // Figure out which view to send the event to.
2194 RenderObject* target = node->renderer();
2195
2196 if (isOverWidget && target && target->isWidget()) {
2197 Widget* widget = toRenderWidget(target)->widget();
2198 if (widget && passWheelEventToWidget(e, widget))
2199 RETURN_WHEEL_EVENT_HANDLED();
2200 }
2201
2202 if (node && !node->dispatchWheelEvent(event))
2203 RETURN_WHEEL_EVENT_HANDLED();
2204 }
2205
2206
2207 // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed.
2208 view = m_frame->view();
2209 if (!view || !view->wheelEvent(event))
2210 return false;
2211
2212 RETURN_WHEEL_EVENT_HANDLED();
2213
2214 #undef RETURN_WHEEL_EVENT_HANDLED
2215 }
2216
defaultWheelEventHandler(Node * startNode,WheelEvent * wheelEvent)2217 void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEvent)
2218 {
2219 if (!startNode || !wheelEvent)
2220 return;
2221
2222 Node* stopNode = m_previousWheelScrolledNode.get();
2223 ScrollGranularity granularity = wheelGranularityToScrollGranularity(wheelEvent->deltaMode());
2224
2225 // Break up into two scrolls if we need to. Diagonal movement on
2226 // a MacBook pro is an example of a 2-dimensional mouse wheel event (where both deltaX and deltaY can be set).
2227 if (scrollNode(wheelEvent->deltaX(), granularity, ScrollRight, startNode, &stopNode, roundedIntPoint(wheelEvent->absoluteLocation())))
2228 wheelEvent->setDefaultHandled();
2229
2230 if (scrollNode(wheelEvent->deltaY(), granularity, ScrollDown, startNode, &stopNode, roundedIntPoint(wheelEvent->absoluteLocation())))
2231 wheelEvent->setDefaultHandled();
2232
2233 if (!m_latchedWheelEventNode)
2234 m_previousWheelScrolledNode = stopNode;
2235 }
2236
handleGestureShowPress()2237 bool EventHandler::handleGestureShowPress()
2238 {
2239 m_lastShowPressTimestamp = WTF::currentTime();
2240
2241 FrameView* view = m_frame->view();
2242 if (!view)
2243 return false;
2244 if (ScrollAnimator* scrollAnimator = view->existingScrollAnimator())
2245 scrollAnimator->cancelAnimations();
2246 const FrameView::ScrollableAreaSet* areas = view->scrollableAreas();
2247 if (!areas)
2248 return false;
2249 for (FrameView::ScrollableAreaSet::const_iterator it = areas->begin(); it != areas->end(); ++it) {
2250 ScrollableArea* sa = *it;
2251 ScrollAnimator* animator = sa->scrollAnimator();
2252 if (animator)
2253 animator->cancelAnimations();
2254 }
2255 return false;
2256 }
2257
handleGestureEvent(const PlatformGestureEvent & gestureEvent)2258 bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
2259 {
2260 IntPoint adjustedPoint = gestureEvent.position();
2261 RefPtr<Frame> subframe = 0;
2262 switch (gestureEvent.type()) {
2263 case PlatformEvent::GestureScrollBegin:
2264 case PlatformEvent::GestureScrollUpdate:
2265 case PlatformEvent::GestureScrollUpdateWithoutPropagation:
2266 case PlatformEvent::GestureScrollEnd:
2267 case PlatformEvent::GestureFlingStart:
2268 // Handle directly in main frame
2269 break;
2270
2271 case PlatformEvent::GestureTap:
2272 case PlatformEvent::GestureTapUnconfirmed:
2273 case PlatformEvent::GestureTapDown:
2274 case PlatformEvent::GestureShowPress:
2275 case PlatformEvent::GestureTapDownCancel:
2276 case PlatformEvent::GestureTwoFingerTap:
2277 case PlatformEvent::GestureLongPress:
2278 case PlatformEvent::GestureLongTap:
2279 case PlatformEvent::GesturePinchBegin:
2280 case PlatformEvent::GesturePinchEnd:
2281 case PlatformEvent::GesturePinchUpdate:
2282 adjustGesturePosition(gestureEvent, adjustedPoint);
2283 subframe = getSubFrameForGestureEvent(adjustedPoint, gestureEvent);
2284 if (subframe)
2285 return subframe->eventHandler().handleGestureEvent(gestureEvent);
2286 break;
2287
2288 default:
2289 ASSERT_NOT_REACHED();
2290 }
2291
2292 Node* eventTarget = 0;
2293 Scrollbar* scrollbar = 0;
2294 if (gestureEvent.type() == PlatformEvent::GestureScrollEnd
2295 || gestureEvent.type() == PlatformEvent::GestureScrollUpdate
2296 || gestureEvent.type() == PlatformEvent::GestureScrollUpdateWithoutPropagation
2297 || gestureEvent.type() == PlatformEvent::GestureFlingStart) {
2298 scrollbar = m_scrollbarHandlingScrollGesture.get();
2299 eventTarget = m_scrollGestureHandlingNode.get();
2300 }
2301
2302 HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent;
2303 double activeInterval = 0;
2304 bool shouldKeepActiveForMinInterval = false;
2305 if (gestureEvent.type() == PlatformEvent::GestureShowPress
2306 || gestureEvent.type() == PlatformEvent::GestureTapUnconfirmed) {
2307 hitType |= HitTestRequest::Active;
2308 } else if (gestureEvent.type() == PlatformEvent::GestureTapDownCancel) {
2309 hitType |= HitTestRequest::Release;
2310 // A TapDownCancel received when no element is active shouldn't really be changing hover state.
2311 if (!m_frame->document()->activeElement())
2312 hitType |= HitTestRequest::ReadOnly;
2313 } else if (gestureEvent.type() == PlatformEvent::GestureTap) {
2314 hitType |= HitTestRequest::Release;
2315 // If the Tap is received very shortly after ShowPress, we want to delay clearing
2316 // of the active state so that it's visible to the user for at least one frame.
2317 activeInterval = WTF::currentTime() - m_lastShowPressTimestamp;
2318 shouldKeepActiveForMinInterval = m_lastShowPressTimestamp && activeInterval < minimumActiveInterval;
2319 if (shouldKeepActiveForMinInterval)
2320 hitType |= HitTestRequest::ReadOnly;
2321 }
2322 else
2323 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
2324
2325 if ((!scrollbar && !eventTarget) || !(hitType & HitTestRequest::ReadOnly)) {
2326 IntPoint hitTestPoint = m_frame->view()->windowToContents(adjustedPoint);
2327 HitTestResult result = hitTestResultAtPoint(hitTestPoint, hitType | HitTestRequest::AllowFrameScrollbars);
2328
2329 if (shouldKeepActiveForMinInterval) {
2330 m_lastDeferredTapElement = result.innerElement();
2331 m_activeIntervalTimer.startOneShot(minimumActiveInterval - activeInterval);
2332 }
2333
2334 eventTarget = result.targetNode();
2335 if (!scrollbar) {
2336 FrameView* view = m_frame->view();
2337 scrollbar = view ? view->scrollbarAtPoint(gestureEvent.position()) : 0;
2338 }
2339 if (!scrollbar)
2340 scrollbar = result.scrollbar();
2341 }
2342
2343 if (scrollbar) {
2344 bool eventSwallowed = scrollbar->gestureEvent(gestureEvent);
2345 if (gestureEvent.type() == PlatformEvent::GestureTapDown && eventSwallowed) {
2346 m_scrollbarHandlingScrollGesture = scrollbar;
2347 } else if (gestureEvent.type() == PlatformEvent::GestureScrollEnd
2348 || gestureEvent.type() == PlatformEvent::GestureFlingStart
2349 || !eventSwallowed) {
2350 m_scrollbarHandlingScrollGesture = 0;
2351 }
2352
2353 if (eventSwallowed)
2354 return true;
2355 }
2356
2357 if (eventTarget) {
2358 bool eventSwallowed = false;
2359 if (handleScrollGestureOnResizer(eventTarget, gestureEvent))
2360 eventSwallowed = true;
2361 else
2362 eventSwallowed = eventTarget->dispatchGestureEvent(gestureEvent);
2363 if (gestureEvent.type() == PlatformEvent::GestureScrollBegin || gestureEvent.type() == PlatformEvent::GestureScrollEnd) {
2364 if (eventSwallowed)
2365 m_scrollGestureHandlingNode = eventTarget;
2366 }
2367
2368 if (eventSwallowed)
2369 return true;
2370 }
2371
2372 // FIXME: A more general scroll system (https://bugs.webkit.org/show_bug.cgi?id=80596) will
2373 // eliminate the need for this.
2374 TemporaryChange<PlatformEvent::Type> baseEventType(m_baseEventType, gestureEvent.type());
2375
2376 switch (gestureEvent.type()) {
2377 case PlatformEvent::GestureScrollBegin:
2378 return handleGestureScrollBegin(gestureEvent);
2379 case PlatformEvent::GestureScrollUpdate:
2380 case PlatformEvent::GestureScrollUpdateWithoutPropagation:
2381 return handleGestureScrollUpdate(gestureEvent);
2382 case PlatformEvent::GestureScrollEnd:
2383 return handleGestureScrollEnd(gestureEvent);
2384 case PlatformEvent::GestureTap:
2385 return handleGestureTap(gestureEvent, adjustedPoint);
2386 case PlatformEvent::GestureShowPress:
2387 return handleGestureShowPress();
2388 case PlatformEvent::GestureLongPress:
2389 return handleGestureLongPress(gestureEvent, adjustedPoint);
2390 case PlatformEvent::GestureLongTap:
2391 return handleGestureLongTap(gestureEvent, adjustedPoint);
2392 case PlatformEvent::GestureTwoFingerTap:
2393 return handleGestureTwoFingerTap(gestureEvent, adjustedPoint);
2394 case PlatformEvent::GestureTapDown:
2395 case PlatformEvent::GesturePinchBegin:
2396 case PlatformEvent::GesturePinchEnd:
2397 case PlatformEvent::GesturePinchUpdate:
2398 case PlatformEvent::GestureTapDownCancel:
2399 case PlatformEvent::GestureTapUnconfirmed:
2400 case PlatformEvent::GestureFlingStart:
2401 break;
2402 default:
2403 ASSERT_NOT_REACHED();
2404 }
2405
2406 return false;
2407 }
2408
handleGestureTap(const PlatformGestureEvent & gestureEvent,const IntPoint & adjustedPoint)2409 bool EventHandler::handleGestureTap(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint)
2410 {
2411 // 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.
2412
2413 unsigned modifierFlags = 0;
2414 if (gestureEvent.altKey())
2415 modifierFlags |= PlatformEvent::AltKey;
2416 if (gestureEvent.ctrlKey())
2417 modifierFlags |= PlatformEvent::CtrlKey;
2418 if (gestureEvent.metaKey())
2419 modifierFlags |= PlatformEvent::MetaKey;
2420 if (gestureEvent.shiftKey())
2421 modifierFlags |= PlatformEvent::ShiftKey;
2422 PlatformEvent::Modifiers modifiers = static_cast<PlatformEvent::Modifiers>(modifierFlags);
2423
2424 PlatformMouseEvent fakeMouseMove(adjustedPoint, gestureEvent.globalPosition(),
2425 NoButton, PlatformEvent::MouseMoved, /* clickCount */ 0,
2426 modifiers, PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
2427 handleMouseMoveEvent(fakeMouseMove);
2428
2429 bool defaultPrevented = false;
2430 PlatformMouseEvent fakeMouseDown(adjustedPoint, gestureEvent.globalPosition(),
2431 LeftButton, PlatformEvent::MousePressed, gestureEvent.tapCount(),
2432 modifiers, PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
2433 defaultPrevented |= handleMousePressEvent(fakeMouseDown);
2434
2435 PlatformMouseEvent fakeMouseUp(adjustedPoint, gestureEvent.globalPosition(),
2436 LeftButton, PlatformEvent::MouseReleased, gestureEvent.tapCount(),
2437 modifiers, PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
2438 defaultPrevented |= handleMouseReleaseEvent(fakeMouseUp);
2439
2440 return defaultPrevented;
2441 }
2442
handleGestureLongPress(const PlatformGestureEvent & gestureEvent,const IntPoint & adjustedPoint)2443 bool EventHandler::handleGestureLongPress(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint)
2444 {
2445 m_longTapShouldInvokeContextMenu = false;
2446 if (m_frame->settings() && m_frame->settings()->touchDragDropEnabled() && m_frame->view()) {
2447 PlatformMouseEvent mouseDownEvent(adjustedPoint, gestureEvent.globalPosition(), LeftButton, PlatformEvent::MousePressed, 1,
2448 gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), WTF::currentTime());
2449 m_mouseDown = mouseDownEvent;
2450
2451 PlatformMouseEvent mouseDragEvent(adjustedPoint, gestureEvent.globalPosition(), LeftButton, PlatformEvent::MouseMoved, 1,
2452 gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), WTF::currentTime());
2453 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
2454 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseDragEvent);
2455 m_didStartDrag = false;
2456 m_mouseDownMayStartDrag = true;
2457 dragState().m_dragSrc = 0;
2458 m_mouseDownPos = m_frame->view()->windowToContents(mouseDragEvent.position());
2459 RefPtr<FrameView> protector(m_frame->view());
2460 handleDrag(mev, DontCheckDragHysteresis);
2461 if (m_didStartDrag) {
2462 m_longTapShouldInvokeContextMenu = true;
2463 return true;
2464 }
2465 }
2466 #if OS(ANDROID)
2467 bool shouldLongPressSelectWord = true;
2468 #else
2469 bool shouldLongPressSelectWord = m_frame->settings() && m_frame->settings()->touchEditingEnabled();
2470 #endif
2471 if (shouldLongPressSelectWord) {
2472 IntPoint hitTestPoint = m_frame->view()->windowToContents(gestureEvent.position());
2473 HitTestResult result = hitTestResultAtPoint(hitTestPoint);
2474 Node* innerNode = result.targetNode();
2475 if (!result.isLiveLink() && innerNode && (innerNode->isContentEditable() || innerNode->isTextNode())) {
2476 selectClosestWordFromHitTestResult(result, DontAppendTrailingWhitespace);
2477 if (m_frame->selection().isRange()) {
2478 focusDocumentView();
2479 return true;
2480 }
2481 }
2482 }
2483 return sendContextMenuEventForGesture(gestureEvent);
2484 }
2485
handleGestureLongTap(const PlatformGestureEvent & gestureEvent,const IntPoint & adjustedPoint)2486 bool EventHandler::handleGestureLongTap(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint)
2487 {
2488 #if !OS(ANDROID)
2489 if (m_longTapShouldInvokeContextMenu) {
2490 m_longTapShouldInvokeContextMenu = false;
2491 return sendContextMenuEventForGesture(gestureEvent);
2492 }
2493 #endif
2494 return false;
2495 }
2496
handleScrollGestureOnResizer(Node * eventTarget,const PlatformGestureEvent & gestureEvent)2497 bool EventHandler::handleScrollGestureOnResizer(Node* eventTarget, const PlatformGestureEvent& gestureEvent) {
2498 if (gestureEvent.type() == PlatformEvent::GestureScrollBegin) {
2499 RenderLayer* layer = eventTarget->renderer() ? eventTarget->renderer()->enclosingLayer() : 0;
2500 IntPoint p = m_frame->view()->windowToContents(gestureEvent.position());
2501 if (layer && layer->scrollableArea() && layer->scrollableArea()->isPointInResizeControl(p, ResizerForTouch)) {
2502 m_resizeScrollableArea = layer->scrollableArea();
2503 m_resizeScrollableArea->setInResizeMode(true);
2504 m_offsetFromResizeCorner = m_resizeScrollableArea->offsetFromResizeCorner(p);
2505 return true;
2506 }
2507 } else if (gestureEvent.type() == PlatformEvent::GestureScrollUpdate ||
2508 gestureEvent.type() == PlatformEvent::GestureScrollUpdateWithoutPropagation) {
2509 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode()) {
2510 m_resizeScrollableArea->resize(gestureEvent, m_offsetFromResizeCorner);
2511 return true;
2512 }
2513 } else if (gestureEvent.type() == PlatformEvent::GestureScrollEnd) {
2514 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode()) {
2515 m_resizeScrollableArea->setInResizeMode(false);
2516 m_resizeScrollableArea = 0;
2517 return false;
2518 }
2519 }
2520
2521 return false;
2522 }
2523
handleGestureTwoFingerTap(const PlatformGestureEvent & gestureEvent,const IntPoint & adjustedPoint)2524 bool EventHandler::handleGestureTwoFingerTap(const PlatformGestureEvent& gestureEvent, const IntPoint& adjustedPoint)
2525 {
2526 return sendContextMenuEventForGesture(gestureEvent);
2527 }
2528
passGestureEventToWidget(const PlatformGestureEvent & gestureEvent,Widget * widget)2529 bool EventHandler::passGestureEventToWidget(const PlatformGestureEvent& gestureEvent, Widget* widget)
2530 {
2531 if (!widget)
2532 return false;
2533
2534 if (!widget->isFrameView())
2535 return false;
2536
2537 return toFrameView(widget)->frame().eventHandler().handleGestureEvent(gestureEvent);
2538 }
2539
passGestureEventToWidgetIfPossible(const PlatformGestureEvent & gestureEvent,RenderObject * renderer)2540 bool EventHandler::passGestureEventToWidgetIfPossible(const PlatformGestureEvent& gestureEvent, RenderObject* renderer)
2541 {
2542 if (m_lastHitTestResultOverWidget && renderer && renderer->isWidget()) {
2543 Widget* widget = toRenderWidget(renderer)->widget();
2544 return widget && passGestureEventToWidget(gestureEvent, widget);
2545 }
2546 return false;
2547 }
2548
handleGestureScrollEnd(const PlatformGestureEvent & gestureEvent)2549 bool EventHandler::handleGestureScrollEnd(const PlatformGestureEvent& gestureEvent) {
2550 RefPtr<Node> node = m_scrollGestureHandlingNode;
2551 clearGestureScrollNodes();
2552
2553 if (node) {
2554 ASSERT(node->refCount() > 0);
2555 passGestureEventToWidgetIfPossible(gestureEvent, node->renderer());
2556 }
2557
2558 return false;
2559 }
2560
handleGestureScrollBegin(const PlatformGestureEvent & gestureEvent)2561 bool EventHandler::handleGestureScrollBegin(const PlatformGestureEvent& gestureEvent)
2562 {
2563 Document* document = m_frame->document();
2564 if (!document->renderView())
2565 return false;
2566
2567 FrameView* view = m_frame->view();
2568 if (!view)
2569 return false;
2570
2571 LayoutPoint viewPoint = view->windowToContents(gestureEvent.position());
2572 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
2573 HitTestResult result(viewPoint);
2574 document->renderView()->hitTest(request, result);
2575
2576 m_lastHitTestResultOverWidget = result.isOverWidget();
2577 m_scrollGestureHandlingNode = result.innerNode();
2578 m_previousGestureScrolledNode = 0;
2579
2580 // If there's no renderer on the node, send the event to the nearest ancestor with a renderer.
2581 // Needed for <option> and <optgroup> elements so we can touch scroll <select>s
2582 while (m_scrollGestureHandlingNode && !m_scrollGestureHandlingNode->renderer())
2583 m_scrollGestureHandlingNode = m_scrollGestureHandlingNode->parentOrShadowHostNode();
2584
2585 if (!m_scrollGestureHandlingNode)
2586 return false;
2587
2588 passGestureEventToWidgetIfPossible(gestureEvent, m_scrollGestureHandlingNode->renderer());
2589
2590 return true;
2591 }
2592
handleGestureScrollUpdate(const PlatformGestureEvent & gestureEvent)2593 bool EventHandler::handleGestureScrollUpdate(const PlatformGestureEvent& gestureEvent)
2594 {
2595 FloatSize delta(gestureEvent.deltaX(), gestureEvent.deltaY());
2596 if (delta.isZero())
2597 return false;
2598
2599 const float scaleFactor = m_frame->pageZoomFactor();
2600 delta.scale(1 / scaleFactor, 1 / scaleFactor);
2601
2602 Node* node = m_scrollGestureHandlingNode.get();
2603 if (!node)
2604 return sendScrollEventToView(gestureEvent, delta);
2605
2606 // Ignore this event if the targeted node does not have a valid renderer.
2607 RenderObject* renderer = node->renderer();
2608 if (!renderer)
2609 return false;
2610
2611 RefPtr<FrameView> protector(m_frame->view());
2612
2613 Node* stopNode = 0;
2614 bool scrollShouldNotPropagate = gestureEvent.type() == PlatformEvent::GestureScrollUpdateWithoutPropagation;
2615
2616 // Try to send the event to the correct view.
2617 if (passGestureEventToWidgetIfPossible(gestureEvent, renderer)) {
2618 if(scrollShouldNotPropagate)
2619 m_previousGestureScrolledNode = m_scrollGestureHandlingNode;
2620
2621 return true;
2622 }
2623
2624 if (scrollShouldNotPropagate)
2625 stopNode = m_previousGestureScrolledNode.get();
2626
2627 // First try to scroll the closest scrollable RenderBox ancestor of |node|.
2628 ScrollGranularity granularity = ScrollByPixel;
2629 bool horizontalScroll = scrollNode(delta.width(), granularity, ScrollLeft, node, &stopNode);
2630 bool verticalScroll = scrollNode(delta.height(), granularity, ScrollUp, node, &stopNode);
2631
2632 if (scrollShouldNotPropagate)
2633 m_previousGestureScrolledNode = stopNode;
2634
2635 if (horizontalScroll || verticalScroll) {
2636 setFrameWasScrolledByUser();
2637 return true;
2638 }
2639
2640 // Otherwise try to scroll the view.
2641 return sendScrollEventToView(gestureEvent, delta);
2642 }
2643
sendScrollEventToView(const PlatformGestureEvent & gestureEvent,const FloatSize & scaledDelta)2644 bool EventHandler::sendScrollEventToView(const PlatformGestureEvent& gestureEvent, const FloatSize& scaledDelta)
2645 {
2646 FrameView* view = m_frame->view();
2647 if (!view)
2648 return false;
2649
2650 const float tickDivisor = static_cast<float>(WheelEvent::TickMultiplier);
2651 IntPoint point(gestureEvent.position().x(), gestureEvent.position().y());
2652 IntPoint globalPoint(gestureEvent.globalPosition().x(), gestureEvent.globalPosition().y());
2653 PlatformWheelEvent syntheticWheelEvent(point, globalPoint,
2654 scaledDelta.width(), scaledDelta.height(),
2655 scaledDelta.width() / tickDivisor, scaledDelta.height() / tickDivisor,
2656 ScrollByPixelWheelEvent,
2657 gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey());
2658 syntheticWheelEvent.setHasPreciseScrollingDeltas(true);
2659
2660 bool scrolledFrame = view->wheelEvent(syntheticWheelEvent);
2661 if (scrolledFrame)
2662 setFrameWasScrolledByUser();
2663
2664 return scrolledFrame;
2665 }
2666
getSubFrameForGestureEvent(const IntPoint & touchAdjustedPoint,const PlatformGestureEvent & gestureEvent)2667 Frame* EventHandler::getSubFrameForGestureEvent(const IntPoint& touchAdjustedPoint, const PlatformGestureEvent& gestureEvent)
2668 {
2669 PlatformMouseEvent mouseDown(touchAdjustedPoint, gestureEvent.globalPosition(), LeftButton, PlatformEvent::MousePressed, 1,
2670 gestureEvent.shiftKey(), gestureEvent.ctrlKey(), gestureEvent.altKey(), gestureEvent.metaKey(), gestureEvent.timestamp());
2671 HitTestRequest request(HitTestRequest::ReadOnly);
2672 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseDown);
2673 return subframeForHitTestResult(mev);
2674 }
2675
clearGestureScrollNodes()2676 void EventHandler::clearGestureScrollNodes()
2677 {
2678 m_scrollGestureHandlingNode = 0;
2679 m_previousGestureScrolledNode = 0;
2680 }
2681
isScrollbarHandlingGestures() const2682 bool EventHandler::isScrollbarHandlingGestures() const
2683 {
2684 return m_scrollbarHandlingScrollGesture.get();
2685 }
2686
shouldApplyTouchAdjustment(const PlatformGestureEvent & event) const2687 bool EventHandler::shouldApplyTouchAdjustment(const PlatformGestureEvent& event) const
2688 {
2689 if (m_frame->settings() && !m_frame->settings()->touchAdjustmentEnabled())
2690 return false;
2691 return !event.area().isEmpty();
2692 }
2693
2694
bestClickableNodeForTouchPoint(const IntPoint & touchCenter,const IntSize & touchRadius,IntPoint & targetPoint,Node * & targetNode)2695 bool EventHandler::bestClickableNodeForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntPoint& targetPoint, Node*& targetNode)
2696 {
2697 IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter);
2698 HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active, touchRadius);
2699
2700 // If the touch is over a scrollbar, don't adjust the touch point since touch adjustment only takes into account
2701 // DOM nodes so a touch over a scrollbar will be adjusted towards nearby nodes. This leads to things like textarea
2702 // scrollbars being untouchable.
2703 if (result.scrollbar())
2704 return false;
2705
2706 IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius);
2707 Vector<RefPtr<Node>, 11> nodes;
2708 copyToVector(result.rectBasedTestResult(), nodes);
2709
2710 // FIXME: Should be able to handle targetNode being a shadow DOM node to avoid performing uncessary hit tests
2711 // in the case where further processing on the node is required. Returning the shadow ancestor prevents a
2712 // regression in touchadjustment/html-label.html. Some refinement is required to testing/internals to
2713 // handle targetNode being a shadow DOM node.
2714
2715 // FIXME: the explicit Vector conversion copies into a temporary and is
2716 // wasteful.
2717 bool success = findBestClickableCandidate(targetNode, targetPoint, touchCenter, touchRect, Vector<RefPtr<Node> > (nodes));
2718 if (success && targetNode)
2719 targetNode = targetNode->deprecatedShadowAncestorNode();
2720 return success;
2721 }
2722
bestContextMenuNodeForTouchPoint(const IntPoint & touchCenter,const IntSize & touchRadius,IntPoint & targetPoint,Node * & targetNode)2723 bool EventHandler::bestContextMenuNodeForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntPoint& targetPoint, Node*& targetNode)
2724 {
2725 IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter);
2726 HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active, touchRadius);
2727
2728 IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius);
2729 Vector<RefPtr<Node>, 11> nodes;
2730 copyToVector(result.rectBasedTestResult(), nodes);
2731
2732 // FIXME: the explicit Vector conversion copies into a temporary and is
2733 // wasteful.
2734 return findBestContextMenuCandidate(targetNode, targetPoint, touchCenter, touchRect, Vector<RefPtr<Node> >(nodes));
2735 }
2736
bestZoomableAreaForTouchPoint(const IntPoint & touchCenter,const IntSize & touchRadius,IntRect & targetArea,Node * & targetNode)2737 bool EventHandler::bestZoomableAreaForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntRect& targetArea, Node*& targetNode)
2738 {
2739 IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter);
2740 HitTestResult result = hitTestResultAtPoint(hitTestPoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent, touchRadius);
2741
2742 IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius);
2743 Vector<RefPtr<Node>, 11> nodes;
2744 copyToVector(result.rectBasedTestResult(), nodes);
2745
2746 // FIXME: the explicit Vector conversion copies into a temporary and is
2747 // wasteful.
2748 return findBestZoomableArea(targetNode, targetArea, touchCenter, touchRect, Vector<RefPtr<Node> >(nodes));
2749 }
2750
adjustGesturePosition(const PlatformGestureEvent & gestureEvent,IntPoint & adjustedPoint)2751 bool EventHandler::adjustGesturePosition(const PlatformGestureEvent& gestureEvent, IntPoint& adjustedPoint)
2752 {
2753 if (!shouldApplyTouchAdjustment(gestureEvent))
2754 return false;
2755
2756 Node* targetNode = 0;
2757 switch (gestureEvent.type()) {
2758 case PlatformEvent::GestureTap:
2759 case PlatformEvent::GestureTapUnconfirmed:
2760 case PlatformEvent::GestureTapDown:
2761 case PlatformEvent::GestureShowPress:
2762 bestClickableNodeForTouchPoint(gestureEvent.position(), IntSize(gestureEvent.area().width() / 2, gestureEvent.area().height() / 2), adjustedPoint, targetNode);
2763 break;
2764 case PlatformEvent::GestureLongPress:
2765 case PlatformEvent::GestureLongTap:
2766 case PlatformEvent::GestureTwoFingerTap:
2767 bestContextMenuNodeForTouchPoint(gestureEvent.position(), IntSize(gestureEvent.area().width() / 2, gestureEvent.area().height() / 2), adjustedPoint, targetNode);
2768 break;
2769 default:
2770 // FIXME: Implement handling for other types as needed.
2771 ASSERT_NOT_REACHED();
2772 }
2773 return targetNode;
2774 }
2775
sendContextMenuEvent(const PlatformMouseEvent & event)2776 bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event)
2777 {
2778 Document* doc = m_frame->document();
2779 FrameView* v = m_frame->view();
2780 if (!v)
2781 return false;
2782
2783 // Clear mouse press state to avoid initiating a drag while context menu is up.
2784 m_mousePressed = false;
2785 bool swallowEvent;
2786 LayoutPoint viewportPos = v->windowToContents(event.position());
2787 HitTestRequest request(HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
2788 MouseEventWithHitTestResults mev = doc->prepareMouseEvent(request, viewportPos, event);
2789
2790 if (!m_frame->selection().contains(viewportPos)
2791 && !mev.scrollbar()
2792 // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse.
2793 // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items
2794 // available for text selections. But only if we're above text.
2795 && (m_frame->selection().isContentEditable() || (mev.targetNode() && mev.targetNode()->isTextNode()))) {
2796 m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
2797
2798 if (mev.hitTestResult().isMisspelled())
2799 selectClosestMisspellingFromMouseEvent(mev);
2800 else if (m_frame->editor().behavior().shouldSelectOnContextualMenuClick())
2801 selectClosestWordOrLinkFromMouseEvent(mev);
2802 }
2803
2804 swallowEvent = !dispatchMouseEvent(EventTypeNames::contextmenu, mev.targetNode(), true, 0, event, false);
2805
2806 return swallowEvent;
2807 }
2808
sendContextMenuEventForKey()2809 bool EventHandler::sendContextMenuEventForKey()
2810 {
2811 FrameView* view = m_frame->view();
2812 if (!view)
2813 return false;
2814
2815 Document* doc = m_frame->document();
2816 if (!doc)
2817 return false;
2818
2819 // Clear mouse press state to avoid initiating a drag while context menu is up.
2820 m_mousePressed = false;
2821
2822 static const int kContextMenuMargin = 1;
2823
2824 #if OS(WIN)
2825 int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
2826 #else
2827 int rightAligned = 0;
2828 #endif
2829 IntPoint location;
2830
2831 Element* focusedElement = doc->focusedElement();
2832 FrameSelection& selection = m_frame->selection();
2833 Position start = selection.selection().start();
2834
2835 if (start.deprecatedNode() && (selection.rootEditableElement() || selection.isRange())) {
2836 RefPtr<Range> selectionRange = selection.toNormalizedRange();
2837 IntRect firstRect = m_frame->editor().firstRectForRange(selectionRange.get());
2838
2839 int x = rightAligned ? firstRect.maxX() : firstRect.x();
2840 // In a multiline edit, firstRect.maxY() would endup on the next line, so -1.
2841 int y = firstRect.maxY() ? firstRect.maxY() - 1 : 0;
2842 location = IntPoint(x, y);
2843 } else if (focusedElement) {
2844 RenderBoxModelObject* box = focusedElement->renderBoxModelObject();
2845 if (!box)
2846 return false;
2847 IntRect clippedRect = box->pixelSnappedAbsoluteClippedOverflowRect();
2848 location = IntPoint(clippedRect.x(), clippedRect.maxY() - 1);
2849 } else {
2850 location = IntPoint(
2851 rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin,
2852 kContextMenuMargin);
2853 }
2854
2855 m_frame->view()->setCursor(pointerCursor());
2856
2857 IntPoint position = view->contentsToRootView(location);
2858 IntPoint globalPosition = view->hostWindow()->rootViewToScreen(IntRect(position, IntSize())).location();
2859
2860 Node* targetNode = doc->focusedElement();
2861 if (!targetNode)
2862 targetNode = doc;
2863
2864 // Use the focused node as the target for hover and active.
2865 HitTestResult result(position);
2866 result.setInnerNode(targetNode);
2867 doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent, result.innerElement());
2868
2869 // The contextmenu event is a mouse event even when invoked using the keyboard.
2870 // This is required for web compatibility.
2871
2872 #if OS(WIN)
2873 PlatformEvent::Type eventType = PlatformEvent::MouseReleased;
2874 #else
2875 PlatformEvent::Type eventType = PlatformEvent::MousePressed;
2876 #endif
2877
2878 PlatformMouseEvent mouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WTF::currentTime());
2879
2880 return !dispatchMouseEvent(EventTypeNames::contextmenu, targetNode, true, 0, mouseEvent, false);
2881 }
2882
sendContextMenuEventForGesture(const PlatformGestureEvent & event)2883 bool EventHandler::sendContextMenuEventForGesture(const PlatformGestureEvent& event)
2884 {
2885 #if OS(WIN)
2886 PlatformEvent::Type eventType = PlatformEvent::MouseReleased;
2887 #else
2888 PlatformEvent::Type eventType = PlatformEvent::MousePressed;
2889 #endif
2890
2891 IntPoint adjustedPoint = event.position();
2892 adjustGesturePosition(event, adjustedPoint);
2893 PlatformMouseEvent mouseEvent(adjustedPoint, event.globalPosition(), RightButton, eventType, 1, false, false, false, false, WTF::currentTime());
2894 // To simulate right-click behavior, we send a right mouse down and then
2895 // context menu event.
2896 handleMousePressEvent(mouseEvent);
2897 return sendContextMenuEvent(mouseEvent);
2898 // We do not need to send a corresponding mouse release because in case of
2899 // right-click, the context menu takes capture and consumes all events.
2900 }
2901
scheduleHoverStateUpdate()2902 void EventHandler::scheduleHoverStateUpdate()
2903 {
2904 if (!m_hoverTimer.isActive())
2905 m_hoverTimer.startOneShot(0);
2906 }
2907
scheduleCursorUpdate()2908 void EventHandler::scheduleCursorUpdate()
2909 {
2910 if (!m_cursorUpdateTimer.isActive())
2911 m_cursorUpdateTimer.startOneShot(cursorUpdateInterval);
2912 }
2913
dispatchFakeMouseMoveEventSoon()2914 void EventHandler::dispatchFakeMouseMoveEventSoon()
2915 {
2916 if (m_mousePressed)
2917 return;
2918
2919 if (m_mousePositionIsUnknown)
2920 return;
2921
2922 Settings* settings = m_frame->settings();
2923 if (settings && !settings->deviceSupportsMouse())
2924 return;
2925
2926 // If the content has ever taken longer than fakeMouseMoveShortInterval we
2927 // reschedule the timer and use a longer time. This will cause the content
2928 // to receive these moves only after the user is done scrolling, reducing
2929 // pauses during the scroll.
2930 if (m_maxMouseMovedDuration > fakeMouseMoveShortInterval) {
2931 if (m_fakeMouseMoveEventTimer.isActive())
2932 m_fakeMouseMoveEventTimer.stop();
2933 m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveLongInterval);
2934 } else {
2935 if (!m_fakeMouseMoveEventTimer.isActive())
2936 m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveShortInterval);
2937 }
2938 }
2939
dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad & quad)2940 void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad)
2941 {
2942 FrameView* view = m_frame->view();
2943 if (!view)
2944 return;
2945
2946 if (!quad.containsPoint(view->windowToContents(m_lastKnownMousePosition)))
2947 return;
2948
2949 dispatchFakeMouseMoveEventSoon();
2950 }
2951
fakeMouseMoveEventTimerFired(Timer<EventHandler> * timer)2952 void EventHandler::fakeMouseMoveEventTimerFired(Timer<EventHandler>* timer)
2953 {
2954 ASSERT_UNUSED(timer, timer == &m_fakeMouseMoveEventTimer);
2955 ASSERT(!m_mousePressed);
2956
2957 Settings* settings = m_frame->settings();
2958 if (settings && !settings->deviceSupportsMouse())
2959 return;
2960
2961 FrameView* view = m_frame->view();
2962 if (!view)
2963 return;
2964
2965 if (!m_frame->page() || !m_frame->page()->focusController().isActive())
2966 return;
2967
2968 // Don't dispatch a synthetic mouse move event if the mouse cursor is not visible to the user.
2969 if (!isCursorVisible())
2970 return;
2971
2972 bool shiftKey;
2973 bool ctrlKey;
2974 bool altKey;
2975 bool metaKey;
2976 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey);
2977 PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, currentTime());
2978 handleMouseMoveEvent(fakeMouseMoveEvent);
2979 }
2980
cancelFakeMouseMoveEvent()2981 void EventHandler::cancelFakeMouseMoveEvent()
2982 {
2983 m_fakeMouseMoveEventTimer.stop();
2984 }
2985
isCursorVisible() const2986 bool EventHandler::isCursorVisible() const
2987 {
2988 return m_frame->page()->isCursorVisible();
2989 }
2990
setResizingFrameSet(HTMLFrameSetElement * frameSet)2991 void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet)
2992 {
2993 m_frameSetBeingResized = frameSet;
2994 }
2995
resizeScrollableAreaDestroyed()2996 void EventHandler::resizeScrollableAreaDestroyed()
2997 {
2998 ASSERT(m_resizeScrollableArea);
2999 m_resizeScrollableArea = 0;
3000 }
3001
hoverTimerFired(Timer<EventHandler> *)3002 void EventHandler::hoverTimerFired(Timer<EventHandler>*)
3003 {
3004 m_hoverTimer.stop();
3005
3006 ASSERT(m_frame);
3007 ASSERT(m_frame->document());
3008
3009 if (RenderView* renderer = m_frame->contentRenderer()) {
3010 if (FrameView* view = m_frame->view()) {
3011 HitTestRequest request(HitTestRequest::Move | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
3012 HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
3013 renderer->hitTest(request, result);
3014 m_frame->document()->updateHoverActiveState(request, result.innerElement());
3015 }
3016 }
3017 }
3018
activeIntervalTimerFired(Timer<EventHandler> *)3019 void EventHandler::activeIntervalTimerFired(Timer<EventHandler>*)
3020 {
3021 m_activeIntervalTimer.stop();
3022
3023 if (m_frame
3024 && m_frame->document()
3025 && m_lastDeferredTapElement) {
3026 // FIXME: Enable condition when http://crbug.com/226842 lands
3027 // m_lastDeferredTapElement.get() == m_frame->document()->activeElement()
3028 HitTestRequest request(HitTestRequest::TouchEvent | HitTestRequest::Release);
3029 m_frame->document()->updateHoverActiveState(request, m_lastDeferredTapElement.get());
3030 }
3031 m_lastDeferredTapElement = 0;
3032 }
3033
notifyElementActivated()3034 void EventHandler::notifyElementActivated()
3035 {
3036 // Since another element has been set to active, stop current timer and clear reference.
3037 if (m_activeIntervalTimer.isActive())
3038 m_activeIntervalTimer.stop();
3039 m_lastDeferredTapElement = 0;
3040 }
3041
handleAccessKey(const PlatformKeyboardEvent & evt)3042 bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& evt)
3043 {
3044 // FIXME: Ignoring the state of Shift key is what neither IE nor Firefox do.
3045 // IE matches lower and upper case access keys regardless of Shift key state - but if both upper and
3046 // lower case variants are present in a document, the correct element is matched based on Shift key state.
3047 // Firefox only matches an access key if Shift is not pressed, and does that case-insensitively.
3048 ASSERT(!(accessKeyModifiers() & PlatformEvent::ShiftKey));
3049 if ((evt.modifiers() & ~PlatformEvent::ShiftKey) != accessKeyModifiers())
3050 return false;
3051 String key = evt.unmodifiedText();
3052 Element* elem = m_frame->document()->getElementByAccessKey(key.lower());
3053 if (!elem)
3054 return false;
3055 elem->accessKeyAction(false);
3056 return true;
3057 }
3058
isKeyEventAllowedInFullScreen(FullscreenElementStack * fullscreen,const PlatformKeyboardEvent & keyEvent) const3059 bool EventHandler::isKeyEventAllowedInFullScreen(FullscreenElementStack* fullscreen, const PlatformKeyboardEvent& keyEvent) const
3060 {
3061 if (fullscreen->webkitFullScreenKeyboardInputAllowed())
3062 return true;
3063
3064 if (keyEvent.type() == PlatformKeyboardEvent::Char) {
3065 if (keyEvent.text().length() != 1)
3066 return false;
3067 UChar character = keyEvent.text()[0];
3068 return character == ' ';
3069 }
3070
3071 int keyCode = keyEvent.windowsVirtualKeyCode();
3072 return (keyCode >= VK_BACK && keyCode <= VK_CAPITAL)
3073 || (keyCode >= VK_SPACE && keyCode <= VK_DELETE)
3074 || (keyCode >= VK_OEM_1 && keyCode <= VK_OEM_PLUS)
3075 || (keyCode >= VK_MULTIPLY && keyCode <= VK_OEM_8);
3076 }
3077
keyEvent(const PlatformKeyboardEvent & initialKeyEvent)3078 bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent)
3079 {
3080 RefPtr<FrameView> protector(m_frame->view());
3081
3082 if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(m_frame->document())) {
3083 if (fullscreen->webkitIsFullScreen() && !isKeyEventAllowedInFullScreen(fullscreen, initialKeyEvent))
3084 return false;
3085 }
3086
3087 if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL)
3088 capsLockStateMayHaveChanged();
3089
3090 #if OS(WIN)
3091 if (panScrollInProgress()) {
3092 // If a key is pressed while the panScroll is in progress then we want to stop
3093 if (initialKeyEvent.type() == PlatformEvent::KeyDown || initialKeyEvent.type() == PlatformEvent::RawKeyDown)
3094 stopAutoscroll();
3095
3096 // If we were in panscroll mode, we swallow the key event
3097 return true;
3098 }
3099 #endif
3100
3101 // Check for cases where we are too early for events -- possible unmatched key up
3102 // from pressing return in the location bar.
3103 RefPtr<Node> node = eventTargetNodeForDocument(m_frame->document());
3104 if (!node)
3105 return false;
3106
3107 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
3108
3109 if (FrameView* view = m_frame->view())
3110 view->resetDeferredRepaintDelay();
3111
3112 // In IE, access keys are special, they are handled after default keydown processing, but cannot be canceled - this is hard to match.
3113 // On Mac OS X, we process them before dispatching keydown, as the default keydown handler implements Emacs key bindings, which may conflict
3114 // with access keys. Then we dispatch keydown, but suppress its default handling.
3115 // On Windows, WebKit explicitly calls handleAccessKey() instead of dispatching a keypress event for WM_SYSCHAR messages.
3116 // Other platforms currently match either Mac or Windows behavior, depending on whether they send combined KeyDown events.
3117 bool matchedAnAccessKey = false;
3118 if (initialKeyEvent.type() == PlatformEvent::KeyDown)
3119 matchedAnAccessKey = handleAccessKey(initialKeyEvent);
3120
3121 // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch.
3122 if (initialKeyEvent.type() == PlatformEvent::KeyUp || initialKeyEvent.type() == PlatformEvent::Char)
3123 return !node->dispatchKeyEvent(initialKeyEvent);
3124
3125 PlatformKeyboardEvent keyDownEvent = initialKeyEvent;
3126 if (keyDownEvent.type() != PlatformEvent::RawKeyDown)
3127 keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown);
3128 RefPtr<KeyboardEvent> keydown = KeyboardEvent::create(keyDownEvent, m_frame->document()->domWindow());
3129 if (matchedAnAccessKey)
3130 keydown->setDefaultPrevented(true);
3131 keydown->setTarget(node);
3132
3133 if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) {
3134 node->dispatchEvent(keydown, IGNORE_EXCEPTION);
3135 // If frame changed as a result of keydown dispatch, then return true to avoid sending a subsequent keypress message to the new frame.
3136 bool changedFocusedFrame = m_frame->page() && m_frame != m_frame->page()->focusController().focusedOrMainFrame();
3137 return keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3138 }
3139
3140 node->dispatchEvent(keydown, IGNORE_EXCEPTION);
3141 // If frame changed as a result of keydown dispatch, then return early to avoid sending a subsequent keypress message to the new frame.
3142 bool changedFocusedFrame = m_frame->page() && m_frame != m_frame->page()->focusController().focusedOrMainFrame();
3143 bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame;
3144 if (keydownResult)
3145 return keydownResult;
3146
3147 // Focus may have changed during keydown handling, so refetch node.
3148 // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original node.
3149 node = eventTargetNodeForDocument(m_frame->document());
3150 if (!node)
3151 return false;
3152
3153 PlatformKeyboardEvent keyPressEvent = initialKeyEvent;
3154 keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Char);
3155 if (keyPressEvent.text().isEmpty())
3156 return keydownResult;
3157 RefPtr<KeyboardEvent> keypress = KeyboardEvent::create(keyPressEvent, m_frame->document()->domWindow());
3158 keypress->setTarget(node);
3159 if (keydownResult)
3160 keypress->setDefaultPrevented(true);
3161 node->dispatchEvent(keypress, IGNORE_EXCEPTION);
3162
3163 return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled();
3164 }
3165
focusDirectionForKey(const AtomicString & keyIdentifier)3166 static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier)
3167 {
3168 DEFINE_STATIC_LOCAL(AtomicString, Down, ("Down", AtomicString::ConstructFromLiteral));
3169 DEFINE_STATIC_LOCAL(AtomicString, Up, ("Up", AtomicString::ConstructFromLiteral));
3170 DEFINE_STATIC_LOCAL(AtomicString, Left, ("Left", AtomicString::ConstructFromLiteral));
3171 DEFINE_STATIC_LOCAL(AtomicString, Right, ("Right", AtomicString::ConstructFromLiteral));
3172
3173 FocusDirection retVal = FocusDirectionNone;
3174
3175 if (keyIdentifier == Down)
3176 retVal = FocusDirectionDown;
3177 else if (keyIdentifier == Up)
3178 retVal = FocusDirectionUp;
3179 else if (keyIdentifier == Left)
3180 retVal = FocusDirectionLeft;
3181 else if (keyIdentifier == Right)
3182 retVal = FocusDirectionRight;
3183
3184 return retVal;
3185 }
3186
defaultKeyboardEventHandler(KeyboardEvent * event)3187 void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event)
3188 {
3189 if (event->type() == EventTypeNames::keydown) {
3190 m_frame->editor().handleKeyboardEvent(event);
3191 if (event->defaultHandled())
3192 return;
3193 if (event->keyIdentifier() == "U+0009")
3194 defaultTabEventHandler(event);
3195 else if (event->keyIdentifier() == "U+0008")
3196 defaultBackspaceEventHandler(event);
3197 else if (event->keyIdentifier() == "U+001B")
3198 defaultEscapeEventHandler(event);
3199 else {
3200 FocusDirection direction = focusDirectionForKey(event->keyIdentifier());
3201 if (direction != FocusDirectionNone)
3202 defaultArrowEventHandler(direction, event);
3203 }
3204 }
3205 if (event->type() == EventTypeNames::keypress) {
3206 m_frame->editor().handleKeyboardEvent(event);
3207 if (event->defaultHandled())
3208 return;
3209 if (event->charCode() == ' ')
3210 defaultSpaceEventHandler(event);
3211 }
3212 }
3213
dragHysteresisExceeded(const IntPoint & floatDragViewportLocation) const3214 bool EventHandler::dragHysteresisExceeded(const IntPoint& floatDragViewportLocation) const
3215 {
3216 FloatPoint dragViewportLocation(floatDragViewportLocation.x(), floatDragViewportLocation.y());
3217 return dragHysteresisExceeded(dragViewportLocation);
3218 }
3219
dragHysteresisExceeded(const FloatPoint & dragViewportLocation) const3220 bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation) const
3221 {
3222 FrameView* view = m_frame->view();
3223 if (!view)
3224 return false;
3225 IntPoint dragLocation = view->windowToContents(flooredIntPoint(dragViewportLocation));
3226 IntSize delta = dragLocation - m_mouseDownPos;
3227
3228 int threshold = GeneralDragHysteresis;
3229 switch (dragState().m_dragType) {
3230 case DragSourceActionSelection:
3231 threshold = TextDragHysteresis;
3232 break;
3233 case DragSourceActionImage:
3234 threshold = ImageDragHysteresis;
3235 break;
3236 case DragSourceActionLink:
3237 threshold = LinkDragHysteresis;
3238 break;
3239 case DragSourceActionDHTML:
3240 break;
3241 case DragSourceActionNone:
3242 ASSERT_NOT_REACHED();
3243 }
3244
3245 return abs(delta.width()) >= threshold || abs(delta.height()) >= threshold;
3246 }
3247
freeClipboard()3248 void EventHandler::freeClipboard()
3249 {
3250 if (dragState().m_dragClipboard)
3251 dragState().m_dragClipboard->setAccessPolicy(ClipboardNumb);
3252 }
3253
dragSourceEndedAt(const PlatformMouseEvent & event,DragOperation operation)3254 void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation)
3255 {
3256 // Send a hit test request so that RenderLayer gets a chance to update the :hover and :active pseudoclasses.
3257 HitTestRequest request(HitTestRequest::Release | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
3258 prepareMouseEvent(request, event);
3259
3260 if (dragState().m_dragSrc) {
3261 dragState().m_dragClipboard->setDestinationOperation(operation);
3262 // for now we don't care if event handler cancels default behavior, since there is none
3263 dispatchDragSrcEvent(EventTypeNames::dragend, event);
3264 }
3265 freeClipboard();
3266 dragState().m_dragSrc = 0;
3267 // In case the drag was ended due to an escape key press we need to ensure
3268 // that consecutive mousemove events don't reinitiate the drag and drop.
3269 m_mouseDownMayStartDrag = false;
3270 }
3271
updateDragStateAfterEditDragIfNeeded(Element * rootEditableElement)3272 void EventHandler::updateDragStateAfterEditDragIfNeeded(Element* rootEditableElement)
3273 {
3274 // If inserting the dragged contents removed the drag source, we still want to fire dragend at the root editble element.
3275 if (dragState().m_dragSrc && !dragState().m_dragSrc->inDocument())
3276 dragState().m_dragSrc = rootEditableElement;
3277 }
3278
3279 // returns if we should continue "default processing", i.e., whether eventhandler canceled
dispatchDragSrcEvent(const AtomicString & eventType,const PlatformMouseEvent & event)3280 bool EventHandler::dispatchDragSrcEvent(const AtomicString& eventType, const PlatformMouseEvent& event)
3281 {
3282 return !dispatchDragEvent(eventType, dragState().m_dragSrc.get(), event, dragState().m_dragClipboard.get());
3283 }
3284
handleDrag(const MouseEventWithHitTestResults & event,CheckDragHysteresis checkDragHysteresis)3285 bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis)
3286 {
3287 ASSERT(event.event().type() == PlatformEvent::MouseMoved);
3288 // Callers must protect the reference to FrameView, since this function may dispatch DOM
3289 // events, causing page/FrameView to go away.
3290 ASSERT(m_frame);
3291 ASSERT(m_frame->view());
3292 if (!m_frame->page())
3293 return false;
3294
3295 if (event.event().button() != LeftButton || event.event().type() != PlatformEvent::MouseMoved) {
3296 // If we allowed the other side of the bridge to handle a drag
3297 // last time, then m_mousePressed might still be set. So we
3298 // clear it now to make sure the next move after a drag
3299 // doesn't look like a drag.
3300 m_mousePressed = false;
3301 return false;
3302 }
3303
3304 if (m_mouseDownMayStartDrag) {
3305 HitTestRequest request(HitTestRequest::ReadOnly);
3306 HitTestResult result(m_mouseDownPos);
3307 m_frame->contentRenderer()->hitTest(request, result);
3308 Node* node = result.innerNode();
3309 if (node) {
3310 DragController::SelectionDragPolicy selectionDragPolicy = event.event().timestamp() - m_mouseDownTimestamp < TextDragDelay
3311 ? DragController::DelayedSelectionDragResolution
3312 : DragController::ImmediateSelectionDragResolution;
3313 dragState().m_dragSrc = m_frame->page()->dragController().draggableNode(m_frame, node, m_mouseDownPos, selectionDragPolicy, dragState().m_dragType);
3314 } else {
3315 dragState().m_dragSrc = 0;
3316 }
3317
3318 if (!dragState().m_dragSrc)
3319 m_mouseDownMayStartDrag = false; // no element is draggable
3320 }
3321
3322 if (!m_mouseDownMayStartDrag)
3323 return !mouseDownMayStartSelect() && !m_mouseDownMayStartAutoscroll;
3324
3325 // We are starting a text/image/url drag, so the cursor should be an arrow
3326 // FIXME <rdar://7577595>: Custom cursors aren't supported during drag and drop (default to pointer).
3327 m_frame->view()->setCursor(pointerCursor());
3328
3329 if (checkDragHysteresis == ShouldCheckDragHysteresis && !dragHysteresisExceeded(event.event().position()))
3330 return true;
3331
3332 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
3333 invalidateClick();
3334
3335 if (!tryStartDrag(event)) {
3336 // Something failed to start the drag, clean up.
3337 freeClipboard();
3338 dragState().m_dragSrc = 0;
3339 }
3340
3341 m_mouseDownMayStartDrag = false;
3342 // Whether or not the drag actually started, no more default handling (like selection).
3343 return true;
3344 }
3345
tryStartDrag(const MouseEventWithHitTestResults & event)3346 bool EventHandler::tryStartDrag(const MouseEventWithHitTestResults& event)
3347 {
3348 freeClipboard(); // would only happen if we missed a dragEnd. Do it anyway, just
3349 // to make sure it gets numbified
3350 dragState().m_dragClipboard = createDraggingClipboard();
3351
3352 // Check to see if this a DOM based drag, if it is get the DOM specified drag
3353 // image and offset
3354 if (dragState().m_dragType == DragSourceActionDHTML) {
3355 if (RenderObject* renderer = dragState().m_dragSrc->renderer()) {
3356 // FIXME: This doesn't work correctly with transforms.
3357 FloatPoint absPos = renderer->localToAbsolute();
3358 IntSize delta = m_mouseDownPos - roundedIntPoint(absPos);
3359 dragState().m_dragClipboard->setDragImageElement(dragState().m_dragSrc.get(), IntPoint(delta));
3360 } else {
3361 // The renderer has disappeared, this can happen if the onStartDrag handler has hidden
3362 // the element in some way. In this case we just kill the drag.
3363 return false;
3364 }
3365 }
3366
3367 DragController& dragController = m_frame->page()->dragController();
3368 if (!dragController.populateDragClipboard(m_frame, dragState(), m_mouseDownPos))
3369 return false;
3370 m_mouseDownMayStartDrag = dispatchDragSrcEvent(EventTypeNames::dragstart, m_mouseDown)
3371 && !m_frame->selection().isInPasswordField();
3372
3373 // Invalidate clipboard here against anymore pasteboard writing for security. The drag
3374 // image can still be changed as we drag, but not the pasteboard data.
3375 dragState().m_dragClipboard->setAccessPolicy(ClipboardImageWritable);
3376
3377 if (m_mouseDownMayStartDrag) {
3378 // Dispatching the event could cause Page to go away. Make sure it's still valid before trying to use DragController.
3379 m_didStartDrag = m_frame->page() && dragController.startDrag(m_frame, dragState(), event.event(), m_mouseDownPos);
3380 // FIXME: This seems pretty useless now. The gesture code uses this as a signal for
3381 // whether or not the drag started, but perhaps it can simply use the return value from
3382 // handleDrag(), even though it doesn't mean exactly the same thing.
3383 if (m_didStartDrag)
3384 return true;
3385 // Drag was canned at the last minute - we owe m_dragSrc a DRAGEND event
3386 dispatchDragSrcEvent(EventTypeNames::dragend, event.event());
3387 }
3388
3389 return false;
3390 }
3391
handleTextInputEvent(const String & text,Event * underlyingEvent,TextEventInputType inputType)3392 bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEvent, TextEventInputType inputType)
3393 {
3394 // Platforms should differentiate real commands like selectAll from text input in disguise (like insertNewline),
3395 // and avoid dispatching text input events from keydown default handlers.
3396 ASSERT(!underlyingEvent || !underlyingEvent->isKeyboardEvent() || toKeyboardEvent(underlyingEvent)->type() == EventTypeNames::keypress);
3397
3398 if (!m_frame)
3399 return false;
3400
3401 EventTarget* target;
3402 if (underlyingEvent)
3403 target = underlyingEvent->target();
3404 else
3405 target = eventTargetNodeForDocument(m_frame->document());
3406 if (!target)
3407 return false;
3408
3409 if (FrameView* view = m_frame->view())
3410 view->resetDeferredRepaintDelay();
3411
3412 RefPtr<TextEvent> event = TextEvent::create(m_frame->domWindow(), text, inputType);
3413 event->setUnderlyingEvent(underlyingEvent);
3414
3415 target->dispatchEvent(event, IGNORE_EXCEPTION);
3416 return event->defaultHandled();
3417 }
3418
defaultTextInputEventHandler(TextEvent * event)3419 void EventHandler::defaultTextInputEventHandler(TextEvent* event)
3420 {
3421 if (m_frame->editor().handleTextEvent(event))
3422 event->setDefaultHandled();
3423 }
3424
defaultSpaceEventHandler(KeyboardEvent * event)3425 void EventHandler::defaultSpaceEventHandler(KeyboardEvent* event)
3426 {
3427 ASSERT(event->type() == EventTypeNames::keypress);
3428
3429 if (event->ctrlKey() || event->metaKey() || event->altKey() || event->altGraphKey())
3430 return;
3431
3432 ScrollDirection direction = event->shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward;
3433 if (scrollOverflow(direction, ScrollByPage)) {
3434 event->setDefaultHandled();
3435 return;
3436 }
3437
3438 FrameView* view = m_frame->view();
3439 if (!view)
3440 return;
3441
3442 if (view->scroll(direction, ScrollByPage))
3443 event->setDefaultHandled();
3444 }
3445
defaultBackspaceEventHandler(KeyboardEvent * event)3446 void EventHandler::defaultBackspaceEventHandler(KeyboardEvent* event)
3447 {
3448 ASSERT(event->type() == EventTypeNames::keydown);
3449
3450 if (event->ctrlKey() || event->metaKey() || event->altKey() || event->altGraphKey())
3451 return;
3452
3453 if (!m_frame->editor().behavior().shouldNavigateBackOnBackspace())
3454 return;
3455
3456 Page* page = m_frame->page();
3457 if (!page)
3458 return;
3459 bool handledEvent = page->mainFrame()->loader().client()->navigateBackForward(event->shiftKey() ? 1 : -1);
3460 if (handledEvent)
3461 event->setDefaultHandled();
3462 }
3463
defaultArrowEventHandler(FocusDirection focusDirection,KeyboardEvent * event)3464 void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, KeyboardEvent* event)
3465 {
3466 ASSERT(event->type() == EventTypeNames::keydown);
3467
3468 if (event->ctrlKey() || event->metaKey() || event->altGraphKey() || event->shiftKey())
3469 return;
3470
3471 Page* page = m_frame->page();
3472 if (!page)
3473 return;
3474
3475 if (!isSpatialNavigationEnabled(m_frame))
3476 return;
3477
3478 // Arrows and other possible directional navigation keys can be used in design
3479 // mode editing.
3480 if (m_frame->document()->inDesignMode())
3481 return;
3482
3483 if (page->focusController().advanceFocus(focusDirection))
3484 event->setDefaultHandled();
3485 }
3486
defaultTabEventHandler(KeyboardEvent * event)3487 void EventHandler::defaultTabEventHandler(KeyboardEvent* event)
3488 {
3489 ASSERT(event->type() == EventTypeNames::keydown);
3490
3491 // We should only advance focus on tabs if no special modifier keys are held down.
3492 if (event->ctrlKey() || event->metaKey() || event->altGraphKey())
3493 return;
3494
3495 Page* page = m_frame->page();
3496 if (!page)
3497 return;
3498 if (!page->tabKeyCyclesThroughElements())
3499 return;
3500
3501 FocusDirection focusDirection = event->shiftKey() ? FocusDirectionBackward : FocusDirectionForward;
3502
3503 // Tabs can be used in design mode editing.
3504 if (m_frame->document()->inDesignMode())
3505 return;
3506
3507 if (page->focusController().advanceFocus(focusDirection))
3508 event->setDefaultHandled();
3509 }
3510
defaultEscapeEventHandler(KeyboardEvent * event)3511 void EventHandler::defaultEscapeEventHandler(KeyboardEvent* event)
3512 {
3513 if (HTMLDialogElement* dialog = m_frame->document()->activeModalDialog())
3514 dialog->dispatchEvent(Event::createCancelable(EventTypeNames::cancel));
3515 }
3516
capsLockStateMayHaveChanged()3517 void EventHandler::capsLockStateMayHaveChanged()
3518 {
3519 if (Element* element = m_frame->document()->focusedElement()) {
3520 if (RenderObject* r = element->renderer()) {
3521 if (r->isTextField())
3522 toRenderTextControlSingleLine(r)->capsLockStateMayHaveChanged();
3523 }
3524 }
3525 }
3526
setFrameWasScrolledByUser()3527 void EventHandler::setFrameWasScrolledByUser()
3528 {
3529 if (FrameView* view = m_frame->view())
3530 view->setWasScrolledByUser(true);
3531 }
3532
passMousePressEventToScrollbar(MouseEventWithHitTestResults & mev,Scrollbar * scrollbar)3533 bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mev, Scrollbar* scrollbar)
3534 {
3535 if (!scrollbar || !scrollbar->enabled())
3536 return false;
3537 setFrameWasScrolledByUser();
3538 scrollbar->mouseDown(mev.event());
3539 return true;
3540 }
3541
3542 // If scrollbar (under mouse) is different from last, send a mouse exited. Set
3543 // last to scrollbar if setLast is true; else set last to 0.
updateLastScrollbarUnderMouse(Scrollbar * scrollbar,bool setLast)3544 void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, bool setLast)
3545 {
3546 if (m_lastScrollbarUnderMouse != scrollbar) {
3547 // Send mouse exited to the old scrollbar.
3548 if (m_lastScrollbarUnderMouse)
3549 m_lastScrollbarUnderMouse->mouseExited();
3550
3551 // Send mouse entered if we're setting a new scrollbar.
3552 if (scrollbar && setLast)
3553 scrollbar->mouseEntered();
3554
3555 m_lastScrollbarUnderMouse = setLast ? scrollbar : 0;
3556 }
3557 }
3558
eventNameForTouchPointState(PlatformTouchPoint::State state)3559 static const AtomicString& eventNameForTouchPointState(PlatformTouchPoint::State state)
3560 {
3561 switch (state) {
3562 case PlatformTouchPoint::TouchReleased:
3563 return EventTypeNames::touchend;
3564 case PlatformTouchPoint::TouchCancelled:
3565 return EventTypeNames::touchcancel;
3566 case PlatformTouchPoint::TouchPressed:
3567 return EventTypeNames::touchstart;
3568 case PlatformTouchPoint::TouchMoved:
3569 return EventTypeNames::touchmove;
3570 case PlatformTouchPoint::TouchStationary:
3571 // TouchStationary state is not converted to touch events, so fall through to assert.
3572 default:
3573 ASSERT_NOT_REACHED();
3574 return emptyAtom;
3575 }
3576 }
3577
hitTestResultInFrame(Frame * frame,const LayoutPoint & point,HitTestRequest::HitTestRequestType hitType)3578 HitTestResult EventHandler::hitTestResultInFrame(Frame* frame, const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType)
3579 {
3580 HitTestResult result(point);
3581
3582 if (!frame || !frame->contentRenderer())
3583 return result;
3584 if (frame->view()) {
3585 IntRect rect = frame->view()->visibleContentRect();
3586 if (!rect.contains(roundedIntPoint(point)))
3587 return result;
3588 }
3589 frame->contentRenderer()->hitTest(HitTestRequest(hitType), result);
3590 return result;
3591 }
3592
handleTouchEvent(const PlatformTouchEvent & event)3593 bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
3594 {
3595 // First build up the lists to use for the 'touches', 'targetTouches' and 'changedTouches' attributes
3596 // in the JS event. See http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/
3597 // for an overview of how these lists fit together.
3598
3599 // Holds the complete set of touches on the screen and will be used as the 'touches' list in the JS event.
3600 RefPtr<TouchList> touches = TouchList::create();
3601
3602 // A different view on the 'touches' list above, filtered and grouped by event target. Used for the
3603 // 'targetTouches' list in the JS event.
3604 typedef HashMap<EventTarget*, RefPtr<TouchList> > TargetTouchesMap;
3605 TargetTouchesMap touchesByTarget;
3606
3607 // Array of touches per state, used to assemble the 'changedTouches' list in the JS event.
3608 typedef HashSet<RefPtr<EventTarget> > EventTargetSet;
3609 struct {
3610 // The touches corresponding to the particular change state this struct instance represents.
3611 RefPtr<TouchList> m_touches;
3612 // Set of targets involved in m_touches.
3613 EventTargetSet m_targets;
3614 } changedTouches[PlatformTouchPoint::TouchStateEnd];
3615
3616 const Vector<PlatformTouchPoint>& points = event.touchPoints();
3617
3618 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
3619
3620 unsigned i;
3621 bool freshTouchEvents = true;
3622 bool allTouchReleased = true;
3623 for (i = 0; i < points.size(); ++i) {
3624 const PlatformTouchPoint& point = points[i];
3625 if (point.state() != PlatformTouchPoint::TouchPressed)
3626 freshTouchEvents = false;
3627 if (point.state() != PlatformTouchPoint::TouchReleased && point.state() != PlatformTouchPoint::TouchCancelled)
3628 allTouchReleased = false;
3629 }
3630
3631 for (i = 0; i < points.size(); ++i) {
3632 const PlatformTouchPoint& point = points[i];
3633 PlatformTouchPoint::State pointState = point.state();
3634 LayoutPoint pagePoint = documentPointForWindowPoint(m_frame, point.pos());
3635
3636 // Gesture events trigger the active state, not touch events,
3637 // so touch event hit tests can always be read only.
3638 HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent | HitTestRequest::ReadOnly;
3639 // The HitTestRequest types used for mouse events map quite adequately
3640 // to touch events. Note that in addition to meaning that the hit test
3641 // should affect the active state of the current node if necessary,
3642 // HitTestRequest::Active signifies that the hit test is taking place
3643 // with the mouse (or finger in this case) being pressed.
3644 switch (pointState) {
3645 case PlatformTouchPoint::TouchPressed:
3646 hitType |= HitTestRequest::Active;
3647 break;
3648 case PlatformTouchPoint::TouchMoved:
3649 hitType |= HitTestRequest::Active | HitTestRequest::Move;
3650 break;
3651 case PlatformTouchPoint::TouchReleased:
3652 case PlatformTouchPoint::TouchCancelled:
3653 hitType |= HitTestRequest::Release;
3654 break;
3655 case PlatformTouchPoint::TouchStationary:
3656 hitType |= HitTestRequest::Active;
3657 break;
3658 default:
3659 ASSERT_NOT_REACHED();
3660 break;
3661 }
3662
3663 // Increment the platform touch id by 1 to avoid storing a key of 0 in the hashmap.
3664 unsigned touchPointTargetKey = point.id() + 1;
3665 RefPtr<EventTarget> touchTarget;
3666 if (pointState == PlatformTouchPoint::TouchPressed) {
3667 HitTestResult result;
3668 if (freshTouchEvents) {
3669 result = hitTestResultAtPoint(pagePoint, hitType);
3670 m_originatingTouchPointTargetKey = touchPointTargetKey;
3671 } else if (m_originatingTouchPointDocument.get() && m_originatingTouchPointDocument->frame()) {
3672 LayoutPoint pagePointInOriginatingDocument = documentPointForWindowPoint(m_originatingTouchPointDocument->frame(), point.pos());
3673 result = hitTestResultInFrame(m_originatingTouchPointDocument->frame(), pagePointInOriginatingDocument, hitType);
3674 } else
3675 continue;
3676
3677 Node* node = result.innerNode();
3678 if (!node)
3679 continue;
3680
3681 // Touch events should not go to text nodes
3682 if (node->isTextNode())
3683 node = EventPath::parent(node);
3684
3685 Document& doc = node->document();
3686 // Record the originating touch document even if it does not have a touch listener.
3687 if (freshTouchEvents) {
3688 m_originatingTouchPointDocument = &doc;
3689 freshTouchEvents = false;
3690 }
3691 if (!doc.hasTouchEventHandlers())
3692 continue;
3693 m_originatingTouchPointTargets.set(touchPointTargetKey, node);
3694 touchTarget = node;
3695
3696 // FIXME(rbyers): Should really be doing a second hit test that ignores inline elements - crbug.com/319479.
3697 TouchAction effectiveTouchAction = computeEffectiveTouchAction(*node);
3698 if (effectiveTouchAction != TouchActionAuto)
3699 m_frame->page()->chrome().client().setTouchAction(effectiveTouchAction);
3700
3701 } else if (pointState == PlatformTouchPoint::TouchReleased || pointState == PlatformTouchPoint::TouchCancelled) {
3702 // The target should be the original target for this touch, so get it from the hashmap. As it's a release or cancel
3703 // we also remove it from the map.
3704 touchTarget = m_originatingTouchPointTargets.take(touchPointTargetKey);
3705 } else
3706 // No hittest is performed on move or stationary, since the target is not allowed to change anyway.
3707 touchTarget = m_originatingTouchPointTargets.get(touchPointTargetKey);
3708
3709 if (!touchTarget.get())
3710 continue;
3711 Document& doc = touchTarget->toNode()->document();
3712 if (!doc.hasTouchEventHandlers())
3713 continue;
3714 Frame* targetFrame = doc.frame();
3715 if (!targetFrame)
3716 continue;
3717
3718 if (m_frame != targetFrame) {
3719 // pagePoint should always be relative to the target elements containing frame.
3720 pagePoint = documentPointForWindowPoint(targetFrame, point.pos());
3721 }
3722
3723 float scaleFactor = targetFrame->pageZoomFactor();
3724
3725 int adjustedPageX = lroundf(pagePoint.x() / scaleFactor);
3726 int adjustedPageY = lroundf(pagePoint.y() / scaleFactor);
3727 int adjustedRadiusX = lroundf(point.radiusX() / scaleFactor);
3728 int adjustedRadiusY = lroundf(point.radiusY() / scaleFactor);
3729
3730 RefPtr<Touch> touch = Touch::create(targetFrame, touchTarget.get(), point.id(),
3731 point.screenPos().x(), point.screenPos().y(),
3732 adjustedPageX, adjustedPageY,
3733 adjustedRadiusX, adjustedRadiusY,
3734 point.rotationAngle(), point.force());
3735
3736 // Ensure this target's touch list exists, even if it ends up empty, so it can always be passed to TouchEvent::Create below.
3737 TargetTouchesMap::iterator targetTouchesIterator = touchesByTarget.find(touchTarget.get());
3738 if (targetTouchesIterator == touchesByTarget.end())
3739 targetTouchesIterator = touchesByTarget.set(touchTarget.get(), TouchList::create()).iterator;
3740
3741 // touches and targetTouches should only contain information about touches still on the screen, so if this point is
3742 // released or cancelled it will only appear in the changedTouches list.
3743 if (pointState != PlatformTouchPoint::TouchReleased && pointState != PlatformTouchPoint::TouchCancelled) {
3744 touches->append(touch);
3745 targetTouchesIterator->value->append(touch);
3746 }
3747
3748 // Now build up the correct list for changedTouches.
3749 // Note that any touches that are in the TouchStationary state (e.g. if
3750 // the user had several points touched but did not move them all) should
3751 // never be in the changedTouches list so we do not handle them explicitly here.
3752 // See https://bugs.webkit.org/show_bug.cgi?id=37609 for further discussion
3753 // about the TouchStationary state.
3754 if (pointState != PlatformTouchPoint::TouchStationary) {
3755 ASSERT(pointState < PlatformTouchPoint::TouchStateEnd);
3756 if (!changedTouches[pointState].m_touches)
3757 changedTouches[pointState].m_touches = TouchList::create();
3758 changedTouches[pointState].m_touches->append(touch);
3759 changedTouches[pointState].m_targets.add(touchTarget);
3760 }
3761 }
3762 m_touchPressed = touches->length() > 0;
3763 if (allTouchReleased)
3764 m_originatingTouchPointDocument.clear();
3765
3766 // Now iterate the changedTouches list and m_targets within it, sending events to the targets as required.
3767 bool swallowedEvent = false;
3768 RefPtr<TouchList> emptyList = TouchList::create();
3769 for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; ++state) {
3770 if (!changedTouches[state].m_touches)
3771 continue;
3772
3773 // When sending a touch cancel event, use empty touches and targetTouches lists.
3774 bool isTouchCancelEvent = (state == PlatformTouchPoint::TouchCancelled);
3775 RefPtr<TouchList>& effectiveTouches(isTouchCancelEvent ? emptyList : touches);
3776 const AtomicString& stateName(eventNameForTouchPointState(static_cast<PlatformTouchPoint::State>(state)));
3777 const EventTargetSet& targetsForState = changedTouches[state].m_targets;
3778
3779 for (EventTargetSet::const_iterator it = targetsForState.begin(); it != targetsForState.end(); ++it) {
3780 EventTarget* touchEventTarget = it->get();
3781 RefPtr<TouchList> targetTouches(isTouchCancelEvent ? emptyList : touchesByTarget.get(touchEventTarget));
3782 ASSERT(targetTouches);
3783
3784 RefPtr<TouchEvent> touchEvent =
3785 TouchEvent::create(effectiveTouches.get(), targetTouches.get(), changedTouches[state].m_touches.get(),
3786 stateName, touchEventTarget->toNode()->document().domWindow(),
3787 0, 0, 0, 0, event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey());
3788 touchEventTarget->toNode()->dispatchTouchEvent(touchEvent.get());
3789 swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled();
3790 }
3791 }
3792
3793 return swallowedEvent;
3794 }
3795
dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent & event)3796 bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent& event)
3797 {
3798 if (!m_frame || !m_frame->settings() || !m_frame->settings()->touchEventEmulationEnabled())
3799 return false;
3800
3801 PlatformEvent::Type eventType = event.type();
3802 if (eventType != PlatformEvent::MouseMoved && eventType != PlatformEvent::MousePressed && eventType != PlatformEvent::MouseReleased)
3803 return false;
3804
3805 HitTestRequest request(HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
3806 MouseEventWithHitTestResults mev = prepareMouseEvent(request, event);
3807 if (mev.scrollbar() || subframeForHitTestResult(mev))
3808 return false;
3809
3810 // The order is important. This check should follow the subframe test: http://webkit.org/b/111292.
3811 if (eventType == PlatformEvent::MouseMoved && event.button() == NoButton)
3812 return true;
3813
3814 SyntheticSingleTouchEvent touchEvent(event);
3815 if (handleTouchEvent(touchEvent))
3816 return true;
3817
3818 return handleMouseEventAsEmulatedGesture(event);
3819 }
3820
handleMouseEventAsEmulatedGesture(const PlatformMouseEvent & event)3821 bool EventHandler::handleMouseEventAsEmulatedGesture(const PlatformMouseEvent& event)
3822 {
3823 PlatformEvent::Type eventType = event.type();
3824 if (event.button() != LeftButton || !m_frame->isMainFrame())
3825 return false;
3826
3827 // Simulate pinch / scroll gesture.
3828 const IntPoint& position = event.position();
3829 bool swallowEvent = false;
3830
3831 FrameView* view = m_frame->view();
3832 if (event.shiftKey()) {
3833 // Shift pressed - consider it gesture.
3834 swallowEvent = true;
3835 Page* page = m_frame->page();
3836 float pageScaleFactor = page->pageScaleFactor();
3837 switch (eventType) {
3838 case PlatformEvent::MousePressed:
3839 m_lastSyntheticPinchAnchorCss = adoptPtr(new IntPoint(view->scrollPosition() + position));
3840 m_lastSyntheticPinchAnchorDip = adoptPtr(new IntPoint(position));
3841 m_lastSyntheticPinchAnchorDip->scale(pageScaleFactor, pageScaleFactor);
3842 m_syntheticPageScaleFactor = pageScaleFactor;
3843 break;
3844 case PlatformEvent::MouseMoved:
3845 {
3846 if (!m_lastSyntheticPinchAnchorCss)
3847 break;
3848
3849 float dy = m_lastSyntheticPinchAnchorDip->y() - position.y() * pageScaleFactor;
3850 float magnifyDelta = exp(dy * 0.002f);
3851 float newPageScaleFactor = m_syntheticPageScaleFactor * magnifyDelta;
3852
3853 IntPoint anchorCss(*m_lastSyntheticPinchAnchorDip.get());
3854 anchorCss.scale(1.f / newPageScaleFactor, 1.f / newPageScaleFactor);
3855 page->inspectorController().requestPageScaleFactor(newPageScaleFactor, *m_lastSyntheticPinchAnchorCss.get() - toIntSize(anchorCss));
3856 break;
3857 }
3858 case PlatformEvent::MouseReleased:
3859 m_lastSyntheticPinchAnchorCss.clear();
3860 m_lastSyntheticPinchAnchorDip.clear();
3861 default:
3862 break;
3863 }
3864 } else {
3865 switch (eventType) {
3866 case PlatformEvent::MouseMoved:
3867 {
3868 // Always consume move events.
3869 swallowEvent = true;
3870 int dx = m_lastSyntheticPanLocation ? position.x() - m_lastSyntheticPanLocation->x() : 0;
3871 int dy = m_lastSyntheticPanLocation ? position.y() - m_lastSyntheticPanLocation->y() : 0;
3872 if (dx || dy)
3873 view->scrollBy(IntSize(-dx, -dy));
3874 // Mouse dragged - consider it gesture.
3875 m_lastSyntheticPanLocation = adoptPtr(new IntPoint(position));
3876 break;
3877 }
3878 case PlatformEvent::MouseReleased:
3879 // There was a drag -> gesture.
3880 swallowEvent = !!m_lastSyntheticPanLocation;
3881 m_lastSyntheticPanLocation.clear();
3882 default:
3883 break;
3884 }
3885 }
3886
3887 return swallowEvent;
3888 }
3889
handleWheelEventAsEmulatedGesture(const PlatformWheelEvent & event)3890 bool EventHandler::handleWheelEventAsEmulatedGesture(const PlatformWheelEvent& event)
3891 {
3892 if (!m_frame || !m_frame->settings() || !m_frame->settings()->touchEventEmulationEnabled())
3893 return false;
3894
3895 // Only convert vertical wheel w/ shift into pinch for touch-enabled device convenience.
3896 if (!event.shiftKey() || !event.deltaY())
3897 return false;
3898
3899 Page* page = m_frame->page();
3900 FrameView* view = m_frame->view();
3901 float pageScaleFactor = page->pageScaleFactor();
3902 IntPoint anchorBeforeCss(view->scrollPosition() + event.position());
3903 IntPoint anchorBeforeDip(event.position());
3904 anchorBeforeDip.scale(pageScaleFactor, pageScaleFactor);
3905
3906 float magnifyDelta = exp(event.deltaY() * 0.002f);
3907 float newPageScaleFactor = pageScaleFactor * magnifyDelta;
3908
3909 IntPoint anchorAfterCss(anchorBeforeDip);
3910 anchorAfterCss.scale(1.f / newPageScaleFactor, 1.f / newPageScaleFactor);
3911 page->inspectorController().requestPageScaleFactor(newPageScaleFactor, anchorBeforeCss - toIntSize(anchorAfterCss));
3912 return true;
3913 }
3914
computeEffectiveTouchAction(const Node & node)3915 TouchAction EventHandler::computeEffectiveTouchAction(const Node& node)
3916 {
3917 // Optimization to minimize risk of this new feature (behavior should be identical
3918 // since there's no way to get non-default touch-action values).
3919 if (!RuntimeEnabledFeatures::cssTouchActionEnabled())
3920 return TouchActionAuto;
3921
3922 // Start by permitting all actions, then walk the block level elements from
3923 // the target node up to the nearest scrollable ancestor and exclude any
3924 // prohibited actions. For now this is trivial, but when we add more types
3925 // of actions it'll get a little more complex.
3926 for (const Node* curNode = &node; curNode; curNode = NodeRenderingTraversal::parent(curNode)) {
3927 // The spec says only block and SVG elements get touch-action.
3928 // FIXME(rbyers): Add correct support for SVG, crbug.com/247396.
3929 if (RenderObject* renderer = curNode->renderer()) {
3930 if (renderer->isRenderBlockFlow()) {
3931 if (renderer->style()->touchAction() == TouchActionNone)
3932 return TouchActionNone;
3933 }
3934
3935 // If we've reached an ancestor that supports a touch action, search no further.
3936 if (renderer->isBox() && toRenderBox(renderer)->scrollsOverflow())
3937 break;
3938 }
3939 }
3940 return TouchActionAuto;
3941 }
3942
setLastKnownMousePosition(const PlatformMouseEvent & event)3943 void EventHandler::setLastKnownMousePosition(const PlatformMouseEvent& event)
3944 {
3945 m_mousePositionIsUnknown = false;
3946 m_lastKnownMousePosition = event.position();
3947 m_lastKnownMouseGlobalPosition = event.globalPosition();
3948 }
3949
passMousePressEventToSubframe(MouseEventWithHitTestResults & mev,Frame * subframe)3950 bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
3951 {
3952 // If we're clicking into a frame that is selected, the frame will appear
3953 // greyed out even though we're clicking on the selection. This looks
3954 // really strange (having the whole frame be greyed out), so we deselect the
3955 // selection.
3956 IntPoint p = m_frame->view()->windowToContents(mev.event().position());
3957 if (m_frame->selection().contains(p)) {
3958 VisiblePosition visiblePos(
3959 mev.targetNode()->renderer()->positionForPoint(mev.localPoint()));
3960 VisibleSelection newSelection(visiblePos);
3961 m_frame->selection().setSelection(newSelection);
3962 }
3963
3964 subframe->eventHandler().handleMousePressEvent(mev.event());
3965 return true;
3966 }
3967
passMouseMoveEventToSubframe(MouseEventWithHitTestResults & mev,Frame * subframe,HitTestResult * hoveredNode)3968 bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode)
3969 {
3970 if (m_mouseDownMayStartDrag && !m_mouseDownWasInSubframe)
3971 return false;
3972 subframe->eventHandler().handleMouseMoveOrLeaveEvent(mev.event(), hoveredNode);
3973 return true;
3974 }
3975
passMouseReleaseEventToSubframe(MouseEventWithHitTestResults & mev,Frame * subframe)3976 bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
3977 {
3978 subframe->eventHandler().handleMouseReleaseEvent(mev.event());
3979 return true;
3980 }
3981
passWheelEventToWidget(const PlatformWheelEvent & wheelEvent,Widget * widget)3982 bool EventHandler::passWheelEventToWidget(const PlatformWheelEvent& wheelEvent, Widget* widget)
3983 {
3984 // We can sometimes get a null widget! EventHandlerMac handles a null
3985 // widget by returning false, so we do the same.
3986 if (!widget)
3987 return false;
3988
3989 // If not a FrameView, then probably a plugin widget. Those will receive
3990 // the event via an EventTargetNode dispatch when this returns false.
3991 if (!widget->isFrameView())
3992 return false;
3993
3994 return toFrameView(widget)->frame().eventHandler().handleWheelEvent(wheelEvent);
3995 }
3996
passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults & event)3997 bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event)
3998 {
3999 // Figure out which view to send the event to.
4000 if (!event.targetNode() || !event.targetNode()->renderer() || !event.targetNode()->renderer()->isWidget())
4001 return false;
4002 return false;
4003 }
4004
createDraggingClipboard() const4005 PassRefPtr<Clipboard> EventHandler::createDraggingClipboard() const
4006 {
4007 return Clipboard::create(Clipboard::DragAndDrop, ClipboardWritable, ChromiumDataObject::create());
4008 }
4009
focusDocumentView()4010 void EventHandler::focusDocumentView()
4011 {
4012 Page* page = m_frame->page();
4013 if (!page)
4014 return;
4015 page->focusController().setFocusedFrame(m_frame);
4016 }
4017
accessKeyModifiers()4018 unsigned EventHandler::accessKeyModifiers()
4019 {
4020 #if OS(MACOSX)
4021 return PlatformEvent::CtrlKey | PlatformEvent::AltKey;
4022 #else
4023 return PlatformEvent::AltKey;
4024 #endif
4025 }
4026
4027 } // namespace WebCore
4028