Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(339)

Side by Side Diff: Source/core/page/EventHandler.cpp

Issue 1153723010: Remove page/EventHandler.* and fix core.gypi (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/core/page/EventHandler.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserv ed.
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 "bindings/core/v8/ExceptionStatePlaceholder.h"
32 #include "core/HTMLNames.h"
33 #include "core/InputTypeNames.h"
34 #include "core/clipboard/DataObject.h"
35 #include "core/clipboard/DataTransfer.h"
36 #include "core/dom/Document.h"
37 #include "core/dom/DocumentMarkerController.h"
38 #include "core/dom/TouchList.h"
39 #include "core/dom/shadow/ComposedTreeTraversal.h"
40 #include "core/dom/shadow/ShadowRoot.h"
41 #include "core/editing/Editor.h"
42 #include "core/editing/FrameSelection.h"
43 #include "core/editing/htmlediting.h"
44 #include "core/editing/iterators/TextIterator.h"
45 #include "core/events/EventPath.h"
46 #include "core/events/KeyboardEvent.h"
47 #include "core/events/MouseEvent.h"
48 #include "core/events/TextEvent.h"
49 #include "core/events/TouchEvent.h"
50 #include "core/events/WheelEvent.h"
51 #include "core/fetch/ImageResource.h"
52 #include "core/frame/EventHandlerRegistry.h"
53 #include "core/frame/FrameHost.h"
54 #include "core/frame/FrameView.h"
55 #include "core/frame/LocalFrame.h"
56 #include "core/frame/PinchViewport.h"
57 #include "core/frame/Settings.h"
58 #include "core/html/HTMLDialogElement.h"
59 #include "core/html/HTMLFrameElementBase.h"
60 #include "core/html/HTMLFrameSetElement.h"
61 #include "core/html/HTMLInputElement.h"
62 #include "core/layout/HitTestRequest.h"
63 #include "core/layout/HitTestResult.h"
64 #include "core/layout/LayoutPart.h"
65 #include "core/layout/LayoutTextControlSingleLine.h"
66 #include "core/layout/LayoutView.h"
67 #include "core/loader/FrameLoader.h"
68 #include "core/loader/FrameLoaderClient.h"
69 #include "core/page/AutoscrollController.h"
70 #include "core/page/ChromeClient.h"
71 #include "core/page/DragController.h"
72 #include "core/page/DragState.h"
73 #include "core/page/FocusController.h"
74 #include "core/page/FrameTree.h"
75 #include "core/page/Page.h"
76 #include "core/page/SpatialNavigation.h"
77 #include "core/page/TouchAdjustment.h"
78 #include "core/page/scrolling/ScrollState.h"
79 #include "core/paint/DeprecatedPaintLayer.h"
80 #include "core/style/ComputedStyle.h"
81 #include "core/svg/SVGDocumentExtensions.h"
82 #include "platform/PlatformGestureEvent.h"
83 #include "platform/PlatformKeyboardEvent.h"
84 #include "platform/PlatformTouchEvent.h"
85 #include "platform/PlatformWheelEvent.h"
86 #include "platform/RuntimeEnabledFeatures.h"
87 #include "platform/TraceEvent.h"
88 #include "platform/WindowsKeyboardCodes.h"
89 #include "platform/geometry/FloatPoint.h"
90 #include "platform/graphics/Image.h"
91 #include "platform/heap/Handle.h"
92 #include "platform/scroll/ScrollAnimator.h"
93 #include "platform/scroll/Scrollbar.h"
94 #include "wtf/Assertions.h"
95 #include "wtf/CurrentTime.h"
96 #include "wtf/StdLibExtras.h"
97 #include "wtf/TemporaryChange.h"
98
99 namespace blink {
100
101 using namespace HTMLNames;
102
103 // The link drag hysteresis is much larger than the others because there
104 // needs to be enough space to cancel the link press without starting a link dra g,
105 // and because dragging links is rare.
106 static const int LinkDragHysteresis = 40;
107 static const int ImageDragHysteresis = 5;
108 static const int TextDragHysteresis = 3;
109 static const int GeneralDragHysteresis = 3;
110
111 // The amount of time to wait before sending a fake mouse event, triggered
112 // during a scroll. The short interval is used if the content responds to the mo use events quickly enough,
113 // otherwise the long interval is used.
114 static const double fakeMouseMoveShortInterval = 0.1;
115 static const double fakeMouseMoveLongInterval = 0.250;
116
117 // The amount of time to wait for a cursor update on style and layout changes
118 // Set to 50Hz, no need to be faster than common screen refresh rate
119 static const double cursorUpdateInterval = 0.02;
120
121 static const int maximumCursorSize = 128;
122
123 // It's pretty unlikely that a scale of less than one would ever be used. But al l we really
124 // need to ensure here is that the scale isn't so small that integer overflow ca n occur when
125 // dividing cursor sizes (limited above) by the scale.
126 static const double minimumCursorScale = 0.001;
127
128 // The minimum amount of time an element stays active after a ShowPress
129 // This is roughly 9 frames, which should be long enough to be noticeable.
130 static const double minimumActiveInterval = 0.15;
131
132 #if OS(MACOSX)
133 static const double TextDragDelay = 0.15;
134 #else
135 static const double TextDragDelay = 0.0;
136 #endif
137
138 enum NoCursorChangeType { NoCursorChange };
139
140 enum class DragInitiator { Mouse, Touch };
141
142 class OptionalCursor {
143 public:
144 OptionalCursor(NoCursorChangeType) : m_isCursorChange(false) { }
145 OptionalCursor(const Cursor& cursor) : m_isCursorChange(true), m_cursor(curs or) { }
146
147 bool isCursorChange() const { return m_isCursorChange; }
148 const Cursor& cursor() const { ASSERT(m_isCursorChange); return m_cursor; }
149
150 private:
151 bool m_isCursorChange;
152 Cursor m_cursor;
153 };
154
155 class MaximumDurationTracker {
156 public:
157 explicit MaximumDurationTracker(double *maxDuration)
158 : m_maxDuration(maxDuration)
159 , m_start(monotonicallyIncreasingTime())
160 {
161 }
162
163 ~MaximumDurationTracker()
164 {
165 *m_maxDuration = max(*m_maxDuration, monotonicallyIncreasingTime() - m_s tart);
166 }
167
168 private:
169 double* m_maxDuration;
170 double m_start;
171 };
172
173 static inline ScrollGranularity wheelGranularityToScrollGranularity(WheelEvent* event)
174 {
175 unsigned deltaMode = event->deltaMode();
176 if (deltaMode == WheelEvent::DOM_DELTA_PAGE)
177 return ScrollByPage;
178 if (deltaMode == WheelEvent::DOM_DELTA_LINE)
179 return ScrollByLine;
180 ASSERT(deltaMode == WheelEvent::DOM_DELTA_PIXEL);
181 return event->hasPreciseScrollingDeltas() ? ScrollByPrecisePixel : ScrollByP ixel;
182 }
183
184 // Refetch the event target node if it is removed or currently is the shadow nod e inside an <input> element.
185 // If a mouse event handler changes the input element type to one that has a wid get associated,
186 // we'd like to EventHandler::handleMousePressEvent to pass the event to the wid get and thus the
187 // event target node can't still be the shadow node.
188 static inline bool shouldRefetchEventTarget(const MouseEventWithHitTestResults& mev)
189 {
190 Node* targetNode = mev.innerNode();
191 if (!targetNode || !targetNode->parentNode())
192 return true;
193 return targetNode->isShadowRoot() && isHTMLInputElement(*toShadowRoot(target Node)->host());
194 }
195
196 void recomputeScrollChain(const LocalFrame& frame, const Node& startNode,
197 WillBeHeapDeque<RefPtrWillBeMember<Element>>& scrollChain)
198 {
199 scrollChain.clear();
200
201 ASSERT(startNode.layoutObject());
202 LayoutBox* curBox = startNode.layoutObject()->enclosingBox();
203
204 // Scrolling propagates along the containing block chain.
205 while (curBox && !curBox->isLayoutView()) {
206 Node* curNode = curBox->node();
207 // FIXME: this should reject more elements, as part of crbug.com/410974.
208 if (curNode && curNode->isElementNode())
209 scrollChain.prepend(toElement(curNode));
210 curBox = curBox->containingBlock();
211 }
212
213 // FIXME: we should exclude the document in some cases, as part
214 // of crbug.com/410974.
215 scrollChain.prepend(frame.document()->documentElement());
216 }
217
218 EventHandler::EventHandler(LocalFrame* frame)
219 : m_frame(frame)
220 , m_mousePressed(false)
221 , m_capturesDragging(false)
222 , m_mouseDownMayStartSelect(false)
223 , m_mouseDownMayStartDrag(false)
224 , m_mouseDownWasSingleClickInSelection(false)
225 , m_selectionInitiationState(HaveNotStartedSelection)
226 , m_hoverTimer(this, &EventHandler::hoverTimerFired)
227 , m_cursorUpdateTimer(this, &EventHandler::cursorUpdateTimerFired)
228 , m_mouseDownMayStartAutoscroll(false)
229 , m_fakeMouseMoveEventTimer(this, &EventHandler::fakeMouseMoveEventTimerFire d)
230 , m_svgPan(false)
231 , m_resizeScrollableArea(nullptr)
232 , m_eventHandlerWillResetCapturingMouseEventsNode(0)
233 , m_clickCount(0)
234 , m_shouldOnlyFireDragOverEvent(false)
235 , m_mousePositionIsUnknown(true)
236 , m_mouseDownTimestamp(0)
237 , m_widgetIsLatched(false)
238 , m_touchPressed(false)
239 , m_scrollGestureHandlingNode(nullptr)
240 , m_lastGestureScrollOverWidget(false)
241 , m_maxMouseMovedDuration(0)
242 , m_longTapShouldInvokeContextMenu(false)
243 , m_activeIntervalTimer(this, &EventHandler::activeIntervalTimerFired)
244 , m_lastShowPressTimestamp(0)
245 , m_deltaConsumedForScrollSequence(false)
246 {
247 }
248
249 EventHandler::~EventHandler()
250 {
251 ASSERT(!m_fakeMouseMoveEventTimer.isActive());
252 }
253
254 DEFINE_TRACE(EventHandler)
255 {
256 #if ENABLE(OILPAN)
257 visitor->trace(m_mousePressNode);
258 visitor->trace(m_capturingMouseEventsNode);
259 visitor->trace(m_nodeUnderMouse);
260 visitor->trace(m_lastNodeUnderMouse);
261 visitor->trace(m_lastMouseMoveEventSubframe);
262 visitor->trace(m_lastScrollbarUnderMouse);
263 visitor->trace(m_clickNode);
264 visitor->trace(m_dragTarget);
265 visitor->trace(m_frameSetBeingResized);
266 visitor->trace(m_latchedWheelEventNode);
267 visitor->trace(m_previousWheelScrolledNode);
268 visitor->trace(m_scrollbarHandlingScrollGesture);
269 visitor->trace(m_targetForTouchID);
270 visitor->trace(m_touchSequenceDocument);
271 visitor->trace(m_scrollGestureHandlingNode);
272 visitor->trace(m_previousGestureScrolledNode);
273 visitor->trace(m_lastDeferredTapElement);
274 visitor->trace(m_currentScrollChain);
275 #endif
276 }
277
278 DragState& EventHandler::dragState()
279 {
280 DEFINE_STATIC_LOCAL(Persistent<DragState>, state, (new DragState()));
281 return *state;
282 }
283
284 void EventHandler::clear()
285 {
286 m_hoverTimer.stop();
287 m_cursorUpdateTimer.stop();
288 m_fakeMouseMoveEventTimer.stop();
289 m_activeIntervalTimer.stop();
290 m_resizeScrollableArea = nullptr;
291 m_nodeUnderMouse = nullptr;
292 m_lastNodeUnderMouse = nullptr;
293 m_lastMouseMoveEventSubframe = nullptr;
294 m_lastScrollbarUnderMouse = nullptr;
295 m_clickCount = 0;
296 m_clickNode = nullptr;
297 m_frameSetBeingResized = nullptr;
298 m_dragTarget = nullptr;
299 m_shouldOnlyFireDragOverEvent = false;
300 m_mousePositionIsUnknown = true;
301 m_lastKnownMousePosition = IntPoint();
302 m_lastKnownMouseGlobalPosition = IntPoint();
303 m_lastMouseDownUserGestureToken.clear();
304 m_mousePressNode = nullptr;
305 m_mousePressed = false;
306 m_capturesDragging = false;
307 m_capturingMouseEventsNode = nullptr;
308 m_latchedWheelEventNode = nullptr;
309 m_previousWheelScrolledNode = nullptr;
310 m_targetForTouchID.clear();
311 m_touchSequenceDocument.clear();
312 m_touchSequenceUserGestureToken.clear();
313 m_scrollGestureHandlingNode = nullptr;
314 m_lastGestureScrollOverWidget = false;
315 m_previousGestureScrolledNode = nullptr;
316 m_scrollbarHandlingScrollGesture = nullptr;
317 m_maxMouseMovedDuration = 0;
318 m_touchPressed = false;
319 m_mouseDownMayStartSelect = false;
320 m_mouseDownMayStartDrag = false;
321 m_lastShowPressTimestamp = 0;
322 m_lastDeferredTapElement = nullptr;
323 m_eventHandlerWillResetCapturingMouseEventsNode = false;
324 m_mouseDownWasSingleClickInSelection = false;
325 m_selectionInitiationState = HaveNotStartedSelection;
326 m_mouseDownMayStartAutoscroll = false;
327 m_svgPan = false;
328 m_mouseDownPos = IntPoint();
329 m_mouseDownTimestamp = 0;
330 m_longTapShouldInvokeContextMenu = false;
331 m_dragStartPos = LayoutPoint();
332 m_offsetFromResizeCorner = LayoutSize();
333 m_mouseDown = PlatformMouseEvent();
334 }
335
336 void EventHandler::nodeWillBeRemoved(Node& nodeToBeRemoved)
337 {
338 if (nodeToBeRemoved.containsIncludingShadowDOM(m_clickNode.get())) {
339 // We don't dispatch click events if the mousedown node is removed
340 // before a mouseup event. It is compatible with IE and Firefox.
341 m_clickNode = nullptr;
342 }
343 }
344
345 static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelecti on& newSelection)
346 {
347 if (selection.selection() != newSelection)
348 selection.setSelection(newSelection);
349 }
350
351 static inline bool dispatchSelectStart(Node* node)
352 {
353 if (!node || !node->layoutObject())
354 return true;
355
356 return node->dispatchEvent(Event::createCancelableBubble(EventTypeNames::sel ectstart));
357 }
358
359 static VisibleSelection expandSelectionToRespectUserSelectAll(Node* targetNode, const VisibleSelection& selection)
360 {
361 Node* rootUserSelectAll = Position::rootUserSelectAllForNode(targetNode);
362 if (!rootUserSelectAll)
363 return selection;
364
365 VisibleSelection newSelection(selection);
366 newSelection.setBase(positionBeforeNode(rootUserSelectAll).upstream(CanCross EditingBoundary));
367 newSelection.setExtent(positionAfterNode(rootUserSelectAll).downstream(CanCr ossEditingBoundary));
368
369 return newSelection;
370 }
371
372 bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targe tNode, const VisibleSelection& selection, TextGranularity granularity)
373 {
374 if (Position::nodeIsUserSelectNone(targetNode))
375 return false;
376
377 if (!dispatchSelectStart(targetNode))
378 return false;
379
380 if (selection.isRange()) {
381 m_selectionInitiationState = ExtendedSelection;
382 } else {
383 granularity = CharacterGranularity;
384 m_selectionInitiationState = PlacedCaret;
385 }
386
387 m_frame->selection().setNonDirectionalSelectionIfNeeded(selection, granulari ty);
388
389 return true;
390 }
391
392 void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& resul t, AppendTrailingWhitespace appendTrailingWhitespace)
393 {
394 Node* innerNode = result.innerNode();
395 VisibleSelection newSelection;
396
397 if (innerNode && innerNode->layoutObject()) {
398 VisiblePosition pos(innerNode->layoutObject()->positionForPoint(result.l ocalPoint()));
399 if (pos.isNotNull()) {
400 newSelection = VisibleSelection(pos);
401 newSelection.expandUsingGranularity(WordGranularity);
402 }
403
404 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSel ection.isRange())
405 newSelection.appendTrailingWhitespace();
406
407 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelec tionToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
408 }
409 }
410
411 void EventHandler::selectClosestMisspellingFromHitTestResult(const HitTestResult & result, AppendTrailingWhitespace appendTrailingWhitespace)
412 {
413 Node* innerNode = result.innerNode();
414 VisibleSelection newSelection;
415
416 if (innerNode && innerNode->layoutObject()) {
417 VisiblePosition pos(innerNode->layoutObject()->positionForPoint(result.l ocalPoint()));
418 Position start = pos.deepEquivalent();
419 Position end = pos.deepEquivalent();
420 if (pos.isNotNull()) {
421 DocumentMarkerVector markers = innerNode->document().markers().marke rsInRange(makeRange(pos, pos).get(), DocumentMarker::MisspellingMarkers());
422 if (markers.size() == 1) {
423 start.moveToOffset(markers[0]->startOffset());
424 end.moveToOffset(markers[0]->endOffset());
425 newSelection = VisibleSelection(start, end);
426 }
427 }
428
429 if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSel ection.isRange())
430 newSelection.appendTrailingWhitespace();
431
432 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelec tionToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
433 }
434 }
435
436 void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestRe sults& result)
437 {
438 if (m_mouseDownMayStartSelect) {
439 selectClosestWordFromHitTestResult(result.hitTestResult(),
440 (result.event().clickCount() == 2 && m_frame->editor().isSelectTrail ingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhi tespace);
441 }
442 }
443
444 void EventHandler::selectClosestMisspellingFromMouseEvent(const MouseEventWithHi tTestResults& result)
445 {
446 if (m_mouseDownMayStartSelect) {
447 selectClosestMisspellingFromHitTestResult(result.hitTestResult(),
448 (result.event().clickCount() == 2 && m_frame->editor().isSelectTrail ingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhi tespace);
449 }
450 }
451
452 void EventHandler::selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHit TestResults& result)
453 {
454 if (!result.hitTestResult().isLiveLink())
455 return selectClosestWordFromMouseEvent(result);
456
457 Node* innerNode = result.innerNode();
458
459 if (innerNode && innerNode->layoutObject() && m_mouseDownMayStartSelect) {
460 VisibleSelection newSelection;
461 Element* URLElement = result.hitTestResult().URLElement();
462 VisiblePosition pos(innerNode->layoutObject()->positionForPoint(result.l ocalPoint()));
463 if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescenda ntOf(URLElement))
464 newSelection = VisibleSelection::selectionFromContentsOfNode(URLElem ent);
465
466 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelec tionToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
467 }
468 }
469
470 bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestR esults& event)
471 {
472 TRACE_EVENT0("blink", "EventHandler::handleMousePressEventDoubleClick");
473
474 if (event.event().button() != LeftButton)
475 return false;
476
477 if (m_frame->selection().isRange()) {
478 // A double-click when range is already selected
479 // should not change the selection. So, do not call
480 // selectClosestWordFromMouseEvent, but do set
481 // m_beganSelectingText to prevent handleMouseReleaseEvent
482 // from setting caret selection.
483 m_selectionInitiationState = ExtendedSelection;
484 } else {
485 selectClosestWordFromMouseEvent(event);
486 }
487 return true;
488 }
489
490 bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestR esults& event)
491 {
492 TRACE_EVENT0("blink", "EventHandler::handleMousePressEventTripleClick");
493
494 if (event.event().button() != LeftButton)
495 return false;
496
497 Node* innerNode = event.innerNode();
498 if (!(innerNode && innerNode->layoutObject() && m_mouseDownMayStartSelect))
499 return false;
500
501 VisibleSelection newSelection;
502 VisiblePosition pos(innerNode->layoutObject()->positionForPoint(event.localP oint()));
503 if (pos.isNotNull()) {
504 newSelection = VisibleSelection(pos);
505 newSelection.expandUsingGranularity(ParagraphGranularity);
506 }
507
508 return updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSe lectionToRespectUserSelectAll(innerNode, newSelection), ParagraphGranularity);
509 }
510
511 static int textDistance(const Position& start, const Position& end)
512 {
513 RefPtrWillBeRawPtr<Range> range = Range::create(*start.document(), start, en d);
514 return TextIterator::rangeLength(range->startPosition(), range->endPosition( ), true);
515 }
516
517 bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR esults& event)
518 {
519 TRACE_EVENT0("blink", "EventHandler::handleMousePressEventSingleClick");
520
521 m_frame->document()->updateLayoutIgnorePendingStylesheets();
522 Node* innerNode = event.innerNode();
523 if (!(innerNode && innerNode->layoutObject() && m_mouseDownMayStartSelect))
524 return false;
525
526 // Extend the selection if the Shift key is down, unless the click is in a l ink.
527 bool extendSelection = event.event().shiftKey() && !event.isOverLink();
528
529 // Don't restart the selection when the mouse is pressed on an
530 // existing selection so we can allow for text dragging.
531 if (FrameView* view = m_frame->view()) {
532 LayoutPoint vPoint = view->rootFrameToContents(event.event().position()) ;
533 if (!extendSelection && m_frame->selection().contains(vPoint)) {
534 m_mouseDownWasSingleClickInSelection = true;
535 return false;
536 }
537 }
538
539 VisiblePosition visiblePos(innerNode->layoutObject()->positionForPoint(event .localPoint()));
540 if (visiblePos.isNull())
541 visiblePos = VisiblePosition(firstPositionInOrBeforeNode(innerNode), DOW NSTREAM);
542 Position pos = visiblePos.deepEquivalent();
543
544 VisibleSelection newSelection = m_frame->selection().selection();
545 TextGranularity granularity = CharacterGranularity;
546
547 if (extendSelection && newSelection.isCaretOrRange()) {
548 VisibleSelection selectionInUserSelectAll(expandSelectionToRespectUserSe lectAll(innerNode, VisibleSelection(VisiblePosition(pos))));
549 if (selectionInUserSelectAll.isRange()) {
550 if (comparePositions(selectionInUserSelectAll.start(), newSelection. start()) < 0)
551 pos = selectionInUserSelectAll.start();
552 else if (comparePositions(newSelection.end(), selectionInUserSelectA ll.end()) < 0)
553 pos = selectionInUserSelectAll.end();
554 }
555
556 if (!m_frame->editor().behavior().shouldConsiderSelectionAsDirectional() ) {
557 if (pos.isNotNull()) {
558 // See <rdar://problem/3668157> REGRESSION (Mail): shift-click d eselects when selection
559 // was created right-to-left
560 Position start = newSelection.start();
561 Position end = newSelection.end();
562 int distanceToStart = textDistance(start, pos);
563 int distanceToEnd = textDistance(pos, end);
564 if (distanceToStart <= distanceToEnd)
565 newSelection = VisibleSelection(end, pos);
566 else
567 newSelection = VisibleSelection(start, pos);
568 }
569 } else {
570 newSelection.setExtent(pos);
571 }
572
573 if (m_frame->selection().granularity() != CharacterGranularity) {
574 granularity = m_frame->selection().granularity();
575 newSelection.expandUsingGranularity(m_frame->selection().granularity ());
576 }
577 } else {
578 newSelection = expandSelectionToRespectUserSelectAll(innerNode, VisibleS election(visiblePos));
579 }
580
581 // Updating the selection is considered side-effect of the event and so it d oesn't impact the handled state.
582 updateSelectionForMouseDownDispatchingSelectStart(innerNode, newSelection, g ranularity);
583 return false;
584 }
585
586 static inline bool canMouseDownStartSelect(Node* node)
587 {
588 if (!node || !node->layoutObject())
589 return true;
590
591 if (!node->canStartSelection())
592 return false;
593
594 return true;
595 }
596
597 bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve nt)
598 {
599 TRACE_EVENT0("blink", "EventHandler::handleMousePressEvent");
600
601 // Reset drag state.
602 dragState().m_dragSrc = nullptr;
603
604 cancelFakeMouseMoveEvent();
605
606 m_frame->document()->updateLayoutIgnorePendingStylesheets();
607
608 if (FrameView* frameView = m_frame->view()) {
609 if (frameView->isPointInScrollbarCorner(event.event().position()))
610 return false;
611 }
612
613 bool singleClick = event.event().clickCount() <= 1;
614
615 // If we got the event back, that must mean it wasn't prevented,
616 // so it's allowed to start a drag or selection if it wasn't in a scrollbar.
617 m_mouseDownMayStartSelect = canMouseDownStartSelect(event.innerNode()) && !e vent.scrollbar();
618
619 m_mouseDownMayStartDrag = singleClick;
620
621 m_mouseDownWasSingleClickInSelection = false;
622
623 m_mouseDown = event.event();
624
625 if (m_frame->document()->isSVGDocument() && m_frame->document()->accessSVGEx tensions().zoomAndPanEnabled()) {
626 if (event.event().shiftKey() && singleClick) {
627 m_svgPan = true;
628 m_frame->document()->accessSVGExtensions().startPan(m_frame->view()- >rootFrameToContents(event.event().position()));
629 return true;
630 }
631 }
632
633 // We don't do this at the start of mouse down handling,
634 // because we don't want to do it until we know we didn't hit a widget.
635 if (singleClick)
636 focusDocumentView();
637
638 Node* innerNode = event.innerNode();
639
640 m_mousePressNode = innerNode;
641 m_dragStartPos = event.event().position();
642
643 bool swallowEvent = false;
644 m_mousePressed = true;
645 m_selectionInitiationState = HaveNotStartedSelection;
646
647 if (event.event().clickCount() == 2)
648 swallowEvent = handleMousePressEventDoubleClick(event);
649 else if (event.event().clickCount() >= 3)
650 swallowEvent = handleMousePressEventTripleClick(event);
651 else
652 swallowEvent = handleMousePressEventSingleClick(event);
653
654 m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect
655 || (m_mousePressNode && m_mousePressNode->layoutBox() && m_mousePressNod e->layoutBox()->canBeProgramaticallyScrolled());
656
657 return swallowEvent;
658 }
659
660 bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& e vent)
661 {
662 TRACE_EVENT0("blink", "EventHandler::handleMouseDraggedEvent");
663
664 // While resetting m_mousePressed here may seem out of place, it turns out
665 // to be needed to handle some bugs^Wfeatures in Blink mouse event handling:
666 // 1. Certain elements, such as <embed>, capture mouse events. They do not
667 // bubble back up. One way for a <embed> to start capturing mouse events
668 // is on a mouse press. The problem is the <embed> node only starts
669 // capturing mouse events *after* m_mousePressed for the containing frame
670 // has already been set to true. As a result, the frame's EventHandler
671 // never sees the mouse release event, which is supposed to reset
672 // m_mousePressed... so m_mousePressed ends up remaining true until the
673 // event handler finally gets another mouse released event. Oops.
674 // 2. Dragging doesn't start until after a mouse press event, but a drag
675 // that ends as a result of a mouse release does not send a mouse release
676 // event. As a result, m_mousePressed also ends up remaining true until
677 // the next mouse release event seen by the EventHandler.
678 if (event.event().button() != LeftButton)
679 m_mousePressed = false;
680
681 if (!m_mousePressed)
682 return false;
683
684 if (handleDrag(event, DragInitiator::Mouse))
685 return true;
686
687 Node* targetNode = event.innerNode();
688 if (!targetNode)
689 return false;
690
691 LayoutObject* layoutObject = targetNode->layoutObject();
692 if (!layoutObject) {
693 Node* parent = ComposedTreeTraversal::parent(*targetNode);
694 if (!parent)
695 return false;
696
697 layoutObject = parent->layoutObject();
698 if (!layoutObject || !layoutObject->isListBox())
699 return false;
700 }
701
702 m_mouseDownMayStartDrag = false;
703
704 if (m_mouseDownMayStartAutoscroll && !panScrollInProgress()) {
705 if (AutoscrollController* controller = autoscrollController()) {
706 controller->startAutoscrollForSelection(layoutObject);
707 m_mouseDownMayStartAutoscroll = false;
708 }
709 }
710
711 if (m_selectionInitiationState != ExtendedSelection) {
712 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active );
713 HitTestResult result(request, m_mouseDownPos);
714 m_frame->document()->layoutView()->hitTest(result);
715
716 updateSelectionForMouseDrag(result);
717 }
718 updateSelectionForMouseDrag(event.hitTestResult());
719 return true;
720 }
721
722 void EventHandler::updateSelectionForMouseDrag()
723 {
724 FrameView* view = m_frame->view();
725 if (!view)
726 return;
727 LayoutView* layoutObject = m_frame->contentLayoutObject();
728 if (!layoutObject)
729 return;
730
731 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | H itTestRequest::Move);
732 HitTestResult result(request, view->rootFrameToContents(m_lastKnownMousePosi tion));
733 layoutObject->hitTest(result);
734 updateSelectionForMouseDrag(result);
735 }
736
737 void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul t)
738 {
739 if (!m_mouseDownMayStartSelect)
740 return;
741
742 Node* target = hitTestResult.innerNode();
743 if (!target)
744 return;
745
746 VisiblePosition targetPosition = m_frame->selection().selection().visiblePos itionRespectingEditingBoundary(hitTestResult.localPoint(), target);
747 // Don't modify the selection if we're not on a node.
748 if (targetPosition.isNull())
749 return;
750
751 // Restart the selection if this is the first mouse move. This work is usual ly
752 // done in handleMousePressEvent, but not if the mouse press was on an exist ing selection.
753 VisibleSelection newSelection = m_frame->selection().selection();
754
755 // Special case to limit selection to the containing block for SVG text.
756 // FIXME: Isn't there a better non-SVG-specific way to do this?
757 if (Node* selectionBaseNode = newSelection.base().deprecatedNode()) {
758 if (LayoutObject* selectionBaseLayoutObject = selectionBaseNode->layoutO bject()) {
759 if (selectionBaseLayoutObject->isSVGText()) {
760 if (target->layoutObject()->containingBlock() != selectionBaseLa youtObject->containingBlock())
761 return;
762 }
763 }
764 }
765
766 if (m_selectionInitiationState == HaveNotStartedSelection && !dispatchSelect Start(target))
767 return;
768
769 if (m_selectionInitiationState != ExtendedSelection) {
770 // Always extend selection here because it's caused by a mouse drag
771 m_selectionInitiationState = ExtendedSelection;
772 newSelection = VisibleSelection(targetPosition);
773 }
774
775 if (RuntimeEnabledFeatures::userSelectAllEnabled()) {
776 Node* rootUserSelectAllForMousePressNode = Position::rootUserSelectAllFo rNode(m_mousePressNode.get());
777 if (rootUserSelectAllForMousePressNode && rootUserSelectAllForMousePress Node == Position::rootUserSelectAllForNode(target)) {
778 newSelection.setBase(positionBeforeNode(rootUserSelectAllForMousePre ssNode).upstream(CanCrossEditingBoundary));
779 newSelection.setExtent(positionAfterNode(rootUserSelectAllForMousePr essNode).downstream(CanCrossEditingBoundary));
780 } else {
781 // Reset base for user select all when base is inside user-select-al l area and extent < base.
782 if (rootUserSelectAllForMousePressNode && comparePositions(target->l ayoutObject()->positionForPoint(hitTestResult.localPoint()), m_mousePressNode->l ayoutObject()->positionForPoint(m_dragStartPos)) < 0)
783 newSelection.setBase(positionAfterNode(rootUserSelectAllForMouse PressNode).downstream(CanCrossEditingBoundary));
784
785 Node* rootUserSelectAllForTarget = Position::rootUserSelectAllForNod e(target);
786 if (rootUserSelectAllForTarget && m_mousePressNode->layoutObject() & & comparePositions(target->layoutObject()->positionForPoint(hitTestResult.localP oint()), m_mousePressNode->layoutObject()->positionForPoint(m_dragStartPos)) < 0 )
787 newSelection.setExtent(positionBeforeNode(rootUserSelectAllForTa rget).upstream(CanCrossEditingBoundary));
788 else if (rootUserSelectAllForTarget && m_mousePressNode->layoutObjec t())
789 newSelection.setExtent(positionAfterNode(rootUserSelectAllForTar get).downstream(CanCrossEditingBoundary));
790 else
791 newSelection.setExtent(targetPosition);
792 }
793 } else {
794 newSelection.setExtent(targetPosition);
795 }
796
797 if (m_frame->selection().granularity() != CharacterGranularity)
798 newSelection.expandUsingGranularity(m_frame->selection().granularity());
799
800 m_frame->selection().setNonDirectionalSelectionIfNeeded(newSelection, m_fram e->selection().granularity(),
801 FrameSelection::AdjustEndpointsAtBidiBoundary);
802 }
803
804 bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e vent)
805 {
806 AutoscrollController* controller = autoscrollController();
807 if (controller && controller->autoscrollInProgress())
808 stopAutoscroll();
809
810 // Used to prevent mouseMoveEvent from initiating a drag before
811 // the mouse is pressed again.
812 m_mousePressed = false;
813 m_capturesDragging = false;
814 m_mouseDownMayStartDrag = false;
815 m_mouseDownMayStartSelect = false;
816 m_mouseDownMayStartAutoscroll = false;
817
818 bool handled = false;
819
820 // Clear the selection if the mouse didn't move after the last mouse
821 // press and it's not a context menu click. We do this so when clicking
822 // on the selection, the selection goes away. However, if we are
823 // editing, place the caret.
824 if (m_mouseDownWasSingleClickInSelection && m_selectionInitiationState != Ex tendedSelection
825 && m_dragStartPos == event.event().position()
826 && m_frame->selection().isRange()
827 && event.event().button() != RightButton) {
828 VisibleSelection newSelection;
829 Node* node = event.innerNode();
830 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBr owsingEnabled();
831 if (node && node->layoutObject() && (caretBrowsing || node->hasEditableS tyle())) {
832 VisiblePosition pos = VisiblePosition(node->layoutObject()->position ForPoint(event.localPoint()));
833 newSelection = VisibleSelection(pos);
834 }
835
836 setSelectionIfNeeded(m_frame->selection(), newSelection);
837
838 handled = true;
839 }
840
841 m_frame->selection().notifyLayoutObjectOfSelectionChange(UserTriggered);
842
843 m_frame->selection().selectFrameElementInParentIfFullySelected();
844
845 if (event.event().button() == MiddleButton && !event.isOverLink()) {
846 // Ignore handled, since we want to paste to where the caret was placed anyway.
847 handled = handlePasteGlobalSelection(event.event()) || handled;
848 }
849
850 return handled;
851 }
852
853 #if OS(WIN)
854
855 void EventHandler::startPanScrolling(LayoutObject* layoutObject)
856 {
857 if (!layoutObject->isBox())
858 return;
859 AutoscrollController* controller = autoscrollController();
860 if (!controller)
861 return;
862 controller->startPanScrolling(toLayoutBox(layoutObject), lastKnownMousePosit ion());
863 invalidateClick();
864 }
865
866 #endif // OS(WIN)
867
868 AutoscrollController* EventHandler::autoscrollController() const
869 {
870 if (Page* page = m_frame->page())
871 return &page->autoscrollController();
872 return nullptr;
873 }
874
875 bool EventHandler::panScrollInProgress() const
876 {
877 return autoscrollController() && autoscrollController()->panScrollInProgress ();
878 }
879
880 HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTe stRequest::HitTestRequestType hitType, const LayoutSize& padding)
881 {
882 TRACE_EVENT0("blink", "EventHandler::hitTestResultAtPoint");
883
884 ASSERT((hitType & HitTestRequest::ListBased) || padding.isEmpty());
885
886 // We always send hitTestResultAtPoint to the main frame if we have one,
887 // otherwise we might hit areas that are obscured by higher frames.
888 if (m_frame->page()) {
889 LocalFrame* mainFrame = m_frame->localFrameRoot();
890 if (mainFrame && m_frame != mainFrame) {
891 FrameView* frameView = m_frame->view();
892 FrameView* mainView = mainFrame->view();
893 if (frameView && mainView) {
894 IntPoint mainFramePoint = mainView->rootFrameToContents(frameVie w->contentsToRootFrame(roundedIntPoint(point)));
895 return mainFrame->eventHandler().hitTestResultAtPoint(mainFrameP oint, hitType, padding);
896 }
897 }
898 }
899
900 // hitTestResultAtPoint is specifically used to hitTest into all frames, thu s it always allows child frame content.
901 HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent);
902 HitTestResult result(request, point, padding.height(), padding.width(), padd ing.height(), padding.width());
903
904 // LayoutView::hitTest causes a layout, and we don't want to hit that until the first
905 // layout because until then, there is nothing shown on the screen - the use r can't
906 // have intentionally clicked on something belonging to this page. Furthermo re,
907 // mousemove events before the first layout should not lead to a premature l ayout()
908 // happening, which could show a flash of white.
909 // See also the similar code in Document::prepareMouseEvent.
910 if (!m_frame->contentLayoutObject() || !m_frame->view() || !m_frame->view()- >didFirstLayout())
911 return result;
912
913 m_frame->contentLayoutObject()->hitTest(result);
914 if (!request.readOnly())
915 m_frame->document()->updateHoverActiveState(request, result.innerElement ());
916
917 return result;
918 }
919
920 void EventHandler::stopAutoscroll()
921 {
922 if (AutoscrollController* controller = autoscrollController())
923 controller->stopAutoscroll();
924 }
925
926 bool EventHandler::scroll(ScrollDirection direction, ScrollGranularity granulari ty, Node* startNode, Node** stopNode, float delta, IntPoint absolutePoint)
927 {
928 if (!delta)
929 return false;
930
931 Node* node = startNode;
932
933 if (!node)
934 node = m_frame->document()->focusedElement();
935
936 if (!node)
937 node = m_mousePressNode.get();
938
939 if (!node || !node->layoutObject())
940 return false;
941
942 LayoutBox* curBox = node->layoutObject()->enclosingBox();
943 while (curBox && !curBox->isLayoutView()) {
944 ScrollDirectionPhysical physicalDirection = toPhysicalDirection(
945 direction, curBox->isHorizontalWritingMode(), curBox->style()->isFli ppedBlocksWritingMode());
946
947 // If we're at the stopNode, we should try to scroll it but we shouldn't bubble past it
948 bool shouldStopBubbling = stopNode && *stopNode && curBox->node() == *st opNode;
949 bool didScroll = curBox->scroll(physicalDirection, granularity, delta);
950
951 if (didScroll && stopNode)
952 *stopNode = curBox->node();
953
954 if (didScroll || shouldStopBubbling) {
955 setFrameWasScrolledByUser();
956 return true;
957 }
958
959 curBox = curBox->containingBlock();
960 }
961
962 return false;
963 }
964
965 void EventHandler::customizedScroll(const Node& startNode, ScrollState& scrollSt ate)
966 {
967 if (scrollState.fullyConsumed())
968 return;
969
970 if (m_currentScrollChain.isEmpty())
971 recomputeScrollChain(*m_frame, startNode, m_currentScrollChain);
972 scrollState.setScrollChain(m_currentScrollChain);
973 scrollState.distributeToScrollChainDescendant();
974 }
975
976 bool EventHandler::bubblingScroll(ScrollDirection direction, ScrollGranularity g ranularity, Node* startingNode)
977 {
978 // The layout needs to be up to date to determine if we can scroll. We may b e
979 // here because of an onLoad event, in which case the final layout hasn't be en performed yet.
980 m_frame->document()->updateLayoutIgnorePendingStylesheets();
981 // FIXME: enable scroll customization in this case. See crbug.com/410974.
982 if (scroll(direction, granularity, startingNode))
983 return true;
984 LocalFrame* frame = m_frame;
985 FrameView* view = frame->view();
986 if (view) {
987 ScrollDirectionPhysical physicalDirection =
988 toPhysicalDirection(direction, view->isVerticalDocument(), view->isF lippedDocument());
989 if (view->scrollableArea()->userScroll(physicalDirection, granularity)) {
990 setFrameWasScrolledByUser();
991 return true;
992 }
993 }
994
995 Frame* parentFrame = frame->tree().parent();
996 if (!parentFrame || !parentFrame->isLocalFrame())
997 return false;
998 // FIXME: Broken for OOPI.
999 return toLocalFrame(parentFrame)->eventHandler().bubblingScroll(direction, g ranularity, m_frame->deprecatedLocalOwner());
1000 }
1001
1002 IntPoint EventHandler::lastKnownMousePosition() const
1003 {
1004 return m_lastKnownMousePosition;
1005 }
1006
1007 static LocalFrame* subframeForTargetNode(Node* node)
1008 {
1009 if (!node)
1010 return nullptr;
1011
1012 LayoutObject* layoutObject = node->layoutObject();
1013 if (!layoutObject || !layoutObject->isLayoutPart())
1014 return nullptr;
1015
1016 Widget* widget = toLayoutPart(layoutObject)->widget();
1017 if (!widget || !widget->isFrameView())
1018 return nullptr;
1019
1020 return &toFrameView(widget)->frame();
1021 }
1022
1023 static LocalFrame* subframeForHitTestResult(const MouseEventWithHitTestResults& hitTestResult)
1024 {
1025 if (!hitTestResult.isOverWidget())
1026 return nullptr;
1027 return subframeForTargetNode(hitTestResult.innerNode());
1028 }
1029
1030 static bool isSubmitImage(Node* node)
1031 {
1032 return isHTMLInputElement(node) && toHTMLInputElement(node)->type() == Input TypeNames::image;
1033 }
1034
1035 bool EventHandler::useHandCursor(Node* node, bool isOverLink)
1036 {
1037 if (!node)
1038 return false;
1039
1040 return ((isOverLink || isSubmitImage(node)) && !node->hasEditableStyle());
1041 }
1042
1043 void EventHandler::cursorUpdateTimerFired(Timer<EventHandler>*)
1044 {
1045 ASSERT(m_frame);
1046 ASSERT(m_frame->document());
1047
1048 updateCursor();
1049 }
1050
1051 void EventHandler::updateCursor()
1052 {
1053 TRACE_EVENT0("input", "EventHandler::updateCursor");
1054
1055 // We must do a cross-frame hit test because the frame that triggered the cu rsor
1056 // update could be occluded by a different frame.
1057 ASSERT(m_frame == m_frame->localFrameRoot());
1058
1059 if (m_mousePositionIsUnknown)
1060 return;
1061
1062 FrameView* view = m_frame->view();
1063 if (!view || !view->shouldSetCursor())
1064 return;
1065
1066 LayoutView* layoutView = view->layoutView();
1067 if (!layoutView)
1068 return;
1069
1070 m_frame->document()->updateLayout();
1071
1072 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::AllowChild FrameContent);
1073 HitTestResult result(request, view->rootFrameToContents(m_lastKnownMousePosi tion));
1074 layoutView->hitTest(result);
1075
1076 if (LocalFrame* frame = result.innerNodeFrame()) {
1077 OptionalCursor optionalCursor = frame->eventHandler().selectCursor(resul t);
1078 if (optionalCursor.isCursorChange()) {
1079 view->setCursor(optionalCursor.cursor());
1080 }
1081 }
1082 }
1083
1084 OptionalCursor EventHandler::selectCursor(const HitTestResult& result)
1085 {
1086 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode())
1087 return NoCursorChange;
1088
1089 Page* page = m_frame->page();
1090 if (!page)
1091 return NoCursorChange;
1092 if (panScrollInProgress())
1093 return NoCursorChange;
1094
1095 Node* node = result.innerPossiblyPseudoNode();
1096 if (!node)
1097 return selectAutoCursor(result, node, iBeamCursor());
1098
1099 LayoutObject* layoutObject = node->layoutObject();
1100 const ComputedStyle* style = layoutObject ? layoutObject->style() : nullptr;
1101
1102 if (layoutObject) {
1103 Cursor overrideCursor;
1104 switch (layoutObject->getCursor(roundedIntPoint(result.localPoint()), ov errideCursor)) {
1105 case SetCursorBasedOnStyle:
1106 break;
1107 case SetCursor:
1108 return overrideCursor;
1109 case DoNotSetCursor:
1110 return NoCursorChange;
1111 }
1112 }
1113
1114 if (style && style->cursors()) {
1115 const CursorList* cursors = style->cursors();
1116 for (unsigned i = 0; i < cursors->size(); ++i) {
1117 StyleImage* styleImage = (*cursors)[i].image();
1118 if (!styleImage)
1119 continue;
1120 ImageResource* cachedImage = styleImage->cachedImage();
1121 if (!cachedImage)
1122 continue;
1123 float scale = styleImage->imageScaleFactor();
1124 bool hotSpotSpecified = (*cursors)[i].hotSpotSpecified();
1125 // Get hotspot and convert from logical pixels to physical pixels.
1126 IntPoint hotSpot = (*cursors)[i].hotSpot();
1127 hotSpot.scale(scale, scale);
1128 IntSize size = cachedImage->imageForLayoutObject(layoutObject)->size ();
1129 if (cachedImage->errorOccurred())
1130 continue;
1131 // Limit the size of cursors (in UI pixels) so that they cannot be
1132 // used to cover UI elements in chrome.
1133 size.scale(1 / scale);
1134 if (size.width() > maximumCursorSize || size.height() > maximumCurso rSize)
1135 continue;
1136
1137 Image* image = cachedImage->imageForLayoutObject(layoutObject);
1138 // Ensure no overflow possible in calculations above.
1139 if (scale < minimumCursorScale)
1140 continue;
1141 return Cursor(image, hotSpotSpecified, hotSpot, scale);
1142 }
1143 }
1144
1145 switch (style ? style->cursor() : CURSOR_AUTO) {
1146 case CURSOR_AUTO: {
1147 bool horizontalText = !style || style->isHorizontalWritingMode();
1148 const Cursor& iBeam = horizontalText ? iBeamCursor() : verticalTextCurso r();
1149 return selectAutoCursor(result, node, iBeam);
1150 }
1151 case CURSOR_CROSS:
1152 return crossCursor();
1153 case CURSOR_POINTER:
1154 return handCursor();
1155 case CURSOR_MOVE:
1156 return moveCursor();
1157 case CURSOR_ALL_SCROLL:
1158 return moveCursor();
1159 case CURSOR_E_RESIZE:
1160 return eastResizeCursor();
1161 case CURSOR_W_RESIZE:
1162 return westResizeCursor();
1163 case CURSOR_N_RESIZE:
1164 return northResizeCursor();
1165 case CURSOR_S_RESIZE:
1166 return southResizeCursor();
1167 case CURSOR_NE_RESIZE:
1168 return northEastResizeCursor();
1169 case CURSOR_SW_RESIZE:
1170 return southWestResizeCursor();
1171 case CURSOR_NW_RESIZE:
1172 return northWestResizeCursor();
1173 case CURSOR_SE_RESIZE:
1174 return southEastResizeCursor();
1175 case CURSOR_NS_RESIZE:
1176 return northSouthResizeCursor();
1177 case CURSOR_EW_RESIZE:
1178 return eastWestResizeCursor();
1179 case CURSOR_NESW_RESIZE:
1180 return northEastSouthWestResizeCursor();
1181 case CURSOR_NWSE_RESIZE:
1182 return northWestSouthEastResizeCursor();
1183 case CURSOR_COL_RESIZE:
1184 return columnResizeCursor();
1185 case CURSOR_ROW_RESIZE:
1186 return rowResizeCursor();
1187 case CURSOR_TEXT:
1188 return iBeamCursor();
1189 case CURSOR_WAIT:
1190 return waitCursor();
1191 case CURSOR_HELP:
1192 return helpCursor();
1193 case CURSOR_VERTICAL_TEXT:
1194 return verticalTextCursor();
1195 case CURSOR_CELL:
1196 return cellCursor();
1197 case CURSOR_CONTEXT_MENU:
1198 return contextMenuCursor();
1199 case CURSOR_PROGRESS:
1200 return progressCursor();
1201 case CURSOR_NO_DROP:
1202 return noDropCursor();
1203 case CURSOR_ALIAS:
1204 return aliasCursor();
1205 case CURSOR_COPY:
1206 return copyCursor();
1207 case CURSOR_NONE:
1208 return noneCursor();
1209 case CURSOR_NOT_ALLOWED:
1210 return notAllowedCursor();
1211 case CURSOR_DEFAULT:
1212 return pointerCursor();
1213 case CURSOR_ZOOM_IN:
1214 return zoomInCursor();
1215 case CURSOR_ZOOM_OUT:
1216 return zoomOutCursor();
1217 case CURSOR_WEBKIT_GRAB:
1218 return grabCursor();
1219 case CURSOR_WEBKIT_GRABBING:
1220 return grabbingCursor();
1221 }
1222 return pointerCursor();
1223 }
1224
1225 OptionalCursor EventHandler::selectAutoCursor(const HitTestResult& result, Node* node, const Cursor& iBeam)
1226 {
1227 bool editable = (node && node->hasEditableStyle());
1228
1229 if (useHandCursor(node, result.isOverLink()))
1230 return handCursor();
1231
1232 bool inResizer = false;
1233 LayoutObject* layoutObject = node ? node->layoutObject() : nullptr;
1234 if (layoutObject && m_frame->view()) {
1235 DeprecatedPaintLayer* layer = layoutObject->enclosingLayer();
1236 inResizer = layer->scrollableArea() && layer->scrollableArea()->isPointI nResizeControl(result.roundedPointInMainFrame(), ResizerForPointer);
1237 }
1238
1239 // During selection, use an I-beam no matter what we're over.
1240 // If a drag may be starting or we're capturing mouse events for a particula r node, don't treat this as a selection.
1241 if (m_mousePressed && m_mouseDownMayStartSelect
1242 && !m_mouseDownMayStartDrag
1243 && m_frame->selection().isCaretOrRange()
1244 && !m_capturingMouseEventsNode) {
1245 return iBeam;
1246 }
1247
1248 if ((editable || (layoutObject && layoutObject->isText() && node->canStartSe lection())) && !inResizer && !result.scrollbar())
1249 return iBeam;
1250 return pointerCursor();
1251 }
1252
1253 static LayoutPoint contentPointFromRootFrame(LocalFrame* frame, const IntPoint& pointInRootFrame)
1254 {
1255 FrameView* view = frame->view();
1256 // FIXME: Is it really OK to use the wrong coordinates here when view is 0?
1257 // Historically the code would just crash; this is clearly no worse than tha t.
1258 return view ? view->rootFrameToContents(pointInRootFrame) : pointInRootFrame ;
1259 }
1260
1261 bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent)
1262 {
1263 TRACE_EVENT0("blink", "EventHandler::handleMousePressEvent");
1264
1265 RefPtrWillBeRawPtr<FrameView> protector(m_frame->view());
1266
1267 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
1268 m_frame->localFrameRoot()->eventHandler().m_lastMouseDownUserGestureToken = gestureIndicator.currentToken();
1269
1270 cancelFakeMouseMoveEvent();
1271 if (m_eventHandlerWillResetCapturingMouseEventsNode)
1272 m_capturingMouseEventsNode = nullptr;
1273 m_mousePressed = true;
1274 m_capturesDragging = true;
1275 setLastKnownMousePosition(mouseEvent);
1276 m_mouseDownTimestamp = mouseEvent.timestamp();
1277 m_mouseDownMayStartDrag = false;
1278 m_mouseDownMayStartSelect = false;
1279 m_mouseDownMayStartAutoscroll = false;
1280 if (FrameView* view = m_frame->view()) {
1281 m_mouseDownPos = view->rootFrameToContents(mouseEvent.position());
1282 } else {
1283 invalidateClick();
1284 return false;
1285 }
1286
1287 HitTestRequest request(HitTestRequest::Active);
1288 // Save the document point we generate in case the window coordinate is inva lidated by what happens
1289 // when we dispatch the event.
1290 LayoutPoint documentPoint = contentPointFromRootFrame(m_frame, mouseEvent.po sition());
1291 MouseEventWithHitTestResults mev = m_frame->document()->prepareMouseEvent(re quest, documentPoint, mouseEvent);
1292
1293 if (!mev.innerNode()) {
1294 invalidateClick();
1295 return false;
1296 }
1297
1298 m_mousePressNode = mev.innerNode();
1299
1300 RefPtrWillBeRawPtr<LocalFrame> subframe = subframeForHitTestResult(mev);
1301 if (subframe && passMousePressEventToSubframe(mev, subframe.get())) {
1302 // Start capturing future events for this frame. We only do this if we didn't clear
1303 // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop.
1304 m_capturesDragging = subframe->eventHandler().capturesDragging();
1305 if (m_mousePressed && m_capturesDragging) {
1306 m_capturingMouseEventsNode = mev.innerNode();
1307 m_eventHandlerWillResetCapturingMouseEventsNode = true;
1308 }
1309 invalidateClick();
1310 return true;
1311 }
1312
1313 #if OS(WIN)
1314 // We store whether pan scrolling is in progress before calling stopAutoscro ll()
1315 // because it will set m_autoscrollType to NoAutoscroll on return.
1316 bool isPanScrollInProgress = panScrollInProgress();
1317 stopAutoscroll();
1318 if (isPanScrollInProgress) {
1319 // We invalidate the click when exiting pan scrolling so that we don't i nadvertently navigate
1320 // away from the current page (e.g. the click was on a hyperlink). See < rdar://problem/6095023>.
1321 invalidateClick();
1322 return true;
1323 }
1324 #endif
1325
1326 m_clickCount = mouseEvent.clickCount();
1327 m_clickNode = mev.innerNode()->isTextNode() ? ComposedTreeTraversal::parent (*mev.innerNode()) : mev.innerNode();
1328
1329 if (FrameView* view = m_frame->view()) {
1330 DeprecatedPaintLayer* layer = mev.innerNode()->layoutObject() ? mev.inne rNode()->layoutObject()->enclosingLayer() : nullptr;
1331 IntPoint p = view->rootFrameToContents(mouseEvent.position());
1332 if (layer && layer->scrollableArea() && layer->scrollableArea()->isPoint InResizeControl(p, ResizerForPointer)) {
1333 m_resizeScrollableArea = layer->scrollableArea();
1334 m_resizeScrollableArea->setInResizeMode(true);
1335 m_offsetFromResizeCorner = LayoutSize(m_resizeScrollableArea->offset FromResizeCorner(p));
1336 invalidateClick();
1337 return true;
1338 }
1339 }
1340
1341 m_frame->selection().setCaretBlinkingSuspended(true);
1342
1343 bool swallowEvent = !dispatchMouseEvent(EventTypeNames::mousedown, mev.inner Node(), m_clickCount, mouseEvent, true);
1344 HitTestResult hitTestResult = hitTestResultInFrame(m_frame, mouseEvent.posit ion(), HitTestRequest::ReadOnly);
1345 swallowEvent = swallowEvent || handleMouseFocus(MouseEventWithHitTestResults (mouseEvent, hitTestResult));
1346 m_capturesDragging = !swallowEvent || mev.scrollbar();
1347
1348 // If the hit testing originally determined the event was in a scrollbar, re fetch the MouseEventWithHitTestResults
1349 // in case the scrollbar widget was destroyed when the mouse event was handl ed.
1350 if (mev.scrollbar()) {
1351 const bool wasLastScrollBar = mev.scrollbar() == m_lastScrollbarUnderMou se.get();
1352 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active );
1353 mev = m_frame->document()->prepareMouseEvent(request, documentPoint, mou seEvent);
1354 if (wasLastScrollBar && mev.scrollbar() != m_lastScrollbarUnderMouse.get ())
1355 m_lastScrollbarUnderMouse = nullptr;
1356 }
1357
1358 if (swallowEvent) {
1359 // scrollbars should get events anyway, even disabled controls might be scrollable
1360 passMousePressEventToScrollbar(mev);
1361 } else {
1362 if (shouldRefetchEventTarget(mev)) {
1363 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Ac tive);
1364 mev = m_frame->document()->prepareMouseEvent(request, documentPoint, mouseEvent);
1365 }
1366
1367 if (passMousePressEventToScrollbar(mev))
1368 swallowEvent = true;
1369 else
1370 swallowEvent = handleMousePressEvent(mev);
1371 }
1372
1373 return swallowEvent;
1374 }
1375
1376 static DeprecatedPaintLayer* layerForNode(Node* node)
1377 {
1378 if (!node)
1379 return nullptr;
1380
1381 LayoutObject* layoutObject = node->layoutObject();
1382 if (!layoutObject)
1383 return nullptr;
1384
1385 DeprecatedPaintLayer* layer = layoutObject->enclosingLayer();
1386 if (!layer)
1387 return nullptr;
1388
1389 return layer;
1390 }
1391
1392 ScrollableArea* EventHandler::associatedScrollableArea(const DeprecatedPaintLaye r* layer) const
1393 {
1394 if (DeprecatedPaintLayerScrollableArea* scrollableArea = layer->scrollableAr ea()) {
1395 if (scrollableArea->scrollsOverflow())
1396 return scrollableArea;
1397 }
1398
1399 return nullptr;
1400 }
1401
1402 bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& event)
1403 {
1404 TRACE_EVENT0("blink", "EventHandler::handleMouseMoveEvent");
1405
1406 RefPtrWillBeRawPtr<FrameView> protector(m_frame->view());
1407 MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration);
1408
1409 HitTestResult hoveredNode = HitTestResult();
1410 bool result = handleMouseMoveOrLeaveEvent(event, &hoveredNode);
1411
1412 Page* page = m_frame->page();
1413 if (!page)
1414 return result;
1415
1416 if (DeprecatedPaintLayer* layer = layerForNode(hoveredNode.innerNode())) {
1417 if (ScrollableArea* layerScrollableArea = associatedScrollableArea(layer ))
1418 layerScrollableArea->mouseMovedInContentArea();
1419 }
1420
1421 if (FrameView* frameView = m_frame->view())
1422 frameView->mouseMovedInContentArea();
1423
1424 hoveredNode.setToShadowHostIfInUserAgentShadowRoot();
1425 page->chromeClient().mouseDidMoveOverElement(hoveredNode);
1426
1427 return result;
1428 }
1429
1430 void EventHandler::handleMouseLeaveEvent(const PlatformMouseEvent& event)
1431 {
1432 TRACE_EVENT0("blink", "EventHandler::handleMouseLeaveEvent");
1433
1434 RefPtrWillBeRawPtr<FrameView> protector(m_frame->view());
1435 handleMouseMoveOrLeaveEvent(event, 0, false, true);
1436 }
1437
1438 bool EventHandler::handleMouseMoveOrLeaveEvent(const PlatformMouseEvent& mouseEv ent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars, bool forceLeave)
1439 {
1440 ASSERT(m_frame);
1441 ASSERT(m_frame->view());
1442
1443 setLastKnownMousePosition(mouseEvent);
1444
1445 if (m_hoverTimer.isActive())
1446 m_hoverTimer.stop();
1447
1448 m_cursorUpdateTimer.stop();
1449
1450 cancelFakeMouseMoveEvent();
1451
1452 if (m_svgPan) {
1453 m_frame->document()->accessSVGExtensions().updatePan(m_frame->view()->ro otFrameToContents(m_lastKnownMousePosition));
1454 return true;
1455 }
1456
1457 if (m_frameSetBeingResized)
1458 return !dispatchMouseEvent(EventTypeNames::mousemove, m_frameSetBeingRes ized.get(), 0, mouseEvent, false);
1459
1460 // Send events right to a scrollbar if the mouse is pressed.
1461 if (m_lastScrollbarUnderMouse && m_mousePressed) {
1462 m_lastScrollbarUnderMouse->mouseMoved(mouseEvent);
1463 return true;
1464 }
1465
1466 // Mouse events simulated from touch should not hit-test again.
1467 ASSERT(!mouseEvent.fromTouch());
1468
1469 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move;
1470 if (m_mousePressed) {
1471 hitType |= HitTestRequest::Active;
1472 } else if (onlyUpdateScrollbars) {
1473 // Mouse events should be treated as "read-only" if we're updating only scrollbars. This
1474 // means that :hover and :active freeze in the state they were in, rathe r than updating
1475 // for nodes the mouse moves while the window is not key (which will be the case if
1476 // onlyUpdateScrollbars is true).
1477 hitType |= HitTestRequest::ReadOnly;
1478 }
1479
1480 // Treat any mouse move events as readonly if the user is currently touching the screen.
1481 if (m_touchPressed)
1482 hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly;
1483 HitTestRequest request(hitType);
1484 MouseEventWithHitTestResults mev = MouseEventWithHitTestResults(mouseEvent, HitTestResult(request, LayoutPoint()));
1485
1486 // We don't want to do a hit-test in forceLeave scenarios because there migh t actually be some other frame above this one at the specified co-ordinate.
1487 // So we must force the hit-test to fail, while still clearing hover/active state.
1488 if (forceLeave)
1489 m_frame->document()->updateHoverActiveState(request, 0);
1490 else
1491 mev = prepareMouseEvent(request, mouseEvent);
1492
1493 if (hoveredNode)
1494 *hoveredNode = mev.hitTestResult();
1495
1496 Scrollbar* scrollbar = nullptr;
1497
1498 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode()) {
1499 m_resizeScrollableArea->resize(mouseEvent, m_offsetFromResizeCorner);
1500 } else {
1501 if (!scrollbar)
1502 scrollbar = mev.scrollbar();
1503
1504 updateLastScrollbarUnderMouse(scrollbar, !m_mousePressed);
1505 if (onlyUpdateScrollbars)
1506 return true;
1507 }
1508
1509 bool swallowEvent = false;
1510 RefPtrWillBeRawPtr<LocalFrame> newSubframe = m_capturingMouseEventsNode.get( ) ? subframeForTargetNode(m_capturingMouseEventsNode.get()) : subframeForHitTest Result(mev);
1511
1512 // We want mouseouts to happen first, from the inside out. First send a mov e event to the last subframe so that it will fire mouseouts.
1513 if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree().isD escendantOf(m_frame) && m_lastMouseMoveEventSubframe != newSubframe)
1514 m_lastMouseMoveEventSubframe->eventHandler().handleMouseLeaveEvent(mouse Event);
1515
1516 if (newSubframe) {
1517 // Update over/out state before passing the event to the subframe.
1518 updateMouseEventTargetNode(mev.innerNode(), mouseEvent, true);
1519
1520 // Event dispatch in updateMouseEventTargetNode may have caused the subf rame of the target
1521 // node to be detached from its FrameView, in which case the event shoul d not be passed.
1522 if (newSubframe->view())
1523 swallowEvent |= passMouseMoveEventToSubframe(mev, newSubframe.get(), hoveredNode);
1524 } else {
1525 if (scrollbar && !m_mousePressed)
1526 scrollbar->mouseMoved(mouseEvent); // Handle hover effects on platfo rms that support visual feedback on scrollbar hovering.
1527 if (FrameView* view = m_frame->view()) {
1528 OptionalCursor optionalCursor = selectCursor(mev.hitTestResult());
1529 if (optionalCursor.isCursorChange()) {
1530 view->setCursor(optionalCursor.cursor());
1531 }
1532 }
1533 }
1534
1535 m_lastMouseMoveEventSubframe = newSubframe;
1536
1537 if (swallowEvent)
1538 return true;
1539
1540 swallowEvent = !dispatchMouseEvent(EventTypeNames::mousemove, mev.innerNode( ), 0, mouseEvent, true);
1541 if (!swallowEvent)
1542 swallowEvent = handleMouseDraggedEvent(mev);
1543
1544 return swallowEvent;
1545 }
1546
1547 void EventHandler::invalidateClick()
1548 {
1549 m_clickCount = 0;
1550 m_clickNode = nullptr;
1551 }
1552
1553 static ContainerNode* parentForClickEvent(const Node& node)
1554 {
1555 // IE doesn't dispatch click events for mousedown/mouseup events across form
1556 // controls.
1557 if (node.isHTMLElement() && toHTMLElement(node).isInteractiveContent())
1558 return nullptr;
1559
1560 return ComposedTreeTraversal::parent(node);
1561 }
1562
1563 bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent)
1564 {
1565 TRACE_EVENT0("blink", "EventHandler::handleMouseReleaseEvent");
1566
1567 RefPtrWillBeRawPtr<FrameView> protector(m_frame->view());
1568
1569 m_frame->selection().setCaretBlinkingSuspended(false);
1570
1571 OwnPtr<UserGestureIndicator> gestureIndicator;
1572
1573 if (m_frame->localFrameRoot()->eventHandler().m_lastMouseDownUserGestureToke n)
1574 gestureIndicator = adoptPtr(new UserGestureIndicator(m_frame->localFrame Root()->eventHandler().m_lastMouseDownUserGestureToken.release()));
1575 else
1576 gestureIndicator = adoptPtr(new UserGestureIndicator(DefinitelyProcessin gUserGesture));
1577
1578 #if OS(WIN)
1579 if (Page* page = m_frame->page())
1580 page->autoscrollController().handleMouseReleaseForPanScrolling(m_frame, mouseEvent);
1581 #endif
1582
1583 m_mousePressed = false;
1584 setLastKnownMousePosition(mouseEvent);
1585
1586 if (m_svgPan) {
1587 m_svgPan = false;
1588 m_frame->document()->accessSVGExtensions().updatePan(m_frame->view()->ro otFrameToContents(m_lastKnownMousePosition));
1589 return true;
1590 }
1591
1592 if (m_frameSetBeingResized)
1593 return !dispatchMouseEvent(EventTypeNames::mouseup, m_frameSetBeingResiz ed.get(), m_clickCount, mouseEvent, false);
1594
1595 if (m_lastScrollbarUnderMouse) {
1596 invalidateClick();
1597 m_lastScrollbarUnderMouse->mouseUp(mouseEvent);
1598 bool setUnder = false;
1599 return !dispatchMouseEvent(EventTypeNames::mouseup, m_nodeUnderMouse.get (), m_clickCount, mouseEvent, setUnder);
1600 }
1601
1602 // Mouse events simulated from touch should not hit-test again.
1603 ASSERT(!mouseEvent.fromTouch());
1604
1605 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Release;
1606 HitTestRequest request(hitType);
1607 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent);
1608 LocalFrame* subframe = m_capturingMouseEventsNode.get() ? subframeForTargetN ode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev);
1609 if (m_eventHandlerWillResetCapturingMouseEventsNode)
1610 m_capturingMouseEventsNode = nullptr;
1611 if (subframe && passMouseReleaseEventToSubframe(mev, subframe))
1612 return true;
1613
1614 bool swallowMouseUpEvent = !dispatchMouseEvent(EventTypeNames::mouseup, mev. innerNode(), m_clickCount, mouseEvent, false);
1615
1616 bool contextMenuEvent = mouseEvent.button() == RightButton;
1617 #if OS(MACOSX)
1618 // FIXME: The Mac port achieves the same behavior by checking whether the co ntext menu is currently open in WebPage::mouseEvent(). Consider merging the impl ementations.
1619 if (mouseEvent.button() == LeftButton && mouseEvent.modifiers() & PlatformEv ent::CtrlKey)
1620 contextMenuEvent = true;
1621 #endif
1622
1623 bool swallowClickEvent = false;
1624 if (m_clickCount > 0 && !contextMenuEvent && mev.innerNode() && m_clickNode && mev.innerNode()->canParticipateInComposedTree() && m_clickNode->canParticipat eInComposedTree()) {
1625 // Updates distribution because a 'mouseup' event listener can make the
1626 // tree dirty at dispatchMouseEvent() invocation above.
1627 // Unless distribution is updated, commonAncestor would hit ASSERT.
1628 // Both m_clickNode and mev.innerNode() don't need to be updated
1629 // because commonAncestor() will exit early if their documents are diffe rent.
1630 m_clickNode->updateDistribution();
1631 if (Node* clickTargetNode = mev.innerNode()->commonAncestor(*m_clickNode , parentForClickEvent))
1632 swallowClickEvent = !dispatchMouseEvent(EventTypeNames::click, click TargetNode, m_clickCount, mouseEvent, true);
1633 }
1634
1635 if (m_resizeScrollableArea) {
1636 m_resizeScrollableArea->setInResizeMode(false);
1637 m_resizeScrollableArea = nullptr;
1638 }
1639
1640 bool swallowMouseReleaseEvent = false;
1641 if (!swallowMouseUpEvent)
1642 swallowMouseReleaseEvent = handleMouseReleaseEvent(mev);
1643
1644 invalidateClick();
1645
1646 return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
1647 }
1648
1649 bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& mouseEve nt)
1650 {
1651 // If the event was a middle click, attempt to copy global selection in afte r
1652 // the newly set caret position.
1653 //
1654 // This code is called from either the mouse up or mouse down handling. Ther e
1655 // is some debate about when the global selection is pasted:
1656 // xterm: pastes on up.
1657 // GTK: pastes on down.
1658 // Qt: pastes on up.
1659 // Firefox: pastes on up.
1660 // Chromium: pastes on up.
1661 //
1662 // There is something of a webcompat angle to this well, as highlighted by
1663 // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on
1664 // down then the text is pasted just before the onclick handler runs and
1665 // clears the text box. So it's important this happens after the event
1666 // handlers have been fired.
1667 if (mouseEvent.type() != PlatformEvent::MouseReleased)
1668 return false;
1669
1670 if (!m_frame->page())
1671 return false;
1672 Frame* focusFrame = m_frame->page()->focusController().focusedOrMainFrame();
1673 // Do not paste here if the focus was moved somewhere else.
1674 if (m_frame == focusFrame && m_frame->editor().behavior().supportsGlobalSele ction())
1675 return m_frame->editor().command("PasteGlobalSelection").execute();
1676
1677 return false;
1678 }
1679
1680
1681 bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Node* dragTa rget, const PlatformMouseEvent& event, DataTransfer* dataTransfer)
1682 {
1683 FrameView* view = m_frame->view();
1684
1685 // FIXME: We might want to dispatch a dragleave even if the view is gone.
1686 if (!view)
1687 return false;
1688
1689 RefPtrWillBeRawPtr<MouseEvent> me = MouseEvent::create(eventType,
1690 true, true, m_frame->document()->domWindow(),
1691 0, event.globalPosition().x(), event.globalPosition().y(), event.positio n().x(), event.position().y(),
1692 event.movementDelta().x(), event.movementDelta().y(),
1693 event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(),
1694 0, MouseEvent::platformModifiersToButtons(event.modifiers()), nullptr, d ataTransfer);
1695
1696 dragTarget->dispatchEvent(me.get(), IGNORE_EXCEPTION);
1697 return me->defaultPrevented();
1698 }
1699
1700 static bool targetIsFrame(Node* target, LocalFrame*& frame)
1701 {
1702 if (!isHTMLFrameElementBase(target))
1703 return false;
1704
1705 // Cross-process drag and drop is not yet supported.
1706 if (toHTMLFrameElementBase(target)->contentFrame() && !toHTMLFrameElementBas e(target)->contentFrame()->isLocalFrame())
1707 return false;
1708
1709 frame = toLocalFrame(toHTMLFrameElementBase(target)->contentFrame());
1710 return true;
1711 }
1712
1713 static bool findDropZone(Node* target, DataTransfer* dataTransfer)
1714 {
1715 Element* element = target->isElementNode() ? toElement(target) : target->par entElement();
1716 for (; element; element = element->parentElement()) {
1717 bool matched = false;
1718 AtomicString dropZoneStr = element->fastGetAttribute(webkitdropzoneAttr) ;
1719
1720 if (dropZoneStr.isEmpty())
1721 continue;
1722
1723 UseCounter::count(element->document(), UseCounter::PrefixedHTMLElementDr opzone);
1724
1725 dropZoneStr = dropZoneStr.lower();
1726
1727 SpaceSplitString keywords(dropZoneStr, SpaceSplitString::ShouldNotFoldCa se);
1728 if (keywords.isNull())
1729 continue;
1730
1731 DragOperation dragOperation = DragOperationNone;
1732 for (unsigned i = 0; i < keywords.size(); i++) {
1733 DragOperation op = convertDropZoneOperationToDragOperation(keywords[ i]);
1734 if (op != DragOperationNone) {
1735 if (dragOperation == DragOperationNone)
1736 dragOperation = op;
1737 } else {
1738 matched = matched || dataTransfer->hasDropZoneType(keywords[i].s tring());
1739 }
1740
1741 if (matched && dragOperation != DragOperationNone)
1742 break;
1743 }
1744 if (matched) {
1745 dataTransfer->setDropEffect(convertDragOperationToDropZoneOperation( dragOperation));
1746 return true;
1747 }
1748 }
1749 return false;
1750 }
1751
1752 bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, DataTransf er* dataTransfer)
1753 {
1754 bool accept = false;
1755
1756 if (!m_frame->view())
1757 return false;
1758
1759 HitTestRequest request(HitTestRequest::ReadOnly);
1760 MouseEventWithHitTestResults mev = prepareMouseEvent(request, event);
1761
1762 // Drag events should never go to text nodes (following IE, and proper mouse over/out dispatch)
1763 RefPtrWillBeRawPtr<Node> newTarget = mev.innerNode();
1764 if (newTarget && newTarget->isTextNode())
1765 newTarget = ComposedTreeTraversal::parent(*newTarget);
1766
1767 if (AutoscrollController* controller = autoscrollController())
1768 controller->updateDragAndDrop(newTarget.get(), event.position(), event.t imestamp());
1769
1770 if (m_dragTarget != newTarget) {
1771 // FIXME: this ordering was explicitly chosen to match WinIE. However,
1772 // it is sometimes incorrect when dragging within subframes, as seen wit h
1773 // LayoutTests/fast/events/drag-in-frames.html.
1774 //
1775 // 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>.
1776 LocalFrame* targetFrame;
1777 if (targetIsFrame(newTarget.get(), targetFrame)) {
1778 if (targetFrame)
1779 accept = targetFrame->eventHandler().updateDragAndDrop(event, da taTransfer);
1780 } else if (newTarget) {
1781 // As per section 7.9.4 of the HTML 5 spec., we must always fire a d rag event before firing a dragenter, dragleave, or dragover event.
1782 if (dragState().m_dragSrc) {
1783 // for now we don't care if event handler cancels default behavi or, since there is none
1784 dispatchDragSrcEvent(EventTypeNames::drag, event);
1785 }
1786 accept = dispatchDragEvent(EventTypeNames::dragenter, newTarget.get( ), event, dataTransfer);
1787 if (!accept)
1788 accept = findDropZone(newTarget.get(), dataTransfer);
1789 }
1790
1791 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
1792 if (targetFrame)
1793 accept = targetFrame->eventHandler().updateDragAndDrop(event, da taTransfer);
1794 } else if (m_dragTarget) {
1795 dispatchDragEvent(EventTypeNames::dragleave, m_dragTarget.get(), eve nt, dataTransfer);
1796 }
1797
1798 if (newTarget) {
1799 // We do not explicitly call dispatchDragEvent here because it could ultimately result in the appearance that
1800 // two dragover events fired. So, we mark that we should only fire a dragover event on the next call to this function.
1801 m_shouldOnlyFireDragOverEvent = true;
1802 }
1803 } else {
1804 LocalFrame* targetFrame;
1805 if (targetIsFrame(newTarget.get(), targetFrame)) {
1806 if (targetFrame)
1807 accept = targetFrame->eventHandler().updateDragAndDrop(event, da taTransfer);
1808 } else if (newTarget) {
1809 // Note, when dealing with sub-frames, we may need to fire only a dr agover event as a drag event may have been fired earlier.
1810 if (!m_shouldOnlyFireDragOverEvent && dragState().m_dragSrc) {
1811 // for now we don't care if event handler cancels default behavi or, since there is none
1812 dispatchDragSrcEvent(EventTypeNames::drag, event);
1813 }
1814 accept = dispatchDragEvent(EventTypeNames::dragover, newTarget.get() , event, dataTransfer);
1815 if (!accept)
1816 accept = findDropZone(newTarget.get(), dataTransfer);
1817 m_shouldOnlyFireDragOverEvent = false;
1818 }
1819 }
1820 m_dragTarget = newTarget;
1821
1822 return accept;
1823 }
1824
1825 void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, DataTransf er* dataTransfer)
1826 {
1827 LocalFrame* targetFrame;
1828 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
1829 if (targetFrame)
1830 targetFrame->eventHandler().cancelDragAndDrop(event, dataTransfer);
1831 } else if (m_dragTarget.get()) {
1832 if (dragState().m_dragSrc)
1833 dispatchDragSrcEvent(EventTypeNames::drag, event);
1834 dispatchDragEvent(EventTypeNames::dragleave, m_dragTarget.get(), event, dataTransfer);
1835 }
1836 clearDragState();
1837 }
1838
1839 bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, DataTrans fer* dataTransfer)
1840 {
1841 LocalFrame* targetFrame;
1842 bool preventedDefault = false;
1843 if (targetIsFrame(m_dragTarget.get(), targetFrame)) {
1844 if (targetFrame)
1845 preventedDefault = targetFrame->eventHandler().performDragAndDrop(ev ent, dataTransfer);
1846 } else if (m_dragTarget.get()) {
1847 preventedDefault = dispatchDragEvent(EventTypeNames::drop, m_dragTarget. get(), event, dataTransfer);
1848 }
1849 clearDragState();
1850 return preventedDefault;
1851 }
1852
1853 void EventHandler::clearDragState()
1854 {
1855 stopAutoscroll();
1856 m_dragTarget = nullptr;
1857 m_capturingMouseEventsNode = nullptr;
1858 m_shouldOnlyFireDragOverEvent = false;
1859 }
1860
1861 void EventHandler::setCapturingMouseEventsNode(PassRefPtrWillBeRawPtr<Node> n)
1862 {
1863 m_capturingMouseEventsNode = n;
1864 m_eventHandlerWillResetCapturingMouseEventsNode = false;
1865 }
1866
1867 MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestReques t& request, const PlatformMouseEvent& mev)
1868 {
1869 ASSERT(m_frame);
1870 ASSERT(m_frame->document());
1871
1872 return m_frame->document()->prepareMouseEvent(request, contentPointFromRootF rame(m_frame, mev.position()), mev);
1873 }
1874
1875 void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo useEvent& mouseEvent, bool fireMouseEvents)
1876 {
1877 Node* result = targetNode;
1878
1879 // If we're capturing, we always go right to that node.
1880 if (m_capturingMouseEventsNode) {
1881 result = m_capturingMouseEventsNode.get();
1882 } else {
1883 // If the target node is a text node, dispatch on the parent node - rdar ://4196646
1884 if (result && result->isTextNode())
1885 result = ComposedTreeTraversal::parent(*result);
1886 }
1887 m_nodeUnderMouse = result;
1888
1889 if (fireMouseEvents) {
1890 DeprecatedPaintLayer* layerForLastNode = layerForNode(m_lastNodeUnderMou se.get());
1891 DeprecatedPaintLayer* layerForNodeUnderMouse = layerForNode(m_nodeUnderM ouse.get());
1892 Page* page = m_frame->page();
1893
1894 if (m_lastNodeUnderMouse && (!m_nodeUnderMouse || m_nodeUnderMouse->docu ment() != m_frame->document())) {
1895 // The mouse has moved between frames.
1896 if (LocalFrame* frame = m_lastNodeUnderMouse->document().frame()) {
1897 if (FrameView* frameView = frame->view())
1898 frameView->mouseExitedContentArea();
1899 }
1900 } else if (page && (layerForLastNode && (!layerForNodeUnderMouse || laye rForNodeUnderMouse != layerForLastNode))) {
1901 // The mouse has moved between layers.
1902 if (ScrollableArea* scrollableAreaForLastNode = associatedScrollable Area(layerForLastNode))
1903 scrollableAreaForLastNode->mouseExitedContentArea();
1904 }
1905
1906 if (m_nodeUnderMouse && (!m_lastNodeUnderMouse || m_lastNodeUnderMouse-> document() != m_frame->document())) {
1907 // The mouse has moved between frames.
1908 if (LocalFrame* frame = m_nodeUnderMouse->document().frame()) {
1909 if (FrameView* frameView = frame->view())
1910 frameView->mouseEnteredContentArea();
1911 }
1912 } else if (page && (layerForNodeUnderMouse && (!layerForLastNode || laye rForNodeUnderMouse != layerForLastNode))) {
1913 // The mouse has moved between layers.
1914 if (ScrollableArea* scrollableAreaForNodeUnderMouse = associatedScro llableArea(layerForNodeUnderMouse))
1915 scrollableAreaForNodeUnderMouse->mouseEnteredContentArea();
1916 }
1917
1918 if (m_lastNodeUnderMouse && m_lastNodeUnderMouse->document() != m_frame- >document()) {
1919 m_lastNodeUnderMouse = nullptr;
1920 m_lastScrollbarUnderMouse = nullptr;
1921 }
1922
1923 if (m_lastNodeUnderMouse != m_nodeUnderMouse)
1924 sendMouseEventsForNodeTransition(m_lastNodeUnderMouse.get(), m_nodeU nderMouse.get(), mouseEvent);
1925
1926 m_lastNodeUnderMouse = m_nodeUnderMouse;
1927 }
1928 }
1929
1930 void EventHandler::sendMouseEventsForNodeTransition(Node* exitedNode, Node* ente redNode, const PlatformMouseEvent& mouseEvent)
1931 {
1932 ASSERT(exitedNode != enteredNode);
1933
1934 // First, dispatch mouseout event (which bubbles to ancestors)
1935 if (exitedNode)
1936 exitedNode->dispatchMouseEvent(mouseEvent, EventTypeNames::mouseout, 0, enteredNode);
1937
1938 // A note on mouseenter and mouseleave: These are non-bubbling events, and t hey are dispatched if there
1939 // is a capturing event handler on an ancestor or a normal event handler on the element itself. This special
1940 // handling is necessary to avoid O(n^2) capturing event handler checks.
1941 //
1942 // Note, however, that this optimization can possibly cause some unanswere d/missing/redundant mouseenter or
1943 // mouseleave events in certain contrived eventhandling scenarios, e.g., whe n:
1944 // - the mouseleave handler for a node sets the only capturing-mouseleave-li stener in its ancestor, or
1945 // - DOM mods in any mouseenter/mouseleave handler changes the common ancest or of exited & entered nodes, etc.
1946 // We think the spec specifies a "frozen" state to avoid such corner cases ( check the discussion on "candidate event
1947 // listeners" at http://www.w3.org/TR/uievents), but our code below preserve s one such behavior from past only to
1948 // match Firefox and IE behavior.
1949 //
1950 // TODO(mustaq): Confirm spec conformance, double-check with other browsers.
1951
1952 // Create lists of all exited/entered ancestors.
1953 WillBeHeapVector<RefPtrWillBeMember<Node>, 32> exitedAncestors;
1954 WillBeHeapVector<RefPtrWillBeMember<Node>, 32> enteredAncestors;
1955 if (exitedNode) {
1956 exitedNode->updateDistribution();
1957 for (Node* node = exitedNode; node; node = ComposedTreeTraversal::parent (*node)) {
1958 exitedAncestors.append(node);
1959 }
1960 }
1961 if (enteredNode) {
1962 enteredNode->updateDistribution();
1963 for (Node* node = enteredNode; node; node = ComposedTreeTraversal::paren t(*node)) {
1964 enteredAncestors.append(node);
1965 }
1966 }
1967
1968 size_t numExitedAncestors = exitedAncestors.size();
1969 size_t numEnteredAncestors = enteredAncestors.size();
1970
1971 // Locate the common ancestor in the two lists. Start with the assumption th at it's off both the lists.
1972 size_t exitedAncestorIndex = numExitedAncestors;
1973 size_t enteredAncestorIndex = numEnteredAncestors;
1974 for (size_t j = 0; j < numExitedAncestors; j++) {
1975 for (size_t i = 0; i < numEnteredAncestors; i++) {
1976 if (exitedAncestors[j] == enteredAncestors[i]) {
1977 exitedAncestorIndex = j;
1978 enteredAncestorIndex = i;
1979 break;
1980 }
1981 }
1982 if (exitedAncestorIndex < numExitedAncestors)
1983 break;
1984 }
1985
1986 // Determine if there is a capturing mouseleave listener in an ancestor.
1987 bool exitedNodeHasCapturingAncestor = false;
1988 for (size_t j = 0; j < numExitedAncestors; j++) {
1989 if (exitedAncestors[j]->hasCapturingEventListeners(EventTypeNames::mouse leave))
1990 exitedNodeHasCapturingAncestor = true;
1991 }
1992
1993 // Send mouseleave events to appropriate exited ancestors, in child-to-paren t order.
1994 for (size_t j = 0; j < exitedAncestorIndex; j++) {
1995 if (exitedNodeHasCapturingAncestor || exitedAncestors[j]->hasEventListen ers(EventTypeNames::mouseleave))
1996 exitedAncestors[j]->dispatchMouseEvent(mouseEvent, EventTypeNames::m ouseleave, 0, enteredNode);
1997 }
1998
1999 // Dispatch mouseover event (which bubbles to ancestors) after the mouseleav e events are sent.
2000 if (enteredNode)
2001 enteredNode->dispatchMouseEvent(mouseEvent, EventTypeNames::mouseover, 0 , exitedNode);
2002
2003 // Determine if there is a capturing mouseenter listener in an ancestor. Thi s must be done /after/ dispatching the
2004 // mouseleave events because the handler for mouseleave might set a capturin g mouseenter handler.
2005 bool enteredNodeHasCapturingAncestor = false;
2006 for (size_t i = 0; i < numEnteredAncestors; i++) {
2007 if (enteredAncestors[i]->hasCapturingEventListeners(EventTypeNames::mous eenter))
2008 enteredNodeHasCapturingAncestor = true;
2009 }
2010
2011 // Send mouseenter events to appropriate entered ancestors, in parent-to-chi ld order.
2012 for (size_t i = enteredAncestorIndex; i > 0; i--) {
2013 if (enteredNodeHasCapturingAncestor || enteredAncestors[i-1]->hasEventLi steners(EventTypeNames::mouseenter))
2014 enteredAncestors[i-1]->dispatchMouseEvent(mouseEvent, EventTypeNames ::mouseenter, 0, exitedNode);
2015 }
2016 }
2017
2018 // The return value means 'continue default handling.'
2019 // TODO(mustaq): setUnder needs a more informative name.
2020 bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe tNode, int clickCount, const PlatformMouseEvent& mouseEvent, bool setUnder)
2021 {
2022 updateMouseEventTargetNode(targetNode, mouseEvent, setUnder);
2023 return !m_nodeUnderMouse || m_nodeUnderMouse->dispatchMouseEvent(mouseEvent, eventType, clickCount);
2024 }
2025
2026 // The return value means 'swallow event' (was handled), as for other handle* fu nctions.
2027 bool EventHandler::handleMouseFocus(const MouseEventWithHitTestResults& targeted Event)
2028 {
2029 const PlatformMouseEvent& mouseEvent = targetedEvent.event();
2030
2031 // If clicking on a frame scrollbar, do not mess up with content focus.
2032 if (FrameView* view = m_frame->view()) {
2033 if (view->scrollbarAtRootFramePoint(mouseEvent.position()))
2034 return false;
2035 }
2036
2037 // The layout needs to be up to date to determine if an element is focusable .
2038 m_frame->document()->updateLayoutIgnorePendingStylesheets();
2039
2040 Element* element = nullptr;
2041 if (m_nodeUnderMouse)
2042 element = m_nodeUnderMouse->isElementNode() ? toElement(m_nodeUnderMouse ) : m_nodeUnderMouse->parentOrShadowHostElement();
2043 for (; element; element = element->parentOrShadowHostElement()) {
2044 if (element->isFocusable() && element->isFocusedElementInDocument())
2045 return false;
2046 if (element->isMouseFocusable())
2047 break;
2048 }
2049 ASSERT(!element || element->isMouseFocusable());
2050
2051 // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus
2052 // a node on mouse down if it's selected and inside a focused node. It will
2053 // be focused if the user does a mouseup over it, however, because the
2054 // mouseup will set a selection inside it, which will call
2055 // FrameSelection::setFocusedNodeIfNeeded.
2056 if (element
2057 && m_frame->selection().isRange()
2058 && m_frame->selection().toNormalizedRange()->compareNode(element, IGNORE _EXCEPTION) == Range::NODE_INSIDE
2059 && element->isDescendantOf(m_frame->document()->focusedElement()))
2060 return false;
2061
2062 // Only change the focus when clicking scrollbars if it can transfered to a
2063 // mouse focusable node.
2064 if (!element && targetedEvent.hitTestResult().scrollbar())
2065 return true;
2066
2067 if (Page* page = m_frame->page()) {
2068 // If focus shift is blocked, we eat the event. Note we should never
2069 // clear swallowEvent if the page already set it (e.g., by canceling
2070 // default behavior).
2071 if (element) {
2072 if (slideFocusOnShadowHostIfNecessary(*element))
2073 return true;
2074 if (!page->focusController().setFocusedElement(element, m_frame, Web FocusTypeMouse))
2075 return true;
2076 } else {
2077 // We call setFocusedElement even with !element in order to blur
2078 // current focus element when a link is clicked; this is expected by
2079 // some sites that rely on onChange handlers running from form
2080 // fields before the button click is processed.
2081 if (!page->focusController().setFocusedElement(0, m_frame))
2082 return true;
2083 }
2084 }
2085
2086 return false;
2087 }
2088
2089 bool EventHandler::slideFocusOnShadowHostIfNecessary(const Element& element)
2090 {
2091 if (element.shadowRoot() && !element.tabStop()) {
2092 Document* doc = m_frame->document();
2093 if (element.containsIncludingShadowDOM(doc->focusedElement())) {
2094 // If the inner element is already focused, do nothing.
2095 return true;
2096 }
2097
2098 // If the host has a focusable inner element, focus it. Otherwise, the h ost takes focus.
2099 Page* page = m_frame->page();
2100 ASSERT(page);
2101 Node* next = page->focusController().findFocusableNode(WebFocusTypeForwa rd, *element.shadowRoot());
2102 if (next && next->isElementNode() && element.containsIncludingShadowDOM( next)) {
2103 // Use WebFocusTypeForward instead of WebFocusTypeMouse here to mean the focus has slided.
2104 toElement(next)->focus(false, WebFocusTypeForward);
2105 return true;
2106 }
2107 }
2108 return false;
2109 }
2110
2111 bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event)
2112 {
2113 #define RETURN_WHEEL_EVENT_HANDLED() \
2114 { \
2115 setFrameWasScrolledByUser(); \
2116 return true; \
2117 }
2118
2119 Document* doc = m_frame->document();
2120
2121 if (!doc->layoutView())
2122 return false;
2123
2124 RefPtrWillBeRawPtr<FrameView> protector(m_frame->view());
2125
2126 FrameView* view = m_frame->view();
2127 if (!view)
2128 return false;
2129
2130 LayoutPoint vPoint = view->rootFrameToContents(event.position());
2131
2132 HitTestRequest request(HitTestRequest::ReadOnly);
2133 HitTestResult result(request, vPoint);
2134 doc->layoutView()->hitTest(result);
2135
2136 Node* node = result.innerNode();
2137 // Wheel events should not dispatch to text nodes.
2138 if (node && node->isTextNode())
2139 node = ComposedTreeTraversal::parent(*node);
2140
2141 bool isOverWidget;
2142 if (event.useLatchedEventNode()) {
2143 if (!m_latchedWheelEventNode) {
2144 m_latchedWheelEventNode = node;
2145 m_widgetIsLatched = result.isOverWidget();
2146 } else {
2147 node = m_latchedWheelEventNode.get();
2148 }
2149
2150 isOverWidget = m_widgetIsLatched;
2151 } else {
2152 if (m_latchedWheelEventNode)
2153 m_latchedWheelEventNode = nullptr;
2154 if (m_previousWheelScrolledNode)
2155 m_previousWheelScrolledNode = nullptr;
2156
2157 isOverWidget = result.isOverWidget();
2158 }
2159
2160 if (node) {
2161 // Figure out which view to send the event to.
2162 LayoutObject* target = node->layoutObject();
2163
2164 if (isOverWidget && target && target->isLayoutPart()) {
2165 Widget* widget = toLayoutPart(target)->widget();
2166 if (widget && passWheelEventToWidget(event, *widget))
2167 RETURN_WHEEL_EVENT_HANDLED();
2168 }
2169
2170 if (node && !node->dispatchWheelEvent(event))
2171 RETURN_WHEEL_EVENT_HANDLED();
2172 }
2173
2174 // We do another check on the frame view because the event handler can run
2175 // JS which results in the frame getting destroyed.
2176 view = m_frame->view();
2177 if (!view)
2178 return false;
2179
2180 if (view->scrollableArea()->handleWheel(event).didScroll)
2181 RETURN_WHEEL_EVENT_HANDLED();
2182
2183 return false;
2184 #undef RETURN_WHEEL_EVENT_HANDLED
2185 }
2186
2187 void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEv ent)
2188 {
2189 if (!startNode || !wheelEvent)
2190 return;
2191
2192 // When the wheelEvent do not scroll, we trigger zoom in/out instead.
2193 if (!wheelEvent->canScroll())
2194 return;
2195
2196 Node* stopNode = m_previousWheelScrolledNode.get();
2197 ScrollGranularity granularity = wheelGranularityToScrollGranularity(wheelEve nt);
2198 IntPoint absolutePosition = roundedIntPoint(wheelEvent->absoluteLocation());
2199
2200 // Break up into two scrolls if we need to. Diagonal movement on
2201 // a MacBook pro is an example of a 2-dimensional mouse wheel event (where b oth deltaX and deltaY can be set).
2202
2203 // FIXME: enable scroll customization in this case. See crbug.com/410974.
2204 if (wheelEvent->railsMode() != Event::RailsModeVertical
2205 && scroll(ScrollRightIgnoringWritingMode, granularity, startNode, &stopN ode, wheelEvent->deltaX(), absolutePosition))
2206 wheelEvent->setDefaultHandled();
2207
2208 if (wheelEvent->railsMode() != Event::RailsModeHorizontal
2209 && scroll(ScrollDownIgnoringWritingMode, granularity, startNode, &stopNo de, wheelEvent->deltaY(), absolutePosition))
2210 wheelEvent->setDefaultHandled();
2211
2212 if (!m_latchedWheelEventNode)
2213 m_previousWheelScrolledNode = stopNode;
2214 }
2215
2216 bool EventHandler::handleGestureShowPress()
2217 {
2218 m_lastShowPressTimestamp = WTF::currentTime();
2219
2220 FrameView* view = m_frame->view();
2221 if (!view)
2222 return false;
2223 if (ScrollAnimator* scrollAnimator = view->existingScrollAnimator())
2224 scrollAnimator->cancelAnimations();
2225 const FrameView::ScrollableAreaSet* areas = view->scrollableAreas();
2226 if (!areas)
2227 return false;
2228 for (const ScrollableArea* scrollableArea : *areas) {
2229 ScrollAnimator* animator = scrollableArea->existingScrollAnimator();
2230 if (animator)
2231 animator->cancelAnimations();
2232 }
2233 return false;
2234 }
2235
2236 bool EventHandler::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
2237 {
2238 // Propagation to inner frames is handled below this function.
2239 ASSERT(m_frame == m_frame->localFrameRoot());
2240
2241 // Scrolling-related gesture events invoke EventHandler recursively for each frame down
2242 // the chain, doing a single-frame hit-test per frame. This matches handleWh eelEvent.
2243 // FIXME: Add a test that traverses this path, e.g. for devtools overlay.
2244 if (gestureEvent.isScrollEvent())
2245 return handleGestureScrollEvent(gestureEvent);
2246
2247 // Hit test across all frames and do touch adjustment as necessary for the e vent type.
2248 GestureEventWithHitTestResults targetedEvent = targetGestureEvent(gestureEve nt);
2249
2250 return handleGestureEvent(targetedEvent);
2251 }
2252
2253 bool EventHandler::handleGestureEvent(const GestureEventWithHitTestResults& targ etedEvent)
2254 {
2255 TRACE_EVENT0("input", "EventHandler::handleGestureEvent");
2256
2257 // Propagation to inner frames is handled below this function.
2258 ASSERT(m_frame == m_frame->localFrameRoot());
2259
2260 // Non-scrolling related gesture events do a single cross-frame hit-test and jump
2261 // directly to the inner most frame. This matches handleMousePressEvent etc.
2262 ASSERT(!targetedEvent.event().isScrollEvent());
2263
2264 // Route to the correct frame.
2265 if (LocalFrame* innerFrame = targetedEvent.hitTestResult().innerNodeFrame())
2266 return innerFrame->eventHandler().handleGestureEventInFrame(targetedEven t);
2267
2268 // No hit test result, handle in root instance. Perhaps we should just retur n false instead?
2269 return handleGestureEventInFrame(targetedEvent);
2270 }
2271
2272 bool EventHandler::handleGestureEventInFrame(const GestureEventWithHitTestResult s& targetedEvent)
2273 {
2274 ASSERT(!targetedEvent.event().isScrollEvent());
2275
2276 RefPtrWillBeRawPtr<Node> eventTarget = targetedEvent.hitTestResult().innerNo de();
2277 RefPtrWillBeRawPtr<Scrollbar> scrollbar = targetedEvent.hitTestResult().scro llbar();
2278 const PlatformGestureEvent& gestureEvent = targetedEvent.event();
2279
2280 if (scrollbar) {
2281 bool eventSwallowed = scrollbar->gestureEvent(gestureEvent);
2282 if (gestureEvent.type() == PlatformEvent::GestureTapDown && eventSwallow ed)
2283 m_scrollbarHandlingScrollGesture = scrollbar;
2284 if (eventSwallowed)
2285 return true;
2286 }
2287
2288 if (eventTarget && eventTarget->dispatchGestureEvent(gestureEvent))
2289 return true;
2290
2291 switch (gestureEvent.type()) {
2292 case PlatformEvent::GestureTap:
2293 return handleGestureTap(targetedEvent);
2294 case PlatformEvent::GestureShowPress:
2295 return handleGestureShowPress();
2296 case PlatformEvent::GestureLongPress:
2297 return handleGestureLongPress(targetedEvent);
2298 case PlatformEvent::GestureLongTap:
2299 return handleGestureLongTap(targetedEvent);
2300 case PlatformEvent::GestureTwoFingerTap:
2301 return sendContextMenuEventForGesture(targetedEvent);
2302 case PlatformEvent::GestureTapDown:
2303 case PlatformEvent::GesturePinchBegin:
2304 case PlatformEvent::GesturePinchEnd:
2305 case PlatformEvent::GesturePinchUpdate:
2306 case PlatformEvent::GestureTapDownCancel:
2307 case PlatformEvent::GestureTapUnconfirmed:
2308 break;
2309 default:
2310 ASSERT_NOT_REACHED();
2311 }
2312
2313 return false;
2314 }
2315
2316 bool EventHandler::handleGestureScrollEvent(const PlatformGestureEvent& gestureE vent)
2317 {
2318 TRACE_EVENT0("input", "EventHandler::handleGestureScrollEvent");
2319
2320 RefPtrWillBeRawPtr<Node> eventTarget = nullptr;
2321 RefPtrWillBeRawPtr<Scrollbar> scrollbar = nullptr;
2322 if (gestureEvent.type() != PlatformEvent::GestureScrollBegin) {
2323 scrollbar = m_scrollbarHandlingScrollGesture.get();
2324 eventTarget = m_scrollGestureHandlingNode.get();
2325 }
2326
2327 if (!eventTarget) {
2328 Document* document = m_frame->document();
2329 if (!document->layoutView())
2330 return false;
2331
2332 FrameView* view = m_frame->view();
2333 LayoutPoint viewPoint = view->rootFrameToContents(gestureEvent.position( ));
2334 HitTestRequest request(HitTestRequest::ReadOnly);
2335 HitTestResult result(request, viewPoint);
2336 document->layoutView()->hitTest(result);
2337
2338 eventTarget = result.innerNode();
2339
2340 m_lastGestureScrollOverWidget = result.isOverWidget();
2341 m_scrollGestureHandlingNode = eventTarget;
2342 m_previousGestureScrolledNode = nullptr;
2343
2344 if (!scrollbar)
2345 scrollbar = result.scrollbar();
2346 }
2347
2348 if (scrollbar) {
2349 bool eventSwallowed = scrollbar->gestureEvent(gestureEvent);
2350 if (gestureEvent.type() == PlatformEvent::GestureScrollEnd
2351 || gestureEvent.type() == PlatformEvent::GestureFlingStart
2352 || !eventSwallowed) {
2353 m_scrollbarHandlingScrollGesture = nullptr;
2354 }
2355 if (eventSwallowed)
2356 return true;
2357 }
2358
2359 if (eventTarget) {
2360 bool eventSwallowed = handleScrollGestureOnResizer(eventTarget.get(), ge stureEvent);
2361 if (!eventSwallowed)
2362 eventSwallowed = eventTarget->dispatchGestureEvent(gestureEvent);
2363 if (eventSwallowed)
2364 return true;
2365 }
2366
2367 switch (gestureEvent.type()) {
2368 case PlatformEvent::GestureScrollBegin:
2369 return handleGestureScrollBegin(gestureEvent);
2370 case PlatformEvent::GestureScrollUpdate:
2371 return handleGestureScrollUpdate(gestureEvent);
2372 case PlatformEvent::GestureScrollEnd:
2373 return handleGestureScrollEnd(gestureEvent);
2374 case PlatformEvent::GestureFlingStart:
2375 case PlatformEvent::GesturePinchBegin:
2376 case PlatformEvent::GesturePinchEnd:
2377 case PlatformEvent::GesturePinchUpdate:
2378 return false;
2379 default:
2380 ASSERT_NOT_REACHED();
2381 return false;
2382 }
2383 }
2384
2385 bool EventHandler::handleGestureTap(const GestureEventWithHitTestResults& target edEvent)
2386 {
2387 RefPtrWillBeRawPtr<FrameView> frameView(m_frame->view());
2388 const PlatformGestureEvent& gestureEvent = targetedEvent.event();
2389 HitTestRequest::HitTestRequestType hitType = getHitTypeForGestureType(gestur eEvent.type());
2390 uint64_t preDispatchDomTreeVersion = m_frame->document()->domTreeVersion();
2391 uint64_t preDispatchStyleVersion = m_frame->document()->styleVersion();
2392
2393 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
2394
2395 HitTestResult currentHitTest = targetedEvent.hitTestResult();
2396
2397 // We use the adjusted position so the application isn't surprised to see a event with
2398 // co-ordinates outside the target's bounds.
2399 IntPoint adjustedPoint = frameView->rootFrameToContents(gestureEvent.positio n());
2400
2401 unsigned modifiers = gestureEvent.modifiers();
2402 PlatformMouseEvent fakeMouseMove(gestureEvent.position(), gestureEvent.globa lPosition(),
2403 NoButton, PlatformEvent::MouseMoved, /* clickCount */ 0,
2404 static_cast<PlatformEvent::Modifiers>(modifiers),
2405 PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
2406 dispatchMouseEvent(EventTypeNames::mousemove, currentHitTest.innerNode(), 0, fakeMouseMove, true);
2407
2408 // Do a new hit-test in case the mousemove event changed the DOM.
2409 // Note that if the original hit test wasn't over an element (eg. was over a scrollbar) we
2410 // don't want to re-hit-test because it may be in the wrong frame (and there 's no way the page
2411 // could have seen the event anyway).
2412 // Also note that the position of the frame may have changed, so we need to recompute the content
2413 // co-ordinates (updating layout/style as hitTestResultAtPoint normally woul d).
2414 // FIXME: Use a hit-test cache to avoid unnecessary hit tests. http://crbug. com/398920
2415 if (currentHitTest.innerNode()) {
2416 LocalFrame* mainFrame = m_frame->localFrameRoot();
2417 if (mainFrame && mainFrame->view())
2418 mainFrame->view()->updateLayoutAndStyleForPainting();
2419 adjustedPoint = frameView->rootFrameToContents(gestureEvent.position());
2420 currentHitTest = hitTestResultInFrame(m_frame, adjustedPoint, hitType);
2421 }
2422 m_clickNode = currentHitTest.innerNode();
2423
2424 // Capture data for showUnhandledTapUIIfNeeded.
2425 RefPtrWillBeRawPtr<Node> tappedNode = m_clickNode;
2426 IntPoint tappedPosition = gestureEvent.position();
2427
2428 if (m_clickNode && m_clickNode->isTextNode())
2429 m_clickNode = ComposedTreeTraversal::parent(*m_clickNode);
2430
2431 PlatformMouseEvent fakeMouseDown(gestureEvent.position(), gestureEvent.globa lPosition(),
2432 LeftButton, PlatformEvent::MousePressed, gestureEvent.tapCount(),
2433 static_cast<PlatformEvent::Modifiers>(modifiers | PlatformEvent::LeftBut tonDown),
2434 PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
2435 bool swallowMouseDownEvent = !dispatchMouseEvent(EventTypeNames::mousedown, currentHitTest.innerNode(), gestureEvent.tapCount(), fakeMouseDown, true);
2436 if (!swallowMouseDownEvent)
2437 swallowMouseDownEvent = handleMouseFocus(MouseEventWithHitTestResults(fa keMouseDown, currentHitTest));
2438 if (!swallowMouseDownEvent)
2439 swallowMouseDownEvent = handleMousePressEvent(MouseEventWithHitTestResul ts(fakeMouseDown, currentHitTest));
2440
2441 // FIXME: Use a hit-test cache to avoid unnecessary hit tests. http://crbug. com/398920
2442 if (currentHitTest.innerNode()) {
2443 LocalFrame* mainFrame = m_frame->localFrameRoot();
2444 if (mainFrame && mainFrame->view())
2445 mainFrame->view()->updateLayoutAndStyleForPainting();
2446 adjustedPoint = frameView->rootFrameToContents(gestureEvent.position());
2447 currentHitTest = hitTestResultInFrame(m_frame, adjustedPoint, hitType);
2448 }
2449 PlatformMouseEvent fakeMouseUp(gestureEvent.position(), gestureEvent.globalP osition(),
2450 LeftButton, PlatformEvent::MouseReleased, gestureEvent.tapCount(),
2451 static_cast<PlatformEvent::Modifiers>(modifiers),
2452 PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
2453 bool swallowMouseUpEvent = !dispatchMouseEvent(EventTypeNames::mouseup, curr entHitTest.innerNode(), gestureEvent.tapCount(), fakeMouseUp, false);
2454
2455 bool swallowClickEvent = false;
2456 if (m_clickNode) {
2457 if (currentHitTest.innerNode()) {
2458 // Updates distribution because a mouseup (or mousedown) event liste ner can make the
2459 // tree dirty at dispatchMouseEvent() invocation above.
2460 // Unless distribution is updated, commonAncestor would hit ASSERT.
2461 // Both m_clickNode and currentHitTest.innerNode()) don't need to be updated
2462 // because commonAncestor() will exit early if their documents are d ifferent.
2463 m_clickNode->updateDistribution();
2464 Node* clickTargetNode = currentHitTest.innerNode()->commonAncestor(* m_clickNode, parentForClickEvent);
2465 swallowClickEvent = !dispatchMouseEvent(EventTypeNames::click, click TargetNode, gestureEvent.tapCount(), fakeMouseUp, true);
2466 }
2467 m_clickNode = nullptr;
2468 }
2469
2470 if (!swallowMouseUpEvent)
2471 swallowMouseUpEvent = handleMouseReleaseEvent(MouseEventWithHitTestResul ts(fakeMouseUp, currentHitTest));
2472
2473 bool swallowed = swallowMouseDownEvent | swallowMouseUpEvent | swallowClickE vent;
2474 if (!swallowed && tappedNode && m_frame->page()) {
2475 bool domTreeChanged = preDispatchDomTreeVersion != m_frame->document()-> domTreeVersion();
2476 bool styleChanged = preDispatchStyleVersion != m_frame->document()->styl eVersion();
2477
2478 IntPoint tappedPositionInViewport = m_frame->page()->frameHost().pinchVi ewport().rootFrameToViewport(tappedPosition);
2479 m_frame->chromeClient().showUnhandledTapUIIfNeeded(tappedPositionInViewp ort, tappedNode.get(), domTreeChanged || styleChanged);
2480 }
2481 return swallowed;
2482 }
2483
2484 bool EventHandler::handleGestureLongPress(const GestureEventWithHitTestResults& targetedEvent)
2485 {
2486 const PlatformGestureEvent& gestureEvent = targetedEvent.event();
2487 IntPoint adjustedPoint = gestureEvent.position();
2488
2489 unsigned modifiers = gestureEvent.modifiers();
2490
2491 // FIXME: Ideally we should try to remove the extra mouse-specific hit-tests here (re-using the
2492 // supplied HitTestResult), but that will require some overhaul of the touch drag-and-drop code
2493 // and LongPress is such a special scenario that it's unlikely to matter muc h in practice.
2494
2495 m_longTapShouldInvokeContextMenu = false;
2496 if (m_frame->settings() && m_frame->settings()->touchDragDropEnabled() && m_ frame->view()) {
2497 PlatformMouseEvent mouseDownEvent(adjustedPoint, gestureEvent.globalPosi tion(), LeftButton, PlatformEvent::MousePressed, 1,
2498 static_cast<PlatformEvent::Modifiers>(modifiers | PlatformEvent::Lef tButtonDown),
2499 PlatformMouseEvent::FromTouch, WTF::currentTime());
2500 m_mouseDown = mouseDownEvent;
2501
2502 PlatformMouseEvent mouseDragEvent(adjustedPoint, gestureEvent.globalPosi tion(), LeftButton, PlatformEvent::MouseMoved, 1,
2503 static_cast<PlatformEvent::Modifiers>(modifiers | PlatformEvent::Lef tButtonDown),
2504 PlatformMouseEvent::FromTouch, WTF::currentTime());
2505 HitTestRequest request(HitTestRequest::ReadOnly);
2506 MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseDragE vent);
2507 m_mouseDownMayStartDrag = true;
2508 dragState().m_dragSrc = nullptr;
2509 m_mouseDownPos = m_frame->view()->rootFrameToContents(mouseDragEvent.pos ition());
2510 RefPtrWillBeRawPtr<FrameView> protector(m_frame->view());
2511 if (handleDrag(mev, DragInitiator::Touch)) {
2512 m_longTapShouldInvokeContextMenu = true;
2513 return true;
2514 }
2515 }
2516 #if OS(ANDROID)
2517 bool shouldLongPressSelectWord = true;
2518 #else
2519 bool shouldLongPressSelectWord = m_frame->settings() && m_frame->settings()- >touchEditingEnabled();
2520 #endif
2521 if (shouldLongPressSelectWord) {
2522 IntPoint hitTestPoint = m_frame->view()->rootFrameToContents(gestureEven t.position());
2523 HitTestResult result = hitTestResultAtPoint(hitTestPoint);
2524 Node* innerNode = result.innerNode();
2525 if (!result.isLiveLink() && innerNode && (innerNode->isContentEditable() || innerNode->isTextNode()
2526 #if OS(ANDROID)
2527 || innerNode->canStartSelection()
2528 #endif
2529 )) {
2530 selectClosestWordFromHitTestResult(result, DontAppendTrailingWhitesp ace);
2531 if (m_frame->selection().isRange()) {
2532 focusDocumentView();
2533 return true;
2534 }
2535 }
2536 }
2537 return sendContextMenuEventForGesture(targetedEvent);
2538 }
2539
2540 bool EventHandler::handleGestureLongTap(const GestureEventWithHitTestResults& ta rgetedEvent)
2541 {
2542 #if !OS(ANDROID)
2543 if (m_longTapShouldInvokeContextMenu) {
2544 m_longTapShouldInvokeContextMenu = false;
2545 return sendContextMenuEventForGesture(targetedEvent);
2546 }
2547 #endif
2548 return false;
2549 }
2550
2551 bool EventHandler::handleScrollGestureOnResizer(Node* eventTarget, const Platfor mGestureEvent& gestureEvent)
2552 {
2553 if (gestureEvent.type() == PlatformEvent::GestureScrollBegin) {
2554 DeprecatedPaintLayer* layer = eventTarget->layoutObject() ? eventTarget- >layoutObject()->enclosingLayer() : nullptr;
2555 IntPoint p = m_frame->view()->rootFrameToContents(gestureEvent.position( ));
2556 if (layer && layer->scrollableArea() && layer->scrollableArea()->isPoint InResizeControl(p, ResizerForTouch)) {
2557 m_resizeScrollableArea = layer->scrollableArea();
2558 m_resizeScrollableArea->setInResizeMode(true);
2559 m_offsetFromResizeCorner = LayoutSize(m_resizeScrollableArea->offset FromResizeCorner(p));
2560 return true;
2561 }
2562 } else if (gestureEvent.type() == PlatformEvent::GestureScrollUpdate) {
2563 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode()) {
2564 m_resizeScrollableArea->resize(gestureEvent, m_offsetFromResizeCorne r);
2565 return true;
2566 }
2567 } else if (gestureEvent.type() == PlatformEvent::GestureScrollEnd) {
2568 if (m_resizeScrollableArea && m_resizeScrollableArea->inResizeMode()) {
2569 m_resizeScrollableArea->setInResizeMode(false);
2570 m_resizeScrollableArea = nullptr;
2571 return false;
2572 }
2573 }
2574
2575 return false;
2576 }
2577
2578 bool EventHandler::passScrollGestureEventToWidget(const PlatformGestureEvent& ge stureEvent, LayoutObject* layoutObject)
2579 {
2580 ASSERT(gestureEvent.isScrollEvent());
2581
2582 if (!m_lastGestureScrollOverWidget)
2583 return false;
2584
2585 if (!layoutObject || !layoutObject->isLayoutPart())
2586 return false;
2587
2588 Widget* widget = toLayoutPart(layoutObject)->widget();
2589
2590 if (!widget || !widget->isFrameView())
2591 return false;
2592
2593 return toFrameView(widget)->frame().eventHandler().handleGestureScrollEvent( gestureEvent);
2594 }
2595
2596 bool EventHandler::handleGestureScrollEnd(const PlatformGestureEvent& gestureEve nt)
2597 {
2598 RefPtrWillBeRawPtr<Node> node = m_scrollGestureHandlingNode;
2599
2600 if (node) {
2601 passScrollGestureEventToWidget(gestureEvent, node->layoutObject());
2602 if (RuntimeEnabledFeatures::scrollCustomizationEnabled()) {
2603 RefPtrWillBeRawPtr<ScrollState> scrollState = ScrollState::create(
2604 0, 0, 0, 0, 0, gestureEvent.inertial(), /* isBeginning */
2605 false, /* isEnding */ true, /* fromUserInput */ true);
2606 customizedScroll(*node.get(), *scrollState);
2607 }
2608 }
2609
2610 clearGestureScrollNodes();
2611 return false;
2612 }
2613
2614 bool EventHandler::handleGestureScrollBegin(const PlatformGestureEvent& gestureE vent)
2615 {
2616 Document* document = m_frame->document();
2617 if (!document->layoutView())
2618 return false;
2619
2620 FrameView* view = m_frame->view();
2621 if (!view)
2622 return false;
2623
2624 // If there's no layoutObject on the node, send the event to the nearest anc estor with a layoutObject.
2625 // Needed for <option> and <optgroup> elements so we can touch scroll <selec t>s
2626 while (m_scrollGestureHandlingNode && !m_scrollGestureHandlingNode->layoutOb ject())
2627 m_scrollGestureHandlingNode = m_scrollGestureHandlingNode->parentOrShado wHostNode();
2628
2629 if (!m_scrollGestureHandlingNode) {
2630 if (RuntimeEnabledFeatures::scrollCustomizationEnabled())
2631 m_scrollGestureHandlingNode = m_frame->document()->documentElement() ;
2632 else
2633 return false;
2634 }
2635 ASSERT(m_scrollGestureHandlingNode);
2636
2637 passScrollGestureEventToWidget(gestureEvent, m_scrollGestureHandlingNode->la youtObject());
2638 if (RuntimeEnabledFeatures::scrollCustomizationEnabled()) {
2639 m_currentScrollChain.clear();
2640 RefPtrWillBeRawPtr<ScrollState> scrollState = ScrollState::create(
2641 0, 0, 0, 0, 0, /* inInertialPhase */ false, /* isBeginning */
2642 true, /* isEnding */ false, /* fromUserInput */ true);
2643 customizedScroll(*m_scrollGestureHandlingNode.get(), *scrollState);
2644 } else {
2645 if (m_frame->isMainFrame())
2646 m_frame->host()->topControls().scrollBegin();
2647 }
2648 return true;
2649 }
2650
2651 bool EventHandler::handleGestureScrollUpdate(const PlatformGestureEvent& gesture Event)
2652 {
2653 ASSERT(gestureEvent.type() == PlatformEvent::GestureScrollUpdate);
2654
2655 FloatSize delta(gestureEvent.deltaX(), gestureEvent.deltaY());
2656 if (delta.isZero())
2657 return false;
2658
2659 Node* node = m_scrollGestureHandlingNode.get();
2660 if (node) {
2661 LayoutObject* layoutObject = node->layoutObject();
2662 if (!layoutObject)
2663 return false;
2664
2665 RefPtrWillBeRawPtr<FrameView> protector(m_frame->view());
2666
2667 Node* stopNode = nullptr;
2668
2669 // Try to send the event to the correct view.
2670 if (passScrollGestureEventToWidget(gestureEvent, layoutObject)) {
2671 if (gestureEvent.preventPropagation()
2672 && !RuntimeEnabledFeatures::scrollCustomizationEnabled()) {
2673 // This is an optimization which doesn't apply with
2674 // scroll customization enabled.
2675 m_previousGestureScrolledNode = m_scrollGestureHandlingNode;
2676 }
2677 // FIXME: we should allow simultaneous scrolling of nested
2678 // iframes along perpendicular axes. See crbug.com/466991.
2679 m_deltaConsumedForScrollSequence = true;
2680 return true;
2681 }
2682
2683 bool scrolled = false;
2684 if (RuntimeEnabledFeatures::scrollCustomizationEnabled()) {
2685 RefPtrWillBeRawPtr<ScrollState> scrollState = ScrollState::create(
2686 gestureEvent.deltaX(), gestureEvent.deltaY(),
2687 0, gestureEvent.velocityX(), gestureEvent.velocityY(),
2688 gestureEvent.inertial(), /* isBeginning */
2689 false, /* isEnding */ false, /* fromUserInput */ true,
2690 !gestureEvent.preventPropagation(), m_deltaConsumedForScrollSequ ence);
2691 if (m_previousGestureScrolledNode) {
2692 // The ScrollState needs to know what the current
2693 // native scrolling element is, so that for an
2694 // inertial scroll that shouldn't propagate, only the
2695 // currently scrolling element responds.
2696 ASSERT(m_previousGestureScrolledNode->isElementNode());
2697 scrollState->setCurrentNativeScrollingElement(toElement(m_previo usGestureScrolledNode.get()));
2698 }
2699 customizedScroll(*node, *scrollState);
2700 m_previousGestureScrolledNode = scrollState->currentNativeScrollingE lement();
2701 m_deltaConsumedForScrollSequence = scrollState->deltaConsumedForScro llSequence();
2702 scrolled = scrollState->deltaX() != gestureEvent.deltaX()
2703 || scrollState->deltaY() != gestureEvent.deltaY();
2704 } else {
2705 if (gestureEvent.preventPropagation())
2706 stopNode = m_previousGestureScrolledNode.get();
2707
2708 // First try to scroll the closest scrollable LayoutBox ancestor of |node|.
2709 ScrollGranularity granularity = ScrollByPrecisePixel;
2710 bool horizontalScroll = scroll(ScrollLeftIgnoringWritingMode, granul arity, node, &stopNode, delta.width());
2711 if (!gestureEvent.preventPropagation())
2712 stopNode = nullptr;
2713 bool verticalScroll = scroll(ScrollUpIgnoringWritingMode, granularit y, node, &stopNode, delta.height());
2714 scrolled = horizontalScroll || verticalScroll;
2715
2716 if (gestureEvent.preventPropagation())
2717 m_previousGestureScrolledNode = stopNode;
2718 }
2719 if (scrolled) {
2720 setFrameWasScrolledByUser();
2721 return true;
2722 }
2723 }
2724
2725 if (RuntimeEnabledFeatures::scrollCustomizationEnabled())
2726 return false;
2727
2728 // Try to scroll the frame view.
2729 if (m_frame->applyScrollDelta(delta, false)) {
2730 setFrameWasScrolledByUser();
2731 return true;
2732 }
2733
2734 return false;
2735 }
2736
2737 void EventHandler::clearGestureScrollNodes()
2738 {
2739 m_scrollGestureHandlingNode = nullptr;
2740 m_previousGestureScrolledNode = nullptr;
2741 m_deltaConsumedForScrollSequence = false;
2742 m_currentScrollChain.clear();
2743 }
2744
2745 bool EventHandler::isScrollbarHandlingGestures() const
2746 {
2747 return m_scrollbarHandlingScrollGesture.get();
2748 }
2749
2750 bool EventHandler::shouldApplyTouchAdjustment(const PlatformGestureEvent& event) const
2751 {
2752 if (m_frame->settings() && !m_frame->settings()->touchAdjustmentEnabled())
2753 return false;
2754 return !event.area().isEmpty();
2755 }
2756
2757 bool EventHandler::bestClickableNodeForHitTestResult(const HitTestResult& result , IntPoint& targetPoint, Node*& targetNode)
2758 {
2759 // FIXME: Unify this with the other best* functions which are very similar.
2760
2761 TRACE_EVENT0("input", "EventHandler::bestClickableNodeForHitTestResult");
2762 ASSERT(result.isRectBasedTest());
2763
2764 // If the touch is over a scrollbar, don't adjust the touch point since touc h adjustment only takes into account
2765 // DOM nodes so a touch over a scrollbar will be adjusted towards nearby nod es. This leads to things like textarea
2766 // scrollbars being untouchable.
2767 if (result.scrollbar()) {
2768 targetNode = 0;
2769 return false;
2770 }
2771
2772 IntPoint touchCenter = m_frame->view()->contentsToRootFrame(result.roundedPo intInMainFrame());
2773 IntRect touchRect = m_frame->view()->contentsToRootFrame(result.hitTestLocat ion().boundingBox());
2774
2775 WillBeHeapVector<RefPtrWillBeMember<Node>, 11> nodes;
2776 copyToVector(result.listBasedTestResult(), nodes);
2777
2778 // FIXME: the explicit Vector conversion copies into a temporary and is wast eful.
2779 return findBestClickableCandidate(targetNode, targetPoint, touchCenter, touc hRect, WillBeHeapVector<RefPtrWillBeMember<Node>> (nodes));
2780 }
2781
2782 bool EventHandler::bestContextMenuNodeForHitTestResult(const HitTestResult& resu lt, IntPoint& targetPoint, Node*& targetNode)
2783 {
2784 ASSERT(result.isRectBasedTest());
2785 IntPoint touchCenter = m_frame->view()->contentsToRootFrame(result.roundedPo intInMainFrame());
2786 IntRect touchRect = m_frame->view()->contentsToRootFrame(result.hitTestLocat ion().boundingBox());
2787 WillBeHeapVector<RefPtrWillBeMember<Node>, 11> nodes;
2788 copyToVector(result.listBasedTestResult(), nodes);
2789
2790 // FIXME: the explicit Vector conversion copies into a temporary and is wast eful.
2791 return findBestContextMenuCandidate(targetNode, targetPoint, touchCenter, to uchRect, WillBeHeapVector<RefPtrWillBeMember<Node>>(nodes));
2792 }
2793
2794 bool EventHandler::bestZoomableAreaForTouchPoint(const IntPoint& touchCenter, co nst IntSize& touchRadius, IntRect& targetArea, Node*& targetNode)
2795 {
2796 if (touchRadius.isEmpty())
2797 return false;
2798
2799 IntPoint hitTestPoint = m_frame->view()->rootFrameToContents(touchCenter);
2800
2801 HitTestRequest::HitTestRequestType hitType = HitTestRequest::ReadOnly | HitT estRequest::Active | HitTestRequest::ListBased;
2802 HitTestResult result = hitTestResultAtPoint(hitTestPoint, hitType, LayoutSiz e(touchRadius));
2803
2804 IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius);
2805 WillBeHeapVector<RefPtrWillBeMember<Node>, 11> nodes;
2806 copyToVector(result.listBasedTestResult(), nodes);
2807
2808 // FIXME: the explicit Vector conversion copies into a temporary and is wast eful.
2809 return findBestZoomableArea(targetNode, targetArea, touchCenter, touchRect, WillBeHeapVector<RefPtrWillBeMember<Node>>(nodes));
2810 }
2811
2812 // Update the hover and active state across all frames for this gesture.
2813 // This logic is different than the mouse case because mice send MouseLeave even ts to frames as they're exited.
2814 // With gestures, a single event conceptually both 'leaves' whatever frame curre ntly had hover and enters a new frame
2815 void EventHandler::updateGestureHoverActiveState(const HitTestRequest& request, Element* innerElement)
2816 {
2817 ASSERT(m_frame == m_frame->localFrameRoot());
2818
2819 WillBeHeapVector<LocalFrame*> newHoverFrameChain;
2820 LocalFrame* newHoverFrameInDocument = innerElement ? innerElement->document( ).frame() : nullptr;
2821 // Insert the ancestors of the frame having the new hovered node to the fram e chain
2822 // The frame chain doesn't include the main frame to avoid the redundant wor k that cleans the hover state.
2823 // Because the hover state for the main frame is updated by calling Document ::updateHoverActiveState
2824 while (newHoverFrameInDocument && newHoverFrameInDocument != m_frame) {
2825 newHoverFrameChain.append(newHoverFrameInDocument);
2826 Frame* parentFrame = newHoverFrameInDocument->tree().parent();
2827 newHoverFrameInDocument = parentFrame && parentFrame->isLocalFrame() ? t oLocalFrame(parentFrame) : nullptr;
2828 }
2829
2830 RefPtrWillBeRawPtr<Node> oldHoverNodeInCurDoc = m_frame->document()->hoverNo de();
2831 RefPtrWillBeRawPtr<Node> newInnermostHoverNode = innerElement;
2832
2833 if (newInnermostHoverNode != oldHoverNodeInCurDoc) {
2834 size_t indexFrameChain = newHoverFrameChain.size();
2835
2836 // Clear the hover state on any frames which are no longer in the frame chain of the hovered elemen
2837 while (oldHoverNodeInCurDoc && oldHoverNodeInCurDoc->isFrameOwnerElement ()) {
2838 LocalFrame* newHoverFrame = nullptr;
2839 // If we can't get the frame from the new hover frame chain,
2840 // the newHoverFrame will be null and the old hover state will be cl eared.
2841 if (indexFrameChain > 0)
2842 newHoverFrame = newHoverFrameChain[--indexFrameChain];
2843
2844 HTMLFrameOwnerElement* owner = toHTMLFrameOwnerElement(oldHoverNodeI nCurDoc.get());
2845 if (!owner->contentFrame() || !owner->contentFrame()->isLocalFrame() )
2846 break;
2847
2848 LocalFrame* oldHoverFrame = toLocalFrame(owner->contentFrame());
2849 Document* doc = oldHoverFrame->document();
2850 if (!doc)
2851 break;
2852
2853 oldHoverNodeInCurDoc = doc->hoverNode();
2854 // If the old hovered frame is different from the new hovered frame.
2855 // we should clear the old hovered node from the old hovered frame.
2856 if (newHoverFrame != oldHoverFrame)
2857 doc->updateHoverActiveState(request, nullptr);
2858 }
2859 }
2860
2861 // Recursively set the new active/hover states on every frame in the chain o f innerElement.
2862 m_frame->document()->updateHoverActiveState(request, innerElement);
2863 }
2864
2865 GestureEventWithHitTestResults EventHandler::targetGestureEvent(const PlatformGe stureEvent& gestureEvent, bool readOnly)
2866 {
2867 TRACE_EVENT0("input", "EventHandler::targetGestureEvent");
2868
2869 ASSERT(m_frame == m_frame->localFrameRoot());
2870 // Scrolling events get hit tested per frame (like wheel events do).
2871 ASSERT(!gestureEvent.isScrollEvent());
2872
2873 HitTestRequest::HitTestRequestType hitType = getHitTypeForGestureType(gestur eEvent.type());
2874 double activeInterval = 0;
2875 bool shouldKeepActiveForMinInterval = false;
2876 if (readOnly) {
2877 hitType |= HitTestRequest::ReadOnly;
2878 } else if (gestureEvent.type() == PlatformEvent::GestureTap) {
2879 // If the Tap is received very shortly after ShowPress, we want to
2880 // delay clearing of the active state so that it's visible to the user
2881 // for at least a couple of frames.
2882 activeInterval = WTF::currentTime() - m_lastShowPressTimestamp;
2883 shouldKeepActiveForMinInterval = m_lastShowPressTimestamp && activeInter val < minimumActiveInterval;
2884 if (shouldKeepActiveForMinInterval)
2885 hitType |= HitTestRequest::ReadOnly;
2886 }
2887
2888 GestureEventWithHitTestResults eventWithHitTestResults = hitTestResultForGes tureEvent(gestureEvent, hitType);
2889 // Now apply hover/active state to the final target.
2890 HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent);
2891 if (!request.readOnly())
2892 updateGestureHoverActiveState(request, eventWithHitTestResults.hitTestRe sult().innerElement());
2893
2894 if (shouldKeepActiveForMinInterval) {
2895 m_lastDeferredTapElement = eventWithHitTestResults.hitTestResult().inner Element();
2896 m_activeIntervalTimer.startOneShot(minimumActiveInterval - activeInterva l, FROM_HERE);
2897 }
2898
2899 return eventWithHitTestResults;
2900 }
2901
2902 GestureEventWithHitTestResults EventHandler::hitTestResultForGestureEvent(const PlatformGestureEvent& gestureEvent, HitTestRequest::HitTestRequestType hitType)
2903 {
2904 // Perform the rect-based hit-test (or point-based if adjustment is disabled ). Note that
2905 // we don't yet apply hover/active state here because we need to resolve tou ch adjustment
2906 // first so that we apply hover/active it to the final adjusted node.
2907 IntPoint hitTestPoint = m_frame->view()->rootFrameToContents(gestureEvent.po sition());
2908 LayoutSize padding;
2909 if (shouldApplyTouchAdjustment(gestureEvent)) {
2910 padding = LayoutSize(gestureEvent.area());
2911 if (!padding.isEmpty()) {
2912 padding.scale(1.f / 2);
2913 hitType |= HitTestRequest::ListBased;
2914 }
2915 }
2916 HitTestResult hitTestResult = hitTestResultAtPoint(hitTestPoint, hitType | H itTestRequest::ReadOnly, padding);
2917
2918 // Adjust the location of the gesture to the most likely nearby node, as app ropriate for the
2919 // type of event.
2920 PlatformGestureEvent adjustedEvent = gestureEvent;
2921 applyTouchAdjustment(&adjustedEvent, &hitTestResult);
2922
2923 // Do a new hit-test at the (adjusted) gesture co-ordinates. This is necessa ry because
2924 // rect-based hit testing and touch adjustment sometimes return a different node than
2925 // what a point-based hit test would return for the same point.
2926 // FIXME: Fix touch adjustment to avoid the need for a redundant hit test. h ttp://crbug.com/398914
2927 if (shouldApplyTouchAdjustment(gestureEvent)) {
2928 LocalFrame* hitFrame = hitTestResult.innerNodeFrame();
2929 if (!hitFrame)
2930 hitFrame = m_frame;
2931 hitTestResult = hitTestResultInFrame(hitFrame, hitFrame->view()->rootFra meToContents(adjustedEvent.position()), (hitType | HitTestRequest::ReadOnly) & ~ HitTestRequest::ListBased);
2932 }
2933
2934 // If we did a rect-based hit test it must be resolved to the best single no de by now to
2935 // ensure consumers don't accidentally use one of the other candidates.
2936 ASSERT(!hitTestResult.isRectBasedTest());
2937
2938 return GestureEventWithHitTestResults(adjustedEvent, hitTestResult);
2939 }
2940
2941 HitTestRequest::HitTestRequestType EventHandler::getHitTypeForGestureType(Platfo rmEvent::Type type)
2942 {
2943 HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent;
2944 switch (type) {
2945 case PlatformEvent::GestureShowPress:
2946 case PlatformEvent::GestureTapUnconfirmed:
2947 return hitType | HitTestRequest::Active;
2948 case PlatformEvent::GestureTapDownCancel:
2949 // A TapDownCancel received when no element is active shouldn't really b e changing hover state.
2950 if (!m_frame->document()->activeHoverElement())
2951 hitType |= HitTestRequest::ReadOnly;
2952 return hitType | HitTestRequest::Release;
2953 case PlatformEvent::GestureTap:
2954 return hitType | HitTestRequest::Release;
2955 case PlatformEvent::GestureTapDown:
2956 case PlatformEvent::GestureLongPress:
2957 case PlatformEvent::GestureLongTap:
2958 case PlatformEvent::GestureTwoFingerTap:
2959 // FIXME: Shouldn't LongTap and TwoFingerTap clear the Active state?
2960 return hitType | HitTestRequest::Active | HitTestRequest::ReadOnly;
2961 default:
2962 ASSERT_NOT_REACHED();
2963 return hitType | HitTestRequest::Active | HitTestRequest::ReadOnly;
2964 }
2965 }
2966
2967 void EventHandler::applyTouchAdjustment(PlatformGestureEvent* gestureEvent, HitT estResult* hitTestResult)
2968 {
2969 if (!shouldApplyTouchAdjustment(*gestureEvent))
2970 return;
2971
2972 Node* adjustedNode = nullptr;
2973 IntPoint adjustedPoint = gestureEvent->position();
2974 bool adjusted = false;
2975 switch (gestureEvent->type()) {
2976 case PlatformEvent::GestureTap:
2977 case PlatformEvent::GestureTapUnconfirmed:
2978 case PlatformEvent::GestureTapDown:
2979 case PlatformEvent::GestureShowPress:
2980 adjusted = bestClickableNodeForHitTestResult(*hitTestResult, adjustedPoi nt, adjustedNode);
2981 break;
2982 case PlatformEvent::GestureLongPress:
2983 case PlatformEvent::GestureLongTap:
2984 case PlatformEvent::GestureTwoFingerTap:
2985 adjusted = bestContextMenuNodeForHitTestResult(*hitTestResult, adjustedP oint, adjustedNode);
2986 break;
2987 default:
2988 ASSERT_NOT_REACHED();
2989 }
2990
2991 // Update the hit-test result to be a point-based result instead of a rect-b ased result.
2992 // FIXME: We should do this even when no candidate matches the node filter. crbug.com/398914
2993 if (adjusted) {
2994 hitTestResult->resolveRectBasedTest(adjustedNode, m_frame->view()->rootF rameToContents(adjustedPoint));
2995 gestureEvent->applyTouchAdjustment(adjustedPoint);
2996 }
2997 }
2998
2999 bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event, Node* o verrideTargetNode)
3000 {
3001 FrameView* v = m_frame->view();
3002 if (!v)
3003 return false;
3004
3005 // Clear mouse press state to avoid initiating a drag while context menu is up.
3006 m_mousePressed = false;
3007 LayoutPoint positionInContents = v->rootFrameToContents(event.position());
3008 HitTestRequest request(HitTestRequest::Active);
3009 MouseEventWithHitTestResults mev = m_frame->document()->prepareMouseEvent(re quest, positionInContents, event);
3010
3011 if (!m_frame->selection().contains(positionInContents)
3012 && !mev.scrollbar()
3013 // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse.
3014 // If the selection is non-editable, we do word selection to make it eas ier to use the contextual menu items
3015 // available for text selections. But only if we're above text.
3016 && (m_frame->selection().isContentEditable() || (mev.innerNode() && mev. innerNode()->isTextNode()))) {
3017 m_mouseDownMayStartSelect = true; // context menu events are always allo wed to perform a selection
3018
3019 if (mev.hitTestResult().isMisspelled())
3020 selectClosestMisspellingFromMouseEvent(mev);
3021 else if (m_frame->editor().behavior().shouldSelectOnContextualMenuClick( ))
3022 selectClosestWordOrLinkFromMouseEvent(mev);
3023 }
3024
3025 Node* targetNode = overrideTargetNode ? overrideTargetNode : mev.innerNode() ;
3026 return !dispatchMouseEvent(EventTypeNames::contextmenu, targetNode, 0, event , false);
3027 }
3028
3029 bool EventHandler::sendContextMenuEventForKey(Element* overrideTargetElement)
3030 {
3031 FrameView* view = m_frame->view();
3032 if (!view)
3033 return false;
3034
3035 Document* doc = m_frame->document();
3036 if (!doc)
3037 return false;
3038
3039 // Clear mouse press state to avoid initiating a drag while context menu is up.
3040 m_mousePressed = false;
3041
3042 static const int kContextMenuMargin = 1;
3043
3044 #if OS(WIN)
3045 int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT);
3046 #else
3047 int rightAligned = 0;
3048 #endif
3049 IntPoint locationInRootFrame;
3050
3051 Element* focusedElement = overrideTargetElement ? overrideTargetElement : do c->focusedElement();
3052 FrameSelection& selection = m_frame->selection();
3053 Position start = selection.selection().start();
3054 PinchViewport& pinchViewport = m_frame->page()->frameHost().pinchViewport();
3055
3056 if (!overrideTargetElement && start.deprecatedNode() && (selection.rootEdita bleElement() || selection.isRange())) {
3057 RefPtrWillBeRawPtr<Range> selectionRange = selection.toNormalizedRange() ;
3058 IntRect firstRect = m_frame->editor().firstRectForRange(selectionRange.g et());
3059
3060 int x = rightAligned ? firstRect.maxX() : firstRect.x();
3061 // In a multiline edit, firstRect.maxY() would endup on the next line, s o -1.
3062 int y = firstRect.maxY() ? firstRect.maxY() - 1 : 0;
3063 locationInRootFrame = view->contentsToRootFrame(IntPoint(x, y));
3064 } else if (focusedElement) {
3065 IntRect clippedRect = focusedElement->boundsInViewportSpace();
3066 // FIXME: boundsInViewportSpace is actually in the weird scaled but untr anslated coordinate space of
3067 // the old-style pinch viewport. crbug.com/459591.
3068 locationInRootFrame = flooredIntPoint(pinchViewport.viewportCSSPixelsToR ootFrame(clippedRect.center()));
3069 } else {
3070 locationInRootFrame = IntPoint(
3071 rightAligned
3072 ? pinchViewport.visibleRect().maxX() - kContextMenuMargin
3073 : pinchViewport.location().x() + kContextMenuMargin,
3074 pinchViewport.location().y() + kContextMenuMargin);
3075 }
3076
3077 m_frame->view()->setCursor(pointerCursor());
3078 IntPoint locationInViewport = pinchViewport.rootFrameToViewport(locationInRo otFrame);
3079 IntPoint globalPosition = view->hostWindow()->viewportToScreen(IntRect(locat ionInViewport, IntSize())).location();
3080
3081 Node* targetNode = overrideTargetElement ? overrideTargetElement : doc->focu sedElement();
3082 if (!targetNode)
3083 targetNode = doc;
3084
3085 // Use the focused node as the target for hover and active.
3086 HitTestRequest request(HitTestRequest::Active);
3087 HitTestResult result(request, locationInRootFrame);
3088 result.setInnerNode(targetNode);
3089 doc->updateHoverActiveState(request, result.innerElement());
3090
3091 // The contextmenu event is a mouse event even when invoked using the keyboa rd.
3092 // This is required for web compatibility.
3093 PlatformEvent::Type eventType = PlatformEvent::MousePressed;
3094 if (m_frame->settings()->showContextMenuOnMouseUp())
3095 eventType = PlatformEvent::MouseReleased;
3096
3097 PlatformMouseEvent mouseEvent(locationInRootFrame, globalPosition, RightButt on, eventType, 1, false, false, false, false, PlatformMouseEvent::RealOrIndistin guishable, WTF::currentTime());
3098
3099 handleMousePressEvent(mouseEvent);
3100 return sendContextMenuEvent(mouseEvent, overrideTargetElement);
3101 }
3102
3103 bool EventHandler::sendContextMenuEventForGesture(const GestureEventWithHitTestR esults& targetedEvent)
3104 {
3105 const PlatformGestureEvent& gestureEvent = targetedEvent.event();
3106 unsigned modifiers = gestureEvent.modifiers();
3107
3108 // Send MouseMoved event prior to handling (https://crbug.com/485290).
3109 PlatformMouseEvent fakeMouseMove(gestureEvent.position(), gestureEvent.globa lPosition(),
3110 NoButton, PlatformEvent::MouseMoved, /* clickCount */ 0,
3111 static_cast<PlatformEvent::Modifiers>(modifiers),
3112 PlatformMouseEvent::FromTouch, gestureEvent.timestamp());
3113 dispatchMouseEvent(EventTypeNames::mousemove, targetedEvent.hitTestResult(). innerNode(), 0, fakeMouseMove, true);
3114
3115 PlatformEvent::Type eventType = PlatformEvent::MousePressed;
3116
3117 if (m_frame->settings()->showContextMenuOnMouseUp())
3118 eventType = PlatformEvent::MouseReleased;
3119 else
3120 modifiers |= PlatformEvent::RightButtonDown;
3121
3122 PlatformMouseEvent mouseEvent(targetedEvent.event().position(), targetedEven t.event().globalPosition(), RightButton, eventType, 1,
3123 static_cast<PlatformEvent::Modifiers>(modifiers),
3124 PlatformMouseEvent::FromTouch, WTF::currentTime());
3125 // To simulate right-click behavior, we send a right mouse down and then
3126 // context menu event.
3127 // FIXME: Send HitTestResults to avoid redundant hit tests.
3128 handleMousePressEvent(mouseEvent);
3129 return sendContextMenuEvent(mouseEvent);
3130 // We do not need to send a corresponding mouse release because in case of
3131 // right-click, the context menu takes capture and consumes all events.
3132 }
3133
3134 void EventHandler::scheduleHoverStateUpdate()
3135 {
3136 if (!m_hoverTimer.isActive())
3137 m_hoverTimer.startOneShot(0, FROM_HERE);
3138 }
3139
3140 void EventHandler::scheduleCursorUpdate()
3141 {
3142 // We only want one timer for the page, rather than each frame having it's o wn timer
3143 // competing which eachother (since there's only one mouse cursor).
3144 ASSERT(m_frame == m_frame->localFrameRoot());
3145
3146 if (!m_cursorUpdateTimer.isActive())
3147 m_cursorUpdateTimer.startOneShot(cursorUpdateInterval, FROM_HERE);
3148 }
3149
3150 bool EventHandler::cursorUpdatePending()
3151 {
3152 return m_cursorUpdateTimer.isActive();
3153 }
3154
3155 void EventHandler::dispatchFakeMouseMoveEventSoon()
3156 {
3157 if (m_mousePressed)
3158 return;
3159
3160 if (m_mousePositionIsUnknown)
3161 return;
3162
3163 Settings* settings = m_frame->settings();
3164 if (settings && !settings->deviceSupportsMouse())
3165 return;
3166
3167 // If the content has ever taken longer than fakeMouseMoveShortInterval we
3168 // reschedule the timer and use a longer time. This will cause the content
3169 // to receive these moves only after the user is done scrolling, reducing
3170 // pauses during the scroll.
3171 if (m_maxMouseMovedDuration > fakeMouseMoveShortInterval) {
3172 if (m_fakeMouseMoveEventTimer.isActive())
3173 m_fakeMouseMoveEventTimer.stop();
3174 m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveLongInterval, FROM_H ERE);
3175 } else {
3176 if (!m_fakeMouseMoveEventTimer.isActive())
3177 m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveShortInterval, F ROM_HERE);
3178 }
3179 }
3180
3181 void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad)
3182 {
3183 FrameView* view = m_frame->view();
3184 if (!view)
3185 return;
3186
3187 if (!quad.containsPoint(view->rootFrameToContents(m_lastKnownMousePosition)) )
3188 return;
3189
3190 dispatchFakeMouseMoveEventSoon();
3191 }
3192
3193 void EventHandler::fakeMouseMoveEventTimerFired(Timer<EventHandler>* timer)
3194 {
3195 TRACE_EVENT0("input", "EventHandler::fakeMouseMoveEventTimerFired");
3196 ASSERT_UNUSED(timer, timer == &m_fakeMouseMoveEventTimer);
3197 ASSERT(!m_mousePressed);
3198
3199 Settings* settings = m_frame->settings();
3200 if (settings && !settings->deviceSupportsMouse())
3201 return;
3202
3203 FrameView* view = m_frame->view();
3204 if (!view)
3205 return;
3206
3207 if (!m_frame->page() || !m_frame->page()->focusController().isActive())
3208 return;
3209
3210 // Don't dispatch a synthetic mouse move event if the mouse cursor is not vi sible to the user.
3211 if (!isCursorVisible())
3212 return;
3213
3214 bool shiftKey;
3215 bool ctrlKey;
3216 bool altKey;
3217 bool metaKey;
3218 PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, me taKey);
3219 PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownM ouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, a ltKey, metaKey, PlatformMouseEvent::RealOrIndistinguishable, currentTime());
3220 handleMouseMoveEvent(fakeMouseMoveEvent);
3221 }
3222
3223 void EventHandler::cancelFakeMouseMoveEvent()
3224 {
3225 m_fakeMouseMoveEventTimer.stop();
3226 }
3227
3228 bool EventHandler::isCursorVisible() const
3229 {
3230 return m_frame->page()->isCursorVisible();
3231 }
3232
3233 void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet)
3234 {
3235 m_frameSetBeingResized = frameSet;
3236 }
3237
3238 void EventHandler::resizeScrollableAreaDestroyed()
3239 {
3240 ASSERT(m_resizeScrollableArea);
3241 m_resizeScrollableArea = nullptr;
3242 }
3243
3244 void EventHandler::hoverTimerFired(Timer<EventHandler>*)
3245 {
3246 TRACE_EVENT0("input", "EventHandler::hoverTimerFired");
3247 m_hoverTimer.stop();
3248
3249 ASSERT(m_frame);
3250 ASSERT(m_frame->document());
3251
3252 if (LayoutView* layoutObject = m_frame->contentLayoutObject()) {
3253 if (FrameView* view = m_frame->view()) {
3254 HitTestRequest request(HitTestRequest::Move);
3255 HitTestResult result(request, view->rootFrameToContents(m_lastKnownM ousePosition));
3256 layoutObject->hitTest(result);
3257 m_frame->document()->updateHoverActiveState(request, result.innerEle ment());
3258 }
3259 }
3260 }
3261
3262 void EventHandler::activeIntervalTimerFired(Timer<EventHandler>*)
3263 {
3264 TRACE_EVENT0("input", "EventHandler::activeIntervalTimerFired");
3265 m_activeIntervalTimer.stop();
3266
3267 if (m_frame
3268 && m_frame->document()
3269 && m_lastDeferredTapElement) {
3270 // FIXME: Enable condition when http://crbug.com/226842 lands
3271 // m_lastDeferredTapElement.get() == m_frame->document()->activeElement( )
3272 HitTestRequest request(HitTestRequest::TouchEvent | HitTestRequest::Rele ase);
3273 m_frame->document()->updateHoverActiveState(request, m_lastDeferredTapEl ement.get());
3274 }
3275 m_lastDeferredTapElement = nullptr;
3276 }
3277
3278 void EventHandler::notifyElementActivated()
3279 {
3280 // Since another element has been set to active, stop current timer and clea r reference.
3281 if (m_activeIntervalTimer.isActive())
3282 m_activeIntervalTimer.stop();
3283 m_lastDeferredTapElement = nullptr;
3284 }
3285
3286 bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& evt)
3287 {
3288 // FIXME: Ignoring the state of Shift key is what neither IE nor Firefox do.
3289 // IE matches lower and upper case access keys regardless of Shift key state - but if both upper and
3290 // lower case variants are present in a document, the correct element is mat ched based on Shift key state.
3291 // Firefox only matches an access key if Shift is not pressed, and does that case-insensitively.
3292 ASSERT(!(accessKeyModifiers() & PlatformEvent::ShiftKey));
3293 if ((evt.modifiers() & ~PlatformEvent::ShiftKey) != accessKeyModifiers())
3294 return false;
3295 String key = evt.unmodifiedText();
3296 Element* elem = m_frame->document()->getElementByAccessKey(key.lower());
3297 if (!elem)
3298 return false;
3299 elem->accessKeyAction(false);
3300 return true;
3301 }
3302
3303 bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent)
3304 {
3305 RefPtrWillBeRawPtr<FrameView> protector(m_frame->view());
3306
3307 if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL)
3308 capsLockStateMayHaveChanged();
3309
3310 #if OS(WIN)
3311 if (panScrollInProgress()) {
3312 // If a key is pressed while the panScroll is in progress then we want t o stop
3313 if (initialKeyEvent.type() == PlatformEvent::KeyDown || initialKeyEvent. type() == PlatformEvent::RawKeyDown)
3314 stopAutoscroll();
3315
3316 // If we were in panscroll mode, we swallow the key event
3317 return true;
3318 }
3319 #endif
3320
3321 // Check for cases where we are too early for events -- possible unmatched k ey up
3322 // from pressing return in the location bar.
3323 RefPtrWillBeRawPtr<Node> node = eventTargetNodeForDocument(m_frame->document ());
3324 if (!node)
3325 return false;
3326
3327 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
3328
3329 // In IE, access keys are special, they are handled after default keydown pr ocessing, but cannot be canceled - this is hard to match.
3330 // On Mac OS X, we process them before dispatching keydown, as the default k eydown handler implements Emacs key bindings, which may conflict
3331 // with access keys. Then we dispatch keydown, but suppress its default hand ling.
3332 // On Windows, WebKit explicitly calls handleAccessKey() instead of dispatch ing a keypress event for WM_SYSCHAR messages.
3333 // Other platforms currently match either Mac or Windows behavior, depending on whether they send combined KeyDown events.
3334 bool matchedAnAccessKey = false;
3335 if (initialKeyEvent.type() == PlatformEvent::KeyDown)
3336 matchedAnAccessKey = handleAccessKey(initialKeyEvent);
3337
3338 // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch.
3339 if (initialKeyEvent.type() == PlatformEvent::KeyUp || initialKeyEvent.type() == PlatformEvent::Char)
3340 return !node->dispatchKeyEvent(initialKeyEvent);
3341
3342 PlatformKeyboardEvent keyDownEvent = initialKeyEvent;
3343 if (keyDownEvent.type() != PlatformEvent::RawKeyDown)
3344 keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown);
3345 RefPtrWillBeRawPtr<KeyboardEvent> keydown = KeyboardEvent::create(keyDownEve nt, m_frame->document()->domWindow());
3346 if (matchedAnAccessKey)
3347 keydown->setDefaultPrevented(true);
3348 keydown->setTarget(node);
3349
3350 if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) {
3351 node->dispatchEvent(keydown, IGNORE_EXCEPTION);
3352 // If frame changed as a result of keydown dispatch, then return true to avoid sending a subsequent keypress message to the new frame.
3353 bool changedFocusedFrame = m_frame->page() && m_frame != m_frame->page() ->focusController().focusedOrMainFrame();
3354 return keydown->defaultHandled() || keydown->defaultPrevented() || chang edFocusedFrame;
3355 }
3356
3357 node->dispatchEvent(keydown, IGNORE_EXCEPTION);
3358 // If frame changed as a result of keydown dispatch, then return early to av oid sending a subsequent keypress message to the new frame.
3359 bool changedFocusedFrame = m_frame->page() && m_frame != m_frame->page()->fo cusController().focusedOrMainFrame();
3360 bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented( ) || changedFocusedFrame;
3361 if (keydownResult)
3362 return keydownResult;
3363
3364 // Focus may have changed during keydown handling, so refetch node.
3365 // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original node.
3366 node = eventTargetNodeForDocument(m_frame->document());
3367 if (!node)
3368 return false;
3369
3370 PlatformKeyboardEvent keyPressEvent = initialKeyEvent;
3371 keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Char);
3372 if (keyPressEvent.text().isEmpty())
3373 return keydownResult;
3374 RefPtrWillBeRawPtr<KeyboardEvent> keypress = KeyboardEvent::create(keyPressE vent, m_frame->document()->domWindow());
3375 keypress->setTarget(node);
3376 if (keydownResult)
3377 keypress->setDefaultPrevented(true);
3378 node->dispatchEvent(keypress, IGNORE_EXCEPTION);
3379
3380 return keydownResult || keypress->defaultPrevented() || keypress->defaultHan dled();
3381 }
3382
3383 static WebFocusType focusDirectionForKey(const AtomicString& keyIdentifier)
3384 {
3385 DEFINE_STATIC_LOCAL(AtomicString, Down, ("Down", AtomicString::ConstructFrom Literal));
3386 DEFINE_STATIC_LOCAL(AtomicString, Up, ("Up", AtomicString::ConstructFromLite ral));
3387 DEFINE_STATIC_LOCAL(AtomicString, Left, ("Left", AtomicString::ConstructFrom Literal));
3388 DEFINE_STATIC_LOCAL(AtomicString, Right, ("Right", AtomicString::ConstructFr omLiteral));
3389
3390 WebFocusType retVal = WebFocusTypeNone;
3391
3392 if (keyIdentifier == Down)
3393 retVal = WebFocusTypeDown;
3394 else if (keyIdentifier == Up)
3395 retVal = WebFocusTypeUp;
3396 else if (keyIdentifier == Left)
3397 retVal = WebFocusTypeLeft;
3398 else if (keyIdentifier == Right)
3399 retVal = WebFocusTypeRight;
3400
3401 return retVal;
3402 }
3403
3404 void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event)
3405 {
3406 if (event->type() == EventTypeNames::keydown) {
3407 // Clear caret blinking suspended state to make sure that caret blinks
3408 // when we type again after long pressing on an empty input field.
3409 if (m_frame && m_frame->selection().isCaretBlinkingSuspended())
3410 m_frame->selection().setCaretBlinkingSuspended(false);
3411
3412 m_frame->editor().handleKeyboardEvent(event);
3413 if (event->defaultHandled())
3414 return;
3415 if (event->keyIdentifier() == "U+0009") {
3416 defaultTabEventHandler(event);
3417 } else if (event->keyIdentifier() == "U+0008") {
3418 defaultBackspaceEventHandler(event);
3419 } else if (event->keyIdentifier() == "U+001B") {
3420 defaultEscapeEventHandler(event);
3421 } else {
3422 WebFocusType type = focusDirectionForKey(AtomicString(event->keyIden tifier()));
3423 if (type != WebFocusTypeNone)
3424 defaultArrowEventHandler(type, event);
3425 }
3426 }
3427 if (event->type() == EventTypeNames::keypress) {
3428 m_frame->editor().handleKeyboardEvent(event);
3429 if (event->defaultHandled())
3430 return;
3431 if (event->charCode() == ' ')
3432 defaultSpaceEventHandler(event);
3433 }
3434 }
3435
3436 bool EventHandler::dragHysteresisExceeded(const IntPoint& dragLocationInRootFram e) const
3437 {
3438 FrameView* view = m_frame->view();
3439 if (!view)
3440 return false;
3441 IntPoint dragLocation = view->rootFrameToContents(dragLocationInRootFrame);
3442 IntSize delta = dragLocation - m_mouseDownPos;
3443
3444 int threshold = GeneralDragHysteresis;
3445 switch (dragState().m_dragType) {
3446 case DragSourceActionSelection:
3447 threshold = TextDragHysteresis;
3448 break;
3449 case DragSourceActionImage:
3450 threshold = ImageDragHysteresis;
3451 break;
3452 case DragSourceActionLink:
3453 threshold = LinkDragHysteresis;
3454 break;
3455 case DragSourceActionDHTML:
3456 break;
3457 case DragSourceActionNone:
3458 ASSERT_NOT_REACHED();
3459 }
3460
3461 return abs(delta.width()) >= threshold || abs(delta.height()) >= threshold;
3462 }
3463
3464 void EventHandler::clearDragDataTransfer()
3465 {
3466 if (dragState().m_dragDataTransfer) {
3467 dragState().m_dragDataTransfer->clearDragImage();
3468 dragState().m_dragDataTransfer->setAccessPolicy(DataTransferNumb);
3469 }
3470 }
3471
3472 void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperat ion operation)
3473 {
3474 // Send a hit test request so that Layer gets a chance to update the :hover and :active pseudoclasses.
3475 HitTestRequest request(HitTestRequest::Release);
3476 prepareMouseEvent(request, event);
3477
3478 if (dragState().m_dragSrc) {
3479 dragState().m_dragDataTransfer->setDestinationOperation(operation);
3480 // for now we don't care if event handler cancels default behavior, sinc e there is none
3481 dispatchDragSrcEvent(EventTypeNames::dragend, event);
3482 }
3483 clearDragDataTransfer();
3484 dragState().m_dragSrc = nullptr;
3485 // In case the drag was ended due to an escape key press we need to ensure
3486 // that consecutive mousemove events don't reinitiate the drag and drop.
3487 m_mouseDownMayStartDrag = false;
3488 }
3489
3490 void EventHandler::updateDragStateAfterEditDragIfNeeded(Element* rootEditableEle ment)
3491 {
3492 // If inserting the dragged contents removed the drag source, we still want to fire dragend at the root editble element.
3493 if (dragState().m_dragSrc && !dragState().m_dragSrc->inDocument())
3494 dragState().m_dragSrc = rootEditableElement;
3495 }
3496
3497 // returns if we should continue "default processing", i.e., whether eventhandle r canceled
3498 bool EventHandler::dispatchDragSrcEvent(const AtomicString& eventType, const Pla tformMouseEvent& event)
3499 {
3500 return !dispatchDragEvent(eventType, dragState().m_dragSrc.get(), event, dra gState().m_dragDataTransfer.get());
3501 }
3502
3503 bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, DragIni tiator initiator)
3504 {
3505 ASSERT(event.event().type() == PlatformEvent::MouseMoved);
3506 // Callers must protect the reference to FrameView, since this function may dispatch DOM
3507 // events, causing page/FrameView to go away.
3508 ASSERT(m_frame);
3509 ASSERT(m_frame->view());
3510 if (!m_frame->page())
3511 return false;
3512
3513 if (m_mouseDownMayStartDrag) {
3514 HitTestRequest request(HitTestRequest::ReadOnly);
3515 HitTestResult result(request, m_mouseDownPos);
3516 m_frame->contentLayoutObject()->hitTest(result);
3517 Node* node = result.innerNode();
3518 if (node) {
3519 DragController::SelectionDragPolicy selectionDragPolicy = event.even t().timestamp() - m_mouseDownTimestamp < TextDragDelay
3520 ? DragController::DelayedSelectionDragResolution
3521 : DragController::ImmediateSelectionDragResolution;
3522 dragState().m_dragSrc = m_frame->page()->dragController().draggableN ode(m_frame, node, m_mouseDownPos, selectionDragPolicy, dragState().m_dragType);
3523 } else {
3524 dragState().m_dragSrc = nullptr;
3525 }
3526
3527 if (!dragState().m_dragSrc)
3528 m_mouseDownMayStartDrag = false; // no element is draggable
3529 }
3530
3531 if (!m_mouseDownMayStartDrag)
3532 return initiator == DragInitiator::Mouse && !mouseDownMayStartSelect() & & !m_mouseDownMayStartAutoscroll;
3533
3534 // We are starting a text/image/url drag, so the cursor should be an arrow
3535 // FIXME <rdar://7577595>: Custom cursors aren't supported during drag and d rop (default to pointer).
3536 m_frame->view()->setCursor(pointerCursor());
3537
3538 if (initiator == DragInitiator::Mouse && !dragHysteresisExceeded(event.event ().position()))
3539 return true;
3540
3541 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
3542 invalidateClick();
3543
3544 if (!tryStartDrag(event)) {
3545 // Something failed to start the drag, clean up.
3546 clearDragDataTransfer();
3547 dragState().m_dragSrc = nullptr;
3548 }
3549
3550 m_mouseDownMayStartDrag = false;
3551 // Whether or not the drag actually started, no more default handling (like selection).
3552 return true;
3553 }
3554
3555 bool EventHandler::tryStartDrag(const MouseEventWithHitTestResults& event)
3556 {
3557 // The DataTransfer would only be non-empty if we missed a dragEnd.
3558 // Clear it anyway, just to make sure it gets numbified.
3559 clearDragDataTransfer();
3560
3561 dragState().m_dragDataTransfer = createDraggingDataTransfer();
3562
3563 // Check to see if this a DOM based drag, if it is get the DOM specified dra g
3564 // image and offset
3565 if (dragState().m_dragType == DragSourceActionDHTML) {
3566 if (LayoutObject* layoutObject = dragState().m_dragSrc->layoutObject()) {
3567 FloatPoint absPos = layoutObject->localToAbsolute(FloatPoint(), UseT ransforms);
3568 IntSize delta = m_mouseDownPos - roundedIntPoint(absPos);
3569 dragState().m_dragDataTransfer->setDragImageElement(dragState().m_dr agSrc.get(), IntPoint(delta));
3570 } else {
3571 // The layoutObject has disappeared, this can happen if the onStartD rag handler has hidden
3572 // the element in some way. In this case we just kill the drag.
3573 return false;
3574 }
3575 }
3576
3577 DragController& dragController = m_frame->page()->dragController();
3578 if (!dragController.populateDragDataTransfer(m_frame, dragState(), m_mouseDo wnPos))
3579 return false;
3580
3581 // If dispatching dragstart brings about another mouse down -- one way
3582 // this will happen is if a DevTools user breaks within a dragstart
3583 // handler and then clicks on the suspended page -- the drag state is
3584 // reset. Hence, need to check if this particular drag operation can
3585 // continue even if dispatchEvent() indicates no (direct) cancellation.
3586 // Do that by checking if m_dragSrc is still set.
3587 m_mouseDownMayStartDrag = dispatchDragSrcEvent(EventTypeNames::dragstart, m_ mouseDown)
3588 && !m_frame->selection().isInPasswordField() && dragState().m_dragSrc;
3589
3590 // Invalidate clipboard here against anymore pasteboard writing for security . The drag
3591 // image can still be changed as we drag, but not the pasteboard data.
3592 dragState().m_dragDataTransfer->setAccessPolicy(DataTransferImageWritable);
3593
3594 if (m_mouseDownMayStartDrag) {
3595 // Dispatching the event could cause Page to go away. Make sure it's sti ll valid before trying to use DragController.
3596 if (m_frame->page() && dragController.startDrag(m_frame, dragState(), ev ent.event(), m_mouseDownPos))
3597 return true;
3598 // Drag was canned at the last minute - we owe m_dragSrc a DRAGEND event
3599 dispatchDragSrcEvent(EventTypeNames::dragend, event.event());
3600 }
3601
3602 return false;
3603 }
3604
3605 bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEve nt, TextEventInputType inputType)
3606 {
3607 // Platforms should differentiate real commands like selectAll from text inp ut in disguise (like insertNewline),
3608 // and avoid dispatching text input events from keydown default handlers.
3609 ASSERT(!underlyingEvent || !underlyingEvent->isKeyboardEvent() || toKeyboard Event(underlyingEvent)->type() == EventTypeNames::keypress);
3610
3611 if (!m_frame)
3612 return false;
3613
3614 EventTarget* target;
3615 if (underlyingEvent)
3616 target = underlyingEvent->target();
3617 else
3618 target = eventTargetNodeForDocument(m_frame->document());
3619 if (!target)
3620 return false;
3621
3622 RefPtrWillBeRawPtr<TextEvent> event = TextEvent::create(m_frame->domWindow() , text, inputType);
3623 event->setUnderlyingEvent(underlyingEvent);
3624
3625 target->dispatchEvent(event, IGNORE_EXCEPTION);
3626 return event->defaultHandled();
3627 }
3628
3629 void EventHandler::defaultTextInputEventHandler(TextEvent* event)
3630 {
3631 if (m_frame->editor().handleTextEvent(event))
3632 event->setDefaultHandled();
3633 }
3634
3635 void EventHandler::defaultSpaceEventHandler(KeyboardEvent* event)
3636 {
3637 ASSERT(event->type() == EventTypeNames::keypress);
3638
3639 if (event->ctrlKey() || event->metaKey() || event->altKey())
3640 return;
3641
3642 ScrollDirection direction = event->shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward;
3643
3644 // FIXME: enable scroll customization in this case. See crbug.com/410974.
3645 if (scroll(direction, ScrollByPage)) {
3646 event->setDefaultHandled();
3647 return;
3648 }
3649
3650 FrameView* view = m_frame->view();
3651 if (!view)
3652 return;
3653
3654 ScrollDirectionPhysical physicalDirection =
3655 toPhysicalDirection(direction, view->isVerticalDocument(), view->isFlipp edDocument());
3656
3657 if (view->scrollableArea()->userScroll(physicalDirection, ScrollByPage))
3658 event->setDefaultHandled();
3659 }
3660
3661 void EventHandler::defaultBackspaceEventHandler(KeyboardEvent* event)
3662 {
3663 ASSERT(event->type() == EventTypeNames::keydown);
3664
3665 if (event->ctrlKey() || event->metaKey() || event->altKey())
3666 return;
3667
3668 if (!m_frame->editor().behavior().shouldNavigateBackOnBackspace())
3669 return;
3670 bool handledEvent = m_frame->loader().client()->navigateBackForward(event->s hiftKey() ? 1 : -1);
3671 if (handledEvent)
3672 event->setDefaultHandled();
3673 }
3674
3675 void EventHandler::defaultArrowEventHandler(WebFocusType focusType, KeyboardEven t* event)
3676 {
3677 ASSERT(event->type() == EventTypeNames::keydown);
3678
3679 if (event->ctrlKey() || event->metaKey() || event->shiftKey())
3680 return;
3681
3682 Page* page = m_frame->page();
3683 if (!page)
3684 return;
3685
3686 if (!isSpatialNavigationEnabled(m_frame))
3687 return;
3688
3689 // Arrows and other possible directional navigation keys can be used in desi gn
3690 // mode editing.
3691 if (m_frame->document()->inDesignMode())
3692 return;
3693
3694 if (page->focusController().advanceFocus(focusType))
3695 event->setDefaultHandled();
3696 }
3697
3698 void EventHandler::defaultTabEventHandler(KeyboardEvent* event)
3699 {
3700 ASSERT(event->type() == EventTypeNames::keydown);
3701
3702 // We should only advance focus on tabs if no special modifier keys are held down.
3703 if (event->ctrlKey() || event->metaKey())
3704 return;
3705
3706 #if !OS(MACOSX)
3707 // Option-Tab is a shortcut based on a system-wide preference on Mac but
3708 // should be ignored on all other platforms.
3709 if (event->altKey())
3710 return;
3711 #endif
3712
3713 Page* page = m_frame->page();
3714 if (!page)
3715 return;
3716 if (!page->tabKeyCyclesThroughElements())
3717 return;
3718
3719 WebFocusType focusType = event->shiftKey() ? WebFocusTypeBackward : WebFocus TypeForward;
3720
3721 // Tabs can be used in design mode editing.
3722 if (m_frame->document()->inDesignMode())
3723 return;
3724
3725 if (page->focusController().advanceFocus(focusType))
3726 event->setDefaultHandled();
3727 }
3728
3729 void EventHandler::defaultEscapeEventHandler(KeyboardEvent* event)
3730 {
3731 if (HTMLDialogElement* dialog = m_frame->document()->activeModalDialog())
3732 dialog->dispatchEvent(Event::createCancelable(EventTypeNames::cancel));
3733 }
3734
3735 void EventHandler::capsLockStateMayHaveChanged()
3736 {
3737 if (Element* element = m_frame->document()->focusedElement()) {
3738 if (LayoutObject* r = element->layoutObject()) {
3739 if (r->isTextField())
3740 toLayoutTextControlSingleLine(r)->capsLockStateMayHaveChanged();
3741 }
3742 }
3743 }
3744
3745 void EventHandler::setFrameWasScrolledByUser()
3746 {
3747 if (FrameView* view = m_frame->view())
3748 view->setWasScrolledByUser(true);
3749 }
3750
3751 bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mev)
3752 {
3753 Scrollbar* scrollbar = mev.scrollbar();
3754 updateLastScrollbarUnderMouse(scrollbar, true);
3755
3756 if (!scrollbar || !scrollbar->enabled())
3757 return false;
3758 setFrameWasScrolledByUser();
3759 scrollbar->mouseDown(mev.event());
3760 return true;
3761 }
3762
3763 // If scrollbar (under mouse) is different from last, send a mouse exited. Set
3764 // last to scrollbar if setLast is true; else set last to 0.
3765 void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, bool setL ast)
3766 {
3767 if (m_lastScrollbarUnderMouse != scrollbar) {
3768 // Send mouse exited to the old scrollbar.
3769 if (m_lastScrollbarUnderMouse)
3770 m_lastScrollbarUnderMouse->mouseExited();
3771
3772 // Send mouse entered if we're setting a new scrollbar.
3773 if (scrollbar && setLast)
3774 scrollbar->mouseEntered();
3775
3776 m_lastScrollbarUnderMouse = setLast ? scrollbar : nullptr;
3777 }
3778 }
3779
3780 static const AtomicString& eventNameForTouchPointState(PlatformTouchPoint::State state)
3781 {
3782 switch (state) {
3783 case PlatformTouchPoint::TouchReleased:
3784 return EventTypeNames::touchend;
3785 case PlatformTouchPoint::TouchCancelled:
3786 return EventTypeNames::touchcancel;
3787 case PlatformTouchPoint::TouchPressed:
3788 return EventTypeNames::touchstart;
3789 case PlatformTouchPoint::TouchMoved:
3790 return EventTypeNames::touchmove;
3791 case PlatformTouchPoint::TouchStationary:
3792 // TouchStationary state is not converted to touch events, so fall throu gh to assert.
3793 default:
3794 ASSERT_NOT_REACHED();
3795 return emptyAtom;
3796 }
3797 }
3798
3799 HitTestResult EventHandler::hitTestResultInFrame(LocalFrame* frame, const Layout Point& point, HitTestRequest::HitTestRequestType hitType)
3800 {
3801 HitTestResult result(HitTestRequest(hitType), point);
3802
3803 if (!frame || !frame->contentLayoutObject())
3804 return result;
3805 if (frame->view()) {
3806 IntRect rect = frame->view()->visibleContentRect(IncludeScrollbars);
3807 if (!rect.contains(roundedIntPoint(point)))
3808 return result;
3809 }
3810 frame->contentLayoutObject()->hitTest(result);
3811 return result;
3812 }
3813
3814 bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
3815 {
3816 TRACE_EVENT0("blink", "EventHandler::handleTouchEvent");
3817
3818 const Vector<PlatformTouchPoint>& points = event.touchPoints();
3819
3820 unsigned i;
3821 bool freshTouchEvents = true;
3822 bool allTouchReleased = true;
3823 for (i = 0; i < points.size(); ++i) {
3824 const PlatformTouchPoint& point = points[i];
3825 if (point.state() != PlatformTouchPoint::TouchPressed)
3826 freshTouchEvents = false;
3827 if (point.state() != PlatformTouchPoint::TouchReleased && point.state() != PlatformTouchPoint::TouchCancelled)
3828 allTouchReleased = false;
3829 }
3830 if (freshTouchEvents) {
3831 // Ideally we'd ASSERT !m_touchSequenceDocument here since we should
3832 // have cleared the active document when we saw the last release. But we
3833 // have some tests that violate this, ClusterFuzz could trigger it, and
3834 // there may be cases where the browser doesn't reliably release all
3835 // touches. http://crbug.com/345372 tracks this.
3836 m_touchSequenceDocument.clear();
3837 m_touchSequenceUserGestureToken.clear();
3838 }
3839
3840 OwnPtr<UserGestureIndicator> gestureIndicator;
3841
3842 if (m_touchSequenceUserGestureToken)
3843 gestureIndicator = adoptPtr(new UserGestureIndicator(m_touchSequenceUser GestureToken.release()));
3844 else
3845 gestureIndicator = adoptPtr(new UserGestureIndicator(DefinitelyProcessin gUserGesture));
3846
3847 m_touchSequenceUserGestureToken = gestureIndicator->currentToken();
3848
3849 ASSERT(m_frame->view());
3850 if (m_touchSequenceDocument && (!m_touchSequenceDocument->frame() || !m_touc hSequenceDocument->frame()->view())) {
3851 // If the active touch document has no frame or view, it's probably bein g destroyed
3852 // so we can't dispatch events.
3853 return false;
3854 }
3855
3856 // First do hit tests for any new touch points.
3857 for (i = 0; i < points.size(); ++i) {
3858 const PlatformTouchPoint& point = points[i];
3859
3860 // Touch events implicitly capture to the touched node, and don't change
3861 // active/hover states themselves (Gesture events do). So we only need
3862 // to hit-test on touchstart, and it can be read-only.
3863 if (point.state() == PlatformTouchPoint::TouchPressed) {
3864 HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEv ent | HitTestRequest::ReadOnly | HitTestRequest::Active;
3865 LayoutPoint pagePoint = roundedLayoutPoint(m_frame->view()->rootFram eToContents(point.pos()));
3866 HitTestResult result;
3867 if (!m_touchSequenceDocument) {
3868 result = hitTestResultAtPoint(pagePoint, hitType);
3869 } else if (m_touchSequenceDocument->frame()) {
3870 LayoutPoint framePoint = roundedLayoutPoint(m_touchSequenceDocum ent->frame()->view()->rootFrameToContents(point.pos()));
3871 result = hitTestResultInFrame(m_touchSequenceDocument->frame(), framePoint, hitType);
3872 } else {
3873 continue;
3874 }
3875
3876 Node* node = result.innerNode();
3877 if (!node)
3878 continue;
3879
3880 // Touch events should not go to text nodes
3881 if (node->isTextNode())
3882 node = ComposedTreeTraversal::parent(*node);
3883
3884 if (!m_touchSequenceDocument) {
3885 // Keep track of which document should receive all touch events
3886 // in the active sequence. This must be a single document to
3887 // ensure we don't leak Nodes between documents.
3888 m_touchSequenceDocument = &(result.innerNode()->document());
3889 ASSERT(m_touchSequenceDocument->frame()->view());
3890 }
3891
3892 // Ideally we'd ASSERT(!m_targetForTouchID.contains(point.id())
3893 // since we shouldn't get a touchstart for a touch that's already
3894 // down. However EventSender allows this to be violated and there's
3895 // some tests that take advantage of it. There may also be edge
3896 // cases in the browser where this happens.
3897 // See http://crbug.com/345372.
3898 m_targetForTouchID.set(point.id(), node);
3899
3900 TouchAction effectiveTouchAction = computeEffectiveTouchAction(*node );
3901 if (effectiveTouchAction != TouchActionAuto)
3902 m_frame->page()->chromeClient().setTouchAction(effectiveTouchAct ion);
3903 }
3904 }
3905
3906 m_touchPressed = !allTouchReleased;
3907
3908 // If there's no document receiving touch events, or no handlers on the
3909 // document set to receive the events, then we can skip all the rest of
3910 // this work.
3911 if (!m_touchSequenceDocument || !m_touchSequenceDocument->frameHost() || !m_ touchSequenceDocument->frameHost()->eventHandlerRegistry().hasEventHandlers(Even tHandlerRegistry::TouchEvent) || !m_touchSequenceDocument->frame()) {
3912 if (allTouchReleased) {
3913 m_touchSequenceDocument.clear();
3914 m_touchSequenceUserGestureToken.clear();
3915 }
3916 return false;
3917 }
3918
3919 // Build up the lists to use for the 'touches', 'targetTouches' and
3920 // 'changedTouches' attributes in the JS event. See
3921 // http://www.w3.org/TR/touch-events/#touchevent-interface for how these
3922 // lists fit together.
3923
3924 // Holds the complete set of touches on the screen.
3925 RefPtrWillBeRawPtr<TouchList> touches = TouchList::create();
3926
3927 // A different view on the 'touches' list above, filtered and grouped by
3928 // event target. Used for the 'targetTouches' list in the JS event.
3929 using TargetTouchesHeapMap = WillBeHeapHashMap<EventTarget*, RefPtrWillBeMem ber<TouchList>>;
3930 TargetTouchesHeapMap touchesByTarget;
3931
3932 // Array of touches per state, used to assemble the 'changedTouches' list.
3933 using EventTargetSet = WillBeHeapHashSet<RefPtrWillBeMember<EventTarget>>;
3934 struct {
3935 // The touches corresponding to the particular change state this struct
3936 // instance represents.
3937 RefPtrWillBeMember<TouchList> m_touches;
3938 // Set of targets involved in m_touches.
3939 EventTargetSet m_targets;
3940 } changedTouches[PlatformTouchPoint::TouchStateEnd];
3941
3942 for (i = 0; i < points.size(); ++i) {
3943 const PlatformTouchPoint& point = points[i];
3944 PlatformTouchPoint::State pointState = point.state();
3945 RefPtrWillBeRawPtr<EventTarget> touchTarget = nullptr;
3946
3947 if (pointState == PlatformTouchPoint::TouchReleased || pointState == Pla tformTouchPoint::TouchCancelled) {
3948 // The target should be the original target for this touch, so get
3949 // it from the hashmap. As it's a release or cancel we also remove
3950 // it from the map.
3951 touchTarget = m_targetForTouchID.take(point.id());
3952 } else {
3953 // No hittest is performed on move or stationary, since the target
3954 // is not allowed to change anyway.
3955 touchTarget = m_targetForTouchID.get(point.id());
3956 }
3957
3958 LocalFrame* targetFrame = nullptr;
3959 bool knownTarget = false;
3960 if (touchTarget) {
3961 Document& doc = touchTarget->toNode()->document();
3962 // If the target node has moved to a new document while it was being touched,
3963 // we can't send events to the new document because that could leak nodes
3964 // from one document to another. See http://crbug.com/394339.
3965 if (&doc == m_touchSequenceDocument.get()) {
3966 targetFrame = doc.frame();
3967 knownTarget = true;
3968 }
3969 }
3970 if (!knownTarget) {
3971 // If we don't have a target registered for the point it means we've
3972 // missed our opportunity to do a hit test for it (due to some
3973 // optimization that prevented blink from ever seeing the
3974 // touchstart), or that the touch started outside the active touch
3975 // sequence document. We should still include the touch in the
3976 // Touches list reported to the application (eg. so it can
3977 // differentiate between a one and two finger gesture), but we won't
3978 // actually dispatch any events for it. Set the target to the
3979 // Document so that there's some valid node here. Perhaps this
3980 // should really be LocalDOMWindow, but in all other cases the targe t of
3981 // a Touch is a Node so using the window could be a breaking change.
3982 // Since we know there was no handler invoked, the specific target
3983 // should be completely irrelevant to the application.
3984 touchTarget = m_touchSequenceDocument;
3985 targetFrame = m_touchSequenceDocument->frame();
3986 }
3987 ASSERT(targetFrame);
3988
3989 // pagePoint should always be in the target element's document coordinat es.
3990 FloatPoint pagePoint = targetFrame->view()->rootFrameToContents(point.po s());
3991
3992 float scaleFactor = 1.0f / targetFrame->pageZoomFactor();
3993
3994 FloatPoint adjustedPagePoint = pagePoint.scaledBy(scaleFactor);
3995 FloatSize adjustedRadius = point.radius().scaledBy(scaleFactor);
3996
3997 RefPtrWillBeRawPtr<Touch> touch = Touch::create(
3998 targetFrame, touchTarget.get(), point.id(), point.screenPos(), adjus tedPagePoint, adjustedRadius, point.rotationAngle(), point.force());
3999
4000 // Ensure this target's touch list exists, even if it ends up empty, so
4001 // it can always be passed to TouchEvent::Create below.
4002 TargetTouchesHeapMap::iterator targetTouchesIterator = touchesByTarget.f ind(touchTarget.get());
4003 if (targetTouchesIterator == touchesByTarget.end()) {
4004 touchesByTarget.set(touchTarget.get(), TouchList::create());
4005 targetTouchesIterator = touchesByTarget.find(touchTarget.get());
4006 }
4007
4008 // touches and targetTouches should only contain information about
4009 // touches still on the screen, so if this point is released or
4010 // cancelled it will only appear in the changedTouches list.
4011 if (pointState != PlatformTouchPoint::TouchReleased && pointState != Pla tformTouchPoint::TouchCancelled) {
4012 touches->append(touch);
4013 targetTouchesIterator->value->append(touch);
4014 }
4015
4016 // Now build up the correct list for changedTouches.
4017 // Note that any touches that are in the TouchStationary state (e.g. if
4018 // the user had several points touched but did not move them all) should
4019 // never be in the changedTouches list so we do not handle them
4020 // explicitly here. See https://bugs.webkit.org/show_bug.cgi?id=37609
4021 // for further discussion about the TouchStationary state.
4022 if (pointState != PlatformTouchPoint::TouchStationary && knownTarget) {
4023 ASSERT(pointState < PlatformTouchPoint::TouchStateEnd);
4024 if (!changedTouches[pointState].m_touches)
4025 changedTouches[pointState].m_touches = TouchList::create();
4026 changedTouches[pointState].m_touches->append(touch);
4027 changedTouches[pointState].m_targets.add(touchTarget);
4028 }
4029 }
4030 if (allTouchReleased) {
4031 m_touchSequenceDocument.clear();
4032 m_touchSequenceUserGestureToken.clear();
4033 }
4034
4035 // Now iterate the changedTouches list and m_targets within it, sending
4036 // events to the targets as required.
4037 bool swallowedEvent = false;
4038 for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; ++state ) {
4039 if (!changedTouches[state].m_touches)
4040 continue;
4041
4042 const AtomicString& stateName(eventNameForTouchPointState(static_cast<Pl atformTouchPoint::State>(state)));
4043 const EventTargetSet& targetsForState = changedTouches[state].m_targets;
4044 for (const RefPtrWillBeMember<EventTarget>& eventTarget : targetsForStat e) {
4045 EventTarget* touchEventTarget = eventTarget.get();
4046 RefPtrWillBeRawPtr<TouchEvent> touchEvent = TouchEvent::create(
4047 touches.get(), touchesByTarget.get(touchEventTarget), changedTou ches[state].m_touches.get(),
4048 stateName, touchEventTarget->toNode()->document().domWindow(),
4049 event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey (), event.cancelable(), event.causesScrollingIfUncanceled(), event.timestamp());
4050 touchEventTarget->toNode()->dispatchTouchEvent(touchEvent.get());
4051 swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled();
4052 }
4053 }
4054
4055 return swallowedEvent;
4056 }
4057
4058 TouchAction EventHandler::intersectTouchAction(TouchAction action1, TouchAction action2)
4059 {
4060 if (action1 == TouchActionNone || action2 == TouchActionNone)
4061 return TouchActionNone;
4062 if (action1 == TouchActionAuto)
4063 return action2;
4064 if (action2 == TouchActionAuto)
4065 return action1;
4066 if (!(action1 & action2))
4067 return TouchActionNone;
4068 return action1 & action2;
4069 }
4070
4071 // touch-action applies to all elements with both width AND height properties.
4072 // According to the CSS Box Model Spec (http://dev.w3.org/csswg/css-box/#the-wid th-and-height-properties)
4073 // width applies to all elements but non-replaced inline elements, table rows, a nd row groups and
4074 // height applies to all elements but non-replaced inline elements, table column s, and column groups.
4075 static inline bool supportsTouchAction(const LayoutObject& object)
4076 {
4077 if (object.isInline() && !object.isReplaced())
4078 return false;
4079 if (object.isTableRow() || object.isLayoutTableCol())
4080 return false;
4081
4082 return true;
4083 }
4084
4085 TouchAction EventHandler::computeEffectiveTouchAction(const Node& node)
4086 {
4087 // Start by permitting all actions, then walk the elements supporting
4088 // touch-action from the target node up to the nearest scrollable ancestor
4089 // and exclude any prohibited actions.
4090 TouchAction effectiveTouchAction = TouchActionAuto;
4091 for (const Node* curNode = &node; curNode; curNode = ComposedTreeTraversal:: parent(*curNode)) {
4092 if (LayoutObject* layoutObject = curNode->layoutObject()) {
4093 if (supportsTouchAction(*layoutObject)) {
4094 TouchAction action = layoutObject->style()->touchAction();
4095 effectiveTouchAction = intersectTouchAction(action, effectiveTou chAction);
4096 if (effectiveTouchAction == TouchActionNone)
4097 break;
4098 }
4099
4100 // If we've reached an ancestor that supports a touch action, search no further.
4101 if (layoutObject->isBox() && toLayoutBox(layoutObject)->scrollsOverf low())
4102 break;
4103 }
4104 }
4105 return effectiveTouchAction;
4106 }
4107
4108 void EventHandler::setLastKnownMousePosition(const PlatformMouseEvent& event)
4109 {
4110 m_mousePositionIsUnknown = false;
4111 m_lastKnownMousePosition = event.position();
4112 m_lastKnownMouseGlobalPosition = event.globalPosition();
4113 }
4114
4115 bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& m ev, LocalFrame* subframe)
4116 {
4117 // If we're clicking into a frame that is selected, the frame will appear
4118 // greyed out even though we're clicking on the selection. This looks
4119 // really strange (having the whole frame be greyed out), so we deselect the
4120 // selection.
4121 IntPoint p = m_frame->view()->rootFrameToContents(mev.event().position());
4122 if (m_frame->selection().contains(p)) {
4123 VisiblePosition visiblePos(
4124 mev.innerNode()->layoutObject()->positionForPoint(mev.localPoint())) ;
4125 VisibleSelection newSelection(visiblePos);
4126 m_frame->selection().setSelection(newSelection);
4127 }
4128
4129 subframe->eventHandler().handleMousePressEvent(mev.event());
4130 return true;
4131 }
4132
4133 bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& me v, LocalFrame* subframe, HitTestResult* hoveredNode)
4134 {
4135 if (m_mouseDownMayStartDrag)
4136 return false;
4137 subframe->eventHandler().handleMouseMoveOrLeaveEvent(mev.event(), hoveredNod e);
4138 return true;
4139 }
4140
4141 bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, LocalFrame* subframe)
4142 {
4143 subframe->eventHandler().handleMouseReleaseEvent(mev.event());
4144 return true;
4145 }
4146
4147 bool EventHandler::passWheelEventToWidget(const PlatformWheelEvent& wheelEvent, Widget& widget)
4148 {
4149 // If not a FrameView, then probably a plugin widget. Those will receive
4150 // the event via an EventTargetNode dispatch when this returns false.
4151 if (!widget.isFrameView())
4152 return false;
4153
4154 return toFrameView(&widget)->frame().eventHandler().handleWheelEvent(wheelEv ent);
4155 }
4156
4157 DataTransfer* EventHandler::createDraggingDataTransfer() const
4158 {
4159 return DataTransfer::create(DataTransfer::DragAndDrop, DataTransferWritable, DataObject::create());
4160 }
4161
4162 void EventHandler::focusDocumentView()
4163 {
4164 Page* page = m_frame->page();
4165 if (!page)
4166 return;
4167 page->focusController().focusDocumentView(m_frame);
4168 }
4169
4170 unsigned EventHandler::accessKeyModifiers()
4171 {
4172 #if OS(MACOSX)
4173 return PlatformEvent::CtrlKey | PlatformEvent::AltKey;
4174 #else
4175 return PlatformEvent::AltKey;
4176 #endif
4177 }
4178
4179 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/page/EventHandler.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698