1 | /* |
2 | * Copyright (C) 2006-2017 Apple Inc. All rights reserved. |
3 | * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) |
4 | * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies) |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * |
15 | * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 "EventHandler.h" |
30 | |
31 | #include "AutoscrollController.h" |
32 | #include "BackForwardController.h" |
33 | #include "CachedImage.h" |
34 | #include "Chrome.h" |
35 | #include "ChromeClient.h" |
36 | #include "ComposedTreeAncestorIterator.h" |
37 | #include "CursorList.h" |
38 | #include "DocumentMarkerController.h" |
39 | #include "DragController.h" |
40 | #include "DragState.h" |
41 | #include "Editing.h" |
42 | #include "Editor.h" |
43 | #include "EditorClient.h" |
44 | #include "EventNames.h" |
45 | #include "FileList.h" |
46 | #include "FloatPoint.h" |
47 | #include "FloatRect.h" |
48 | #include "FocusController.h" |
49 | #include "Frame.h" |
50 | #include "FrameLoader.h" |
51 | #include "FrameSelection.h" |
52 | #include "FrameTree.h" |
53 | #include "FrameView.h" |
54 | #include "FullscreenManager.h" |
55 | #include "HTMLDocument.h" |
56 | #include "HTMLFrameElement.h" |
57 | #include "HTMLFrameSetElement.h" |
58 | #include "HTMLHtmlElement.h" |
59 | #include "HTMLIFrameElement.h" |
60 | #include "HTMLInputElement.h" |
61 | #include "HTMLNames.h" |
62 | #include "HitTestRequest.h" |
63 | #include "HitTestResult.h" |
64 | #include "Image.h" |
65 | #include "InspectorInstrumentation.h" |
66 | #include "KeyboardEvent.h" |
67 | #include "Logging.h" |
68 | #include "MouseEvent.h" |
69 | #include "MouseEventWithHitTestResults.h" |
70 | #include "NotImplemented.h" |
71 | #include "Page.h" |
72 | #include "PageOverlayController.h" |
73 | #include "Pasteboard.h" |
74 | #include "PlatformEvent.h" |
75 | #include "PlatformKeyboardEvent.h" |
76 | #include "PlatformWheelEvent.h" |
77 | #include "PluginDocument.h" |
78 | #include "Range.h" |
79 | #include "RenderFrameSet.h" |
80 | #include "RenderLayer.h" |
81 | #include "RenderListBox.h" |
82 | #include "RenderTextControlSingleLine.h" |
83 | #include "RenderView.h" |
84 | #include "RenderWidget.h" |
85 | #include "ResourceLoadObserver.h" |
86 | #include "RuntimeApplicationChecks.h" |
87 | #include "SVGDocument.h" |
88 | #include "SVGNames.h" |
89 | #include "ScrollLatchingState.h" |
90 | #include "Scrollbar.h" |
91 | #include "Settings.h" |
92 | #include "ShadowRoot.h" |
93 | #include "SpatialNavigation.h" |
94 | #include "StaticPasteboard.h" |
95 | #include "StyleCachedImage.h" |
96 | #include "TextEvent.h" |
97 | #include "TextIterator.h" |
98 | #include "UserGestureIndicator.h" |
99 | #include "UserTypingGestureIndicator.h" |
100 | #include "ValidationMessageClient.h" |
101 | #include "VisibleUnits.h" |
102 | #include "WheelEvent.h" |
103 | #include "WheelEventDeltaFilter.h" |
104 | #include "WindowsKeyboardCodes.h" |
105 | #include <wtf/Assertions.h> |
106 | #include <wtf/NeverDestroyed.h> |
107 | #include <wtf/StdLibExtras.h> |
108 | |
109 | #if ENABLE(IOS_TOUCH_EVENTS) |
110 | #include "PlatformTouchEventIOS.h" |
111 | #endif |
112 | |
113 | #if ENABLE(TOUCH_EVENTS) |
114 | #include "TouchEvent.h" |
115 | #include "TouchList.h" |
116 | #endif |
117 | |
118 | #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) |
119 | #include "PlatformTouchEvent.h" |
120 | #endif |
121 | |
122 | #if ENABLE(MAC_GESTURE_EVENTS) |
123 | #include "PlatformGestureEventMac.h" |
124 | #endif |
125 | |
126 | #if ENABLE(POINTER_LOCK) |
127 | #include "PointerLockController.h" |
128 | #endif |
129 | |
130 | namespace WebCore { |
131 | |
132 | using namespace HTMLNames; |
133 | |
134 | #if ENABLE(DRAG_SUPPORT) |
135 | // The link drag hysteresis is much larger than the others because there |
136 | // needs to be enough space to cancel the link press without starting a link drag, |
137 | // and because dragging links is rare. |
138 | const int LinkDragHysteresis = 40; |
139 | const int ImageDragHysteresis = 5; |
140 | const int TextDragHysteresis = 3; |
141 | const int ColorDragHystersis = 3; |
142 | const int GeneralDragHysteresis = 3; |
143 | #if PLATFORM(MAC) |
144 | const Seconds EventHandler::TextDragDelay { 150_ms }; |
145 | #else |
146 | const Seconds EventHandler::TextDragDelay { 0_s }; |
147 | #endif |
148 | #endif // ENABLE(DRAG_SUPPORT) |
149 | |
150 | #if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) |
151 | const float GestureUnknown = 0; |
152 | #endif |
153 | |
154 | #if ENABLE(IOS_TOUCH_EVENTS) |
155 | // FIXME: Share this constant with EventHandler and SliderThumbElement. |
156 | const unsigned InvalidTouchIdentifier = 0; |
157 | #endif |
158 | |
159 | // Match key code of composition keydown event on windows. |
160 | // IE sends VK_PROCESSKEY which has value 229; |
161 | const int CompositionEventKeyCode = 229; |
162 | |
163 | using namespace SVGNames; |
164 | |
165 | #if !ENABLE(IOS_TOUCH_EVENTS) |
166 | // The amount of time to wait before sending a fake mouse event, triggered |
167 | // during a scroll. The short interval is used if the content responds to the mouse events |
168 | // in fakeMouseMoveDurationThreshold or less, otherwise the long interval is used. |
169 | const double fakeMouseMoveDurationThreshold = 0.01; |
170 | const Seconds fakeMouseMoveShortInterval = { 100_ms }; |
171 | const Seconds fakeMouseMoveLongInterval = { 250_ms }; |
172 | #endif |
173 | |
174 | #if ENABLE(CURSOR_SUPPORT) |
175 | // The amount of time to wait for a cursor update on style and layout changes |
176 | // Set to 50Hz, no need to be faster than common screen refresh rate |
177 | static const Seconds cursorUpdateInterval { 20_ms }; |
178 | |
179 | const int maximumCursorSize = 128; |
180 | #endif |
181 | |
182 | #if ENABLE(MOUSE_CURSOR_SCALE) |
183 | // It's pretty unlikely that a scale of less than one would ever be used. But all we really |
184 | // need to ensure here is that the scale isn't so small that integer overflow can occur when |
185 | // dividing cursor sizes (limited above) by the scale. |
186 | const double minimumCursorScale = 0.001; |
187 | #endif |
188 | |
189 | class MaximumDurationTracker { |
190 | public: |
191 | explicit MaximumDurationTracker(double *maxDuration) |
192 | : m_maxDuration(maxDuration) |
193 | , m_start(MonotonicTime::now()) |
194 | { |
195 | } |
196 | |
197 | ~MaximumDurationTracker() |
198 | { |
199 | *m_maxDuration = std::max(*m_maxDuration, (MonotonicTime::now() - m_start).seconds()); |
200 | } |
201 | |
202 | private: |
203 | double* m_maxDuration; |
204 | MonotonicTime m_start; |
205 | }; |
206 | |
207 | #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) |
208 | class SyntheticTouchPoint : public PlatformTouchPoint { |
209 | public: |
210 | |
211 | // The default values are based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html |
212 | explicit SyntheticTouchPoint(const PlatformMouseEvent& event) |
213 | { |
214 | const static int idDefaultValue = 0; |
215 | const static int radiusYDefaultValue = 1; |
216 | const static int radiusXDefaultValue = 1; |
217 | const static float rotationAngleDefaultValue = 0.0f; |
218 | const static float forceDefaultValue = 1.0f; |
219 | |
220 | m_id = idDefaultValue; // There is only one active TouchPoint. |
221 | m_screenPos = event.globalPosition(); |
222 | m_pos = event.position(); |
223 | m_radiusY = radiusYDefaultValue; |
224 | m_radiusX = radiusXDefaultValue; |
225 | m_rotationAngle = rotationAngleDefaultValue; |
226 | m_force = forceDefaultValue; |
227 | |
228 | PlatformEvent::Type type = event.type(); |
229 | ASSERT(type == PlatformEvent::MouseMoved || type == PlatformEvent::MousePressed || type == PlatformEvent::MouseReleased); |
230 | |
231 | switch (type) { |
232 | case PlatformEvent::MouseMoved: |
233 | m_state = TouchMoved; |
234 | break; |
235 | case PlatformEvent::MousePressed: |
236 | m_state = TouchPressed; |
237 | break; |
238 | case PlatformEvent::MouseReleased: |
239 | m_state = TouchReleased; |
240 | break; |
241 | default: |
242 | ASSERT_NOT_REACHED(); |
243 | break; |
244 | } |
245 | } |
246 | }; |
247 | |
248 | class SyntheticSingleTouchEvent : public PlatformTouchEvent { |
249 | public: |
250 | explicit SyntheticSingleTouchEvent(const PlatformMouseEvent& event) |
251 | { |
252 | switch (event.type()) { |
253 | case PlatformEvent::MouseMoved: |
254 | m_type = TouchMove; |
255 | break; |
256 | case PlatformEvent::MousePressed: |
257 | m_type = TouchStart; |
258 | break; |
259 | case PlatformEvent::MouseReleased: |
260 | m_type = TouchEnd; |
261 | break; |
262 | default: |
263 | ASSERT_NOT_REACHED(); |
264 | m_type = NoType; |
265 | break; |
266 | } |
267 | m_timestamp = event.timestamp(); |
268 | m_modifiers = event.modifiers(); |
269 | m_touchPoints.append(SyntheticTouchPoint(event)); |
270 | } |
271 | }; |
272 | #endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) |
273 | |
274 | static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned deltaMode) |
275 | { |
276 | switch (deltaMode) { |
277 | case WheelEvent::DOM_DELTA_PAGE: |
278 | return ScrollByPage; |
279 | case WheelEvent::DOM_DELTA_LINE: |
280 | return ScrollByLine; |
281 | case WheelEvent::DOM_DELTA_PIXEL: |
282 | return ScrollByPixel; |
283 | default: |
284 | return ScrollByPixel; |
285 | } |
286 | } |
287 | |
288 | static inline bool didScrollInScrollableArea(ScrollableArea* scrollableArea, WheelEvent& wheelEvent) |
289 | { |
290 | ScrollGranularity scrollGranularity = wheelGranularityToScrollGranularity(wheelEvent.deltaMode()); |
291 | bool didHandleWheelEvent = false; |
292 | if (float absoluteDelta = std::abs(wheelEvent.deltaX())) |
293 | didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaX() > 0 ? ScrollRight : ScrollLeft, scrollGranularity, absoluteDelta); |
294 | |
295 | if (float absoluteDelta = std::abs(wheelEvent.deltaY())) |
296 | didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaY() > 0 ? ScrollDown : ScrollUp, scrollGranularity, absoluteDelta); |
297 | |
298 | return didHandleWheelEvent; |
299 | } |
300 | |
301 | static inline bool handleWheelEventInAppropriateEnclosingBox(Node* startNode, WheelEvent& wheelEvent, Element** stopElement, const FloatSize& filteredPlatformDelta, const FloatSize& filteredVelocity) |
302 | { |
303 | bool shouldHandleEvent = wheelEvent.deltaX() || wheelEvent.deltaY(); |
304 | #if PLATFORM(MAC) |
305 | shouldHandleEvent |= wheelEvent.phase() == PlatformWheelEventPhaseEnded; |
306 | #if ENABLE(CSS_SCROLL_SNAP) |
307 | shouldHandleEvent |= wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded; |
308 | #endif |
309 | #endif |
310 | if (!startNode->renderer() || !shouldHandleEvent) |
311 | return false; |
312 | |
313 | RenderBox& initialEnclosingBox = startNode->renderer()->enclosingBox(); |
314 | if (initialEnclosingBox.isListBox()) |
315 | return didScrollInScrollableArea(static_cast<RenderListBox*>(&initialEnclosingBox), wheelEvent); |
316 | |
317 | RenderBox* currentEnclosingBox = &initialEnclosingBox; |
318 | while (currentEnclosingBox) { |
319 | if (RenderLayer* boxLayer = currentEnclosingBox->layer()) { |
320 | auto platformEvent = wheelEvent.underlyingPlatformEvent(); |
321 | bool scrollingWasHandled; |
322 | if (platformEvent) { |
323 | auto copiedEvent = platformEvent->copyWithDeltasAndVelocity(filteredPlatformDelta.width(), filteredPlatformDelta.height(), filteredVelocity); |
324 | scrollingWasHandled = boxLayer->handleWheelEvent(copiedEvent); |
325 | } else |
326 | scrollingWasHandled = didScrollInScrollableArea(boxLayer, wheelEvent); |
327 | |
328 | if (scrollingWasHandled) { |
329 | if (stopElement) |
330 | *stopElement = currentEnclosingBox->element(); |
331 | return true; |
332 | } |
333 | } |
334 | |
335 | if (stopElement && *stopElement && *stopElement == currentEnclosingBox->element()) |
336 | return true; |
337 | |
338 | currentEnclosingBox = currentEnclosingBox->containingBlock(); |
339 | if (!currentEnclosingBox || currentEnclosingBox->isRenderView()) |
340 | return false; |
341 | } |
342 | return false; |
343 | } |
344 | |
345 | #if (ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS_FAMILY)) |
346 | static inline bool shouldGesturesTriggerActive() |
347 | { |
348 | // If the platform we're on supports GestureTapDown and GestureTapCancel then we'll |
349 | // rely on them to set the active state. Unfortunately there's no generic way to |
350 | // know in advance what event types are supported. |
351 | return false; |
352 | } |
353 | #endif |
354 | |
355 | #if !PLATFORM(COCOA) |
356 | |
357 | inline bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&) |
358 | { |
359 | return false; |
360 | } |
361 | |
362 | #if ENABLE(DRAG_SUPPORT) |
363 | inline bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&) |
364 | { |
365 | return false; |
366 | } |
367 | #endif |
368 | |
369 | #endif |
370 | |
371 | EventHandler::EventHandler(Frame& frame) |
372 | : m_frame(frame) |
373 | , m_hoverTimer(*this, &EventHandler::hoverTimerFired) |
374 | #if ENABLE(CURSOR_SUPPORT) |
375 | , m_cursorUpdateTimer(*this, &EventHandler::cursorUpdateTimerFired) |
376 | #endif |
377 | #if PLATFORM(MAC) |
378 | , m_pendingMomentumWheelEventsTimer(*this, &EventHandler::clearLatchedState) |
379 | #endif |
380 | , m_autoscrollController(std::make_unique<AutoscrollController>()) |
381 | #if !ENABLE(IOS_TOUCH_EVENTS) |
382 | , m_fakeMouseMoveEventTimer(*this, &EventHandler::fakeMouseMoveEventTimerFired) |
383 | #endif |
384 | #if ENABLE(CURSOR_VISIBILITY) |
385 | , m_autoHideCursorTimer(*this, &EventHandler::autoHideCursorTimerFired) |
386 | #endif |
387 | { |
388 | } |
389 | |
390 | EventHandler::~EventHandler() |
391 | { |
392 | #if !ENABLE(IOS_TOUCH_EVENTS) |
393 | ASSERT(!m_fakeMouseMoveEventTimer.isActive()); |
394 | #endif |
395 | #if ENABLE(CURSOR_VISIBILITY) |
396 | ASSERT(!m_autoHideCursorTimer.isActive()); |
397 | #endif |
398 | } |
399 | |
400 | #if ENABLE(DRAG_SUPPORT) |
401 | |
402 | DragState& EventHandler::dragState() |
403 | { |
404 | static NeverDestroyed<DragState> state; |
405 | return state; |
406 | } |
407 | |
408 | #endif |
409 | |
410 | void EventHandler::clear() |
411 | { |
412 | m_hoverTimer.stop(); |
413 | #if ENABLE(CURSOR_SUPPORT) |
414 | m_cursorUpdateTimer.stop(); |
415 | #endif |
416 | #if !ENABLE(IOS_TOUCH_EVENTS) |
417 | m_fakeMouseMoveEventTimer.stop(); |
418 | #endif |
419 | #if ENABLE(CURSOR_VISIBILITY) |
420 | cancelAutoHideCursorTimer(); |
421 | #endif |
422 | m_resizeLayer = nullptr; |
423 | m_elementUnderMouse = nullptr; |
424 | m_lastElementUnderMouse = nullptr; |
425 | m_lastMouseMoveEventSubframe = nullptr; |
426 | m_lastScrollbarUnderMouse = nullptr; |
427 | m_clickCount = 0; |
428 | m_clickNode = nullptr; |
429 | #if ENABLE(IOS_GESTURE_EVENTS) |
430 | m_gestureInitialDiameter = GestureUnknown; |
431 | m_gestureInitialRotation = GestureUnknown; |
432 | #endif |
433 | #if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) |
434 | m_gestureLastDiameter = GestureUnknown; |
435 | m_gestureLastRotation = GestureUnknown; |
436 | m_gestureTargets.clear(); |
437 | #endif |
438 | #if ENABLE(IOS_TOUCH_EVENTS) |
439 | m_touches.clear(); |
440 | m_firstTouchID = InvalidTouchIdentifier; |
441 | m_touchEventTargetSubframe = nullptr; |
442 | #endif |
443 | m_frameSetBeingResized = nullptr; |
444 | #if ENABLE(DRAG_SUPPORT) |
445 | m_dragTarget = nullptr; |
446 | m_shouldOnlyFireDragOverEvent = false; |
447 | #endif |
448 | m_mousePositionIsUnknown = true; |
449 | m_lastKnownMousePosition = IntPoint(); |
450 | m_lastKnownMouseGlobalPosition = IntPoint(); |
451 | m_mousePressNode = nullptr; |
452 | m_mousePressed = false; |
453 | m_capturesDragging = false; |
454 | m_capturingMouseEventsElement = nullptr; |
455 | clearLatchedState(); |
456 | #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) |
457 | m_originatingTouchPointTargets.clear(); |
458 | m_originatingTouchPointDocument = nullptr; |
459 | m_originatingTouchPointTargetKey = 0; |
460 | #endif |
461 | m_maxMouseMovedDuration = 0; |
462 | m_didStartDrag = false; |
463 | } |
464 | |
465 | void EventHandler::nodeWillBeRemoved(Node& nodeToBeRemoved) |
466 | { |
467 | if (nodeToBeRemoved.contains(m_clickNode.get())) |
468 | m_clickNode = nullptr; |
469 | } |
470 | |
471 | static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& newSelection) |
472 | { |
473 | if (selection.selection() != newSelection && selection.shouldChangeSelection(newSelection)) |
474 | selection.setSelection(newSelection); |
475 | } |
476 | |
477 | static inline bool dispatchSelectStart(Node* node) |
478 | { |
479 | if (!node || !node->renderer()) |
480 | return true; |
481 | |
482 | auto event = Event::create(eventNames().selectstartEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes); |
483 | node->dispatchEvent(event); |
484 | return !event->defaultPrevented(); |
485 | } |
486 | |
487 | static Node* nodeToSelectOnMouseDownForNode(Node& targetNode) |
488 | { |
489 | #if ENABLE(USERSELECT_ALL) |
490 | if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(&targetNode)) |
491 | return rootUserSelectAll; |
492 | #endif |
493 | |
494 | if (targetNode.shouldSelectOnMouseDown()) |
495 | return &targetNode; |
496 | |
497 | return nullptr; |
498 | } |
499 | |
500 | static VisibleSelection expandSelectionToRespectSelectOnMouseDown(Node& targetNode, const VisibleSelection& selection) |
501 | { |
502 | Node* nodeToSelect = nodeToSelectOnMouseDownForNode(targetNode); |
503 | if (!nodeToSelect) |
504 | return selection; |
505 | |
506 | VisibleSelection newSelection(selection); |
507 | newSelection.setBase(positionBeforeNode(nodeToSelect).upstream(CanCrossEditingBoundary)); |
508 | newSelection.setExtent(positionAfterNode(nodeToSelect).downstream(CanCrossEditingBoundary)); |
509 | |
510 | return newSelection; |
511 | } |
512 | |
513 | bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& selection, TextGranularity granularity) |
514 | { |
515 | if (Position::nodeIsUserSelectNone(targetNode)) |
516 | return false; |
517 | |
518 | if (!dispatchSelectStart(targetNode)) { |
519 | m_mouseDownMayStartSelect = false; |
520 | return false; |
521 | } |
522 | |
523 | if (selection.isRange()) |
524 | m_selectionInitiationState = ExtendedSelection; |
525 | else { |
526 | granularity = CharacterGranularity; |
527 | m_selectionInitiationState = PlacedCaret; |
528 | } |
529 | |
530 | m_frame.selection().setSelectionByMouseIfDifferent(selection, granularity); |
531 | |
532 | return true; |
533 | } |
534 | |
535 | void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& result, AppendTrailingWhitespace appendTrailingWhitespace) |
536 | { |
537 | Node* targetNode = result.targetNode(); |
538 | VisibleSelection newSelection; |
539 | |
540 | if (targetNode && targetNode->renderer()) { |
541 | VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr)); |
542 | if (pos.isNotNull()) { |
543 | newSelection = VisibleSelection(pos); |
544 | newSelection.expandUsingGranularity(WordGranularity); |
545 | } |
546 | |
547 | if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange()) |
548 | newSelection.appendTrailingWhitespace(); |
549 | |
550 | updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity); |
551 | } |
552 | } |
553 | |
554 | static AppendTrailingWhitespace shouldAppendTrailingWhitespace(const MouseEventWithHitTestResults& result, const Frame& frame) |
555 | { |
556 | return (result.event().clickCount() == 2 && frame.editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace; |
557 | } |
558 | |
559 | void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& result) |
560 | { |
561 | if (m_mouseDownMayStartSelect) |
562 | selectClosestWordFromHitTestResult(result.hitTestResult(), shouldAppendTrailingWhitespace(result, m_frame)); |
563 | } |
564 | |
565 | #if !PLATFORM(MAC) |
566 | VisibleSelection EventHandler::selectClosestWordFromHitTestResultBasedOnLookup(const HitTestResult&) |
567 | { |
568 | return VisibleSelection(); |
569 | } |
570 | #endif |
571 | |
572 | void EventHandler::selectClosestContextualWordFromMouseEvent(const MouseEventWithHitTestResults& mouseEvent) |
573 | { |
574 | Node* targetNode = mouseEvent.targetNode(); |
575 | const HitTestResult& result = mouseEvent.hitTestResult(); |
576 | VisibleSelection newSelection; |
577 | bool appendTrailingWhitespace = shouldAppendTrailingWhitespace(mouseEvent, m_frame); |
578 | |
579 | if (targetNode && targetNode->renderer()) { |
580 | newSelection = selectClosestWordFromHitTestResultBasedOnLookup(result); |
581 | if (newSelection.isNone()) { |
582 | VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr)); |
583 | if (pos.isNotNull()) { |
584 | newSelection = VisibleSelection(pos); |
585 | newSelection.expandUsingGranularity(WordGranularity); |
586 | } |
587 | } |
588 | |
589 | if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange()) |
590 | newSelection.appendTrailingWhitespace(); |
591 | |
592 | updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity); |
593 | } |
594 | } |
595 | |
596 | void EventHandler::selectClosestContextualWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result) |
597 | { |
598 | Element* urlElement = result.hitTestResult().URLElement(); |
599 | if (!urlElement || !isDraggableLink(*urlElement)) { |
600 | if (Node* targetNode = result.targetNode()) { |
601 | if (isEditableNode(*targetNode)) |
602 | return selectClosestWordFromMouseEvent(result); |
603 | } |
604 | |
605 | return selectClosestContextualWordFromMouseEvent(result); |
606 | } |
607 | |
608 | Node* targetNode = result.targetNode(); |
609 | |
610 | if (targetNode && targetNode->renderer() && m_mouseDownMayStartSelect) { |
611 | VisibleSelection newSelection; |
612 | VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr)); |
613 | if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(*urlElement)) |
614 | newSelection = VisibleSelection::selectionFromContentsOfNode(urlElement); |
615 | |
616 | updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity); |
617 | } |
618 | } |
619 | |
620 | bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestResults& event) |
621 | { |
622 | if (event.event().button() != LeftButton) |
623 | return false; |
624 | |
625 | if (m_frame.selection().isRange()) |
626 | // A double-click when range is already selected |
627 | // should not change the selection. So, do not call |
628 | // selectClosestWordFromMouseEvent, but do set |
629 | // m_beganSelectingText to prevent handleMouseReleaseEvent |
630 | // from setting caret selection. |
631 | m_selectionInitiationState = ExtendedSelection; |
632 | else |
633 | selectClosestWordFromMouseEvent(event); |
634 | |
635 | return true; |
636 | } |
637 | |
638 | bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestResults& event) |
639 | { |
640 | if (event.event().button() != LeftButton) |
641 | return false; |
642 | |
643 | Node* targetNode = event.targetNode(); |
644 | if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect)) |
645 | return false; |
646 | |
647 | VisibleSelection newSelection; |
648 | VisiblePosition pos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr)); |
649 | if (pos.isNotNull()) { |
650 | newSelection = VisibleSelection(pos); |
651 | newSelection.expandUsingGranularity(ParagraphGranularity); |
652 | } |
653 | |
654 | return updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), ParagraphGranularity); |
655 | } |
656 | |
657 | static int textDistance(const Position& start, const Position& end) |
658 | { |
659 | auto range = Range::create(start.anchorNode()->document(), start, end); |
660 | return TextIterator::rangeLength(range.ptr(), true); |
661 | } |
662 | |
663 | bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestResults& event) |
664 | { |
665 | Ref<Frame> protectedFrame(m_frame); |
666 | |
667 | m_frame.document()->updateLayoutIgnorePendingStylesheets(); |
668 | Node* targetNode = event.targetNode(); |
669 | if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect)) |
670 | return false; |
671 | |
672 | // Extend the selection if the Shift key is down, unless the click is in a link. |
673 | bool extendSelection = event.event().shiftKey() && !event.isOverLink(); |
674 | |
675 | // Don't restart the selection when the mouse is pressed on an |
676 | // existing selection so we can allow for text dragging. |
677 | if (FrameView* view = m_frame.view()) { |
678 | LayoutPoint vPoint = view->windowToContents(event.event().position()); |
679 | if (!extendSelection && m_frame.selection().contains(vPoint)) { |
680 | m_mouseDownWasSingleClickInSelection = true; |
681 | return false; |
682 | } |
683 | } |
684 | |
685 | VisiblePosition visiblePos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr)); |
686 | if (visiblePos.isNull()) |
687 | visiblePos = VisiblePosition(firstPositionInOrBeforeNode(targetNode), DOWNSTREAM); |
688 | Position pos = visiblePos.deepEquivalent(); |
689 | |
690 | VisibleSelection newSelection = m_frame.selection().selection(); |
691 | TextGranularity granularity = CharacterGranularity; |
692 | |
693 | #if PLATFORM(IOS_FAMILY) |
694 | // The text selection assistant will handle selection in the case where we are already editing the node |
695 | if (newSelection.rootEditableElement() == targetNode->rootEditableElement()) |
696 | return true; |
697 | #endif |
698 | |
699 | if (extendSelection && newSelection.isCaretOrRange()) { |
700 | VisibleSelection selectionInUserSelectAll = expandSelectionToRespectSelectOnMouseDown(*targetNode, VisibleSelection(pos)); |
701 | if (selectionInUserSelectAll.isRange()) { |
702 | if (comparePositions(selectionInUserSelectAll.start(), newSelection.start()) < 0) |
703 | pos = selectionInUserSelectAll.start(); |
704 | else if (comparePositions(newSelection.end(), selectionInUserSelectAll.end()) < 0) |
705 | pos = selectionInUserSelectAll.end(); |
706 | } |
707 | |
708 | if (!m_frame.editor().behavior().shouldConsiderSelectionAsDirectional() && pos.isNotNull()) { |
709 | // See <rdar://problem/3668157> REGRESSION (Mail): shift-click deselects when selection |
710 | // was created right-to-left |
711 | Position start = newSelection.start(); |
712 | Position end = newSelection.end(); |
713 | int distanceToStart = textDistance(start, pos); |
714 | int distanceToEnd = textDistance(pos, end); |
715 | if (distanceToStart <= distanceToEnd) |
716 | newSelection = VisibleSelection(end, pos); |
717 | else |
718 | newSelection = VisibleSelection(start, pos); |
719 | } else |
720 | newSelection.setExtent(pos); |
721 | |
722 | if (m_frame.selection().granularity() != CharacterGranularity) { |
723 | granularity = m_frame.selection().granularity(); |
724 | newSelection.expandUsingGranularity(m_frame.selection().granularity()); |
725 | } |
726 | } else |
727 | newSelection = expandSelectionToRespectSelectOnMouseDown(*targetNode, visiblePos); |
728 | |
729 | bool handled = updateSelectionForMouseDownDispatchingSelectStart(targetNode, newSelection, granularity); |
730 | |
731 | if (event.event().button() == MiddleButton) { |
732 | // Ignore handled, since we want to paste to where the caret was placed anyway. |
733 | handled = handlePasteGlobalSelection(event.event()) || handled; |
734 | } |
735 | return handled; |
736 | } |
737 | |
738 | static inline bool canMouseDownStartSelect(Node* node) |
739 | { |
740 | if (!node || !node->renderer()) |
741 | return true; |
742 | |
743 | return node->canStartSelection() || Position::nodeIsUserSelectAll(node); |
744 | } |
745 | |
746 | bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& event) |
747 | { |
748 | Ref<Frame> protectedFrame(m_frame); |
749 | |
750 | #if ENABLE(DRAG_SUPPORT) |
751 | // Reset drag state. |
752 | dragState().source = nullptr; |
753 | #endif |
754 | |
755 | #if !ENABLE(IOS_TOUCH_EVENTS) |
756 | cancelFakeMouseMoveEvent(); |
757 | #endif |
758 | |
759 | m_frame.document()->updateLayoutIgnorePendingStylesheets(); |
760 | |
761 | if (ScrollView* scrollView = m_frame.view()) { |
762 | if (scrollView->isPointInScrollbarCorner(event.event().position())) |
763 | return false; |
764 | } |
765 | |
766 | bool singleClick = event.event().clickCount() <= 1; |
767 | |
768 | // If we got the event back, that must mean it wasn't prevented, |
769 | // so it's allowed to start a drag or selection if it wasn't in a scrollbar. |
770 | m_mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode()) && !event.scrollbar(); |
771 | |
772 | #if ENABLE(DRAG_SUPPORT) |
773 | // Careful that the drag starting logic stays in sync with eventMayStartDrag() |
774 | // FIXME: eventMayStartDrag() does not check for shift key press, link or image event targets. |
775 | // Bug: https://bugs.webkit.org/show_bug.cgi?id=155390 |
776 | |
777 | // Single mouse down on links or images can always trigger drag-n-drop. |
778 | bool isMouseDownOnLinkOrImage = event.isOverLink() || event.hitTestResult().image(); |
779 | m_mouseDownMayStartDrag = singleClick && (!event.event().shiftKey() || isMouseDownOnLinkOrImage) && shouldAllowMouseDownToStartDrag(); |
780 | #endif |
781 | |
782 | m_mouseDownWasSingleClickInSelection = false; |
783 | |
784 | m_mouseDown = event.event(); |
785 | |
786 | if (m_immediateActionStage != ImmediateActionStage::PerformedHitTest) |
787 | m_immediateActionStage = ImmediateActionStage::None; |
788 | |
789 | if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event)) |
790 | return true; |
791 | |
792 | if (is<SVGDocument>(*m_frame.document()) && downcast<SVGDocument>(*m_frame.document()).zoomAndPanEnabled()) { |
793 | if (event.event().shiftKey() && singleClick) { |
794 | m_svgPan = true; |
795 | downcast<SVGDocument>(*m_frame.document()).startPan(m_frame.view()->windowToContents(event.event().position())); |
796 | return true; |
797 | } |
798 | } |
799 | |
800 | // We don't do this at the start of mouse down handling, |
801 | // because we don't want to do it until we know we didn't hit a widget. |
802 | if (singleClick) |
803 | focusDocumentView(); |
804 | |
805 | m_mousePressNode = event.targetNode(); |
806 | m_frame.document()->setFocusNavigationStartingNode(event.targetNode()); |
807 | |
808 | #if ENABLE(DRAG_SUPPORT) |
809 | m_dragStartPosition = event.event().position(); |
810 | #endif |
811 | |
812 | m_mousePressed = true; |
813 | m_selectionInitiationState = HaveNotStartedSelection; |
814 | |
815 | bool swallowEvent = false; |
816 | if (event.event().clickCount() == 2) |
817 | swallowEvent = handleMousePressEventDoubleClick(event); |
818 | else if (event.event().clickCount() >= 3) |
819 | swallowEvent = handleMousePressEventTripleClick(event); |
820 | else |
821 | swallowEvent = handleMousePressEventSingleClick(event); |
822 | |
823 | m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect |
824 | || (m_mousePressNode && m_mousePressNode->renderBox() && m_mousePressNode->renderBox()->canBeProgramaticallyScrolled()); |
825 | |
826 | return swallowEvent; |
827 | } |
828 | |
829 | VisiblePosition EventHandler::selectionExtentRespectingEditingBoundary(const VisibleSelection& selection, const LayoutPoint& localPoint, Node* targetNode) |
830 | { |
831 | FloatPoint selectionEndPoint = localPoint; |
832 | Element* editableElement = selection.rootEditableElement(); |
833 | |
834 | if (!targetNode || !targetNode->renderer()) |
835 | return VisiblePosition(); |
836 | |
837 | if (editableElement && !editableElement->contains(targetNode)) { |
838 | if (!editableElement->renderer()) |
839 | return VisiblePosition(); |
840 | |
841 | FloatPoint absolutePoint = targetNode->renderer()->localToAbsolute(FloatPoint(selectionEndPoint)); |
842 | selectionEndPoint = editableElement->renderer()->absoluteToLocal(absolutePoint); |
843 | targetNode = editableElement; |
844 | } |
845 | |
846 | return targetNode->renderer()->positionForPoint(LayoutPoint(selectionEndPoint), nullptr); |
847 | } |
848 | |
849 | #if ENABLE(DRAG_SUPPORT) |
850 | |
851 | #if !PLATFORM(IOS_FAMILY) |
852 | |
853 | bool EventHandler::supportsSelectionUpdatesOnMouseDrag() const |
854 | { |
855 | return true; |
856 | } |
857 | |
858 | bool EventHandler::shouldAllowMouseDownToStartDrag() const |
859 | { |
860 | return true; |
861 | } |
862 | |
863 | #endif |
864 | |
865 | bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis) |
866 | { |
867 | if (!m_mousePressed) |
868 | return false; |
869 | |
870 | Ref<Frame> protectedFrame(m_frame); |
871 | |
872 | if (handleDrag(event, checkDragHysteresis)) |
873 | return true; |
874 | |
875 | Node* targetNode = event.targetNode(); |
876 | if (event.event().button() != LeftButton || !targetNode) |
877 | return false; |
878 | |
879 | RenderObject* renderer = targetNode->renderer(); |
880 | if (!renderer) { |
881 | Element* parent = targetNode->parentOrShadowHostElement(); |
882 | if (!parent) |
883 | return false; |
884 | |
885 | renderer = parent->renderer(); |
886 | if (!renderer || !renderer->isListBox()) |
887 | return false; |
888 | } |
889 | |
890 | #if PLATFORM(COCOA) // FIXME: Why does this assertion fire on other platforms? |
891 | ASSERT(m_mouseDownMayStartSelect || m_mouseDownMayStartAutoscroll); |
892 | #endif |
893 | |
894 | m_mouseDownMayStartDrag = false; |
895 | |
896 | if (m_mouseDownMayStartAutoscroll && !panScrollInProgress()) { |
897 | m_autoscrollController->startAutoscrollForSelection(renderer); |
898 | m_mouseDownMayStartAutoscroll = false; |
899 | } |
900 | |
901 | if (m_selectionInitiationState != ExtendedSelection) { |
902 | HitTestResult result(m_mouseDownPos); |
903 | m_frame.document()->renderView()->hitTest(HitTestRequest(), result); |
904 | |
905 | updateSelectionForMouseDrag(result); |
906 | } |
907 | updateSelectionForMouseDrag(event.hitTestResult()); |
908 | return true; |
909 | } |
910 | |
911 | bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const |
912 | { |
913 | // This is a pre-flight check of whether the event might lead to a drag being started. Be careful |
914 | // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag |
915 | // in handleMousePressEvent |
916 | RenderView* renderView = m_frame.contentRenderer(); |
917 | if (!renderView) |
918 | return false; |
919 | |
920 | if (event.button() != LeftButton || event.clickCount() != 1) |
921 | return false; |
922 | |
923 | FrameView* view = m_frame.view(); |
924 | if (!view) |
925 | return false; |
926 | |
927 | Page* page = m_frame.page(); |
928 | if (!page) |
929 | return false; |
930 | |
931 | Ref<Frame> protectedFrame(m_frame); |
932 | |
933 | updateDragSourceActionsAllowed(); |
934 | HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); |
935 | HitTestResult result(view->windowToContents(event.position())); |
936 | renderView->hitTest(request, result); |
937 | DragState state; |
938 | Element* targetElement = result.targetElement(); |
939 | return targetElement && page->dragController().draggableElement(&m_frame, targetElement, result.roundedPointInInnerNodeFrame(), state); |
940 | } |
941 | |
942 | void EventHandler::updateSelectionForMouseDrag() |
943 | { |
944 | if (!supportsSelectionUpdatesOnMouseDrag()) |
945 | return; |
946 | |
947 | FrameView* view = m_frame.view(); |
948 | if (!view) |
949 | return; |
950 | RenderView* renderView = m_frame.contentRenderer(); |
951 | if (!renderView) |
952 | return; |
953 | |
954 | HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent); |
955 | HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); |
956 | renderView->hitTest(request, result); |
957 | updateSelectionForMouseDrag(result); |
958 | } |
959 | |
960 | void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResult) |
961 | { |
962 | if (!supportsSelectionUpdatesOnMouseDrag()) |
963 | return; |
964 | |
965 | if (!m_mouseDownMayStartSelect) |
966 | return; |
967 | |
968 | Node* target = hitTestResult.targetNode(); |
969 | if (!target) |
970 | return; |
971 | |
972 | VisiblePosition targetPosition = selectionExtentRespectingEditingBoundary(m_frame.selection().selection(), hitTestResult.localPoint(), target); |
973 | |
974 | // Don't modify the selection if we're not on a node. |
975 | if (targetPosition.isNull()) |
976 | return; |
977 | |
978 | // Restart the selection if this is the first mouse move. This work is usually |
979 | // done in handleMousePressEvent, but not if the mouse press was on an existing selection. |
980 | VisibleSelection newSelection = m_frame.selection().selection(); |
981 | |
982 | // Special case to limit selection to the containing block for SVG text. |
983 | // FIXME: Isn't there a better non-SVG-specific way to do this? |
984 | if (Node* selectionBaseNode = newSelection.base().deprecatedNode()) { |
985 | if (RenderObject* selectionBaseRenderer = selectionBaseNode->renderer()) { |
986 | if (selectionBaseRenderer->isSVGText()) { |
987 | if (target->renderer()->containingBlock() != selectionBaseRenderer->containingBlock()) |
988 | return; |
989 | } |
990 | } |
991 | } |
992 | |
993 | |
994 | if (m_selectionInitiationState == HaveNotStartedSelection && !dispatchSelectStart(target)) { |
995 | m_mouseDownMayStartSelect = false; |
996 | return; |
997 | } |
998 | |
999 | if (m_selectionInitiationState != ExtendedSelection) { |
1000 | // Always extend selection here because it's caused by a mouse drag |
1001 | m_selectionInitiationState = ExtendedSelection; |
1002 | newSelection = VisibleSelection(targetPosition); |
1003 | } |
1004 | |
1005 | #if ENABLE(USERSELECT_ALL) |
1006 | Node* rootUserSelectAllForMousePressNode = Position::rootUserSelectAllForNode(m_mousePressNode.get()); |
1007 | if (rootUserSelectAllForMousePressNode && rootUserSelectAllForMousePressNode == Position::rootUserSelectAllForNode(target)) { |
1008 | newSelection.setBase(positionBeforeNode(rootUserSelectAllForMousePressNode).upstream(CanCrossEditingBoundary)); |
1009 | newSelection.setExtent(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary)); |
1010 | } else { |
1011 | // Reset base for user select all when base is inside user-select-all area and extent < base. |
1012 | if (rootUserSelectAllForMousePressNode && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0) |
1013 | newSelection.setBase(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary)); |
1014 | |
1015 | Node* rootUserSelectAllForTarget = Position::rootUserSelectAllForNode(target); |
1016 | if (rootUserSelectAllForTarget && m_mousePressNode->renderer() && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0) |
1017 | newSelection.setExtent(positionBeforeNode(rootUserSelectAllForTarget).upstream(CanCrossEditingBoundary)); |
1018 | else if (rootUserSelectAllForTarget && m_mousePressNode->renderer()) |
1019 | newSelection.setExtent(positionAfterNode(rootUserSelectAllForTarget).downstream(CanCrossEditingBoundary)); |
1020 | else |
1021 | newSelection.setExtent(targetPosition); |
1022 | } |
1023 | #else |
1024 | newSelection.setExtent(targetPosition); |
1025 | #endif |
1026 | |
1027 | if (m_frame.selection().granularity() != CharacterGranularity) |
1028 | newSelection.expandUsingGranularity(m_frame.selection().granularity()); |
1029 | |
1030 | m_frame.selection().setSelectionByMouseIfDifferent(newSelection, m_frame.selection().granularity(), |
1031 | FrameSelection::AdjustEndpointsAtBidiBoundary); |
1032 | } |
1033 | #endif // ENABLE(DRAG_SUPPORT) |
1034 | |
1035 | void EventHandler::lostMouseCapture() |
1036 | { |
1037 | m_frame.selection().setCaretBlinkingSuspended(false); |
1038 | } |
1039 | |
1040 | bool EventHandler::handleMouseUp(const MouseEventWithHitTestResults& event) |
1041 | { |
1042 | if (eventLoopHandleMouseUp(event)) |
1043 | return true; |
1044 | |
1045 | // If this was the first click in the window, we don't even want to clear the selection. |
1046 | // This case occurs when the user clicks on a draggable element, since we have to process |
1047 | // the mouse down and drag events to see if we might start a drag. For other first clicks |
1048 | // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets |
1049 | // ignored upstream of this layer. |
1050 | return eventActivatedView(event.event()); |
1051 | } |
1052 | |
1053 | bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event) |
1054 | { |
1055 | if (autoscrollInProgress()) |
1056 | stopAutoscrollTimer(); |
1057 | |
1058 | Ref<Frame> protectedFrame(m_frame); |
1059 | |
1060 | if (handleMouseUp(event)) |
1061 | return true; |
1062 | |
1063 | // Used to prevent mouseMoveEvent from initiating a drag before |
1064 | // the mouse is pressed again. |
1065 | m_mousePressed = false; |
1066 | m_capturesDragging = false; |
1067 | #if ENABLE(DRAG_SUPPORT) |
1068 | m_mouseDownMayStartDrag = false; |
1069 | #endif |
1070 | m_mouseDownMayStartSelect = false; |
1071 | m_mouseDownMayStartAutoscroll = false; |
1072 | m_mouseDownWasInSubframe = false; |
1073 | |
1074 | bool handled = false; |
1075 | |
1076 | // Clear the selection if the mouse didn't move after the last mouse |
1077 | // press and it's not a context menu click. We do this so when clicking |
1078 | // on the selection, the selection goes away. However, if we are |
1079 | // editing, place the caret. |
1080 | if (m_mouseDownWasSingleClickInSelection && m_selectionInitiationState != ExtendedSelection |
1081 | #if ENABLE(DRAG_SUPPORT) |
1082 | && m_dragStartPosition == event.event().position() |
1083 | #endif |
1084 | && m_frame.selection().isRange() |
1085 | && event.event().button() != RightButton) { |
1086 | VisibleSelection newSelection; |
1087 | Node* node = event.targetNode(); |
1088 | bool caretBrowsing = m_frame.settings().caretBrowsingEnabled(); |
1089 | bool allowSelectionChanges = true; |
1090 | if (node && node->renderer() && (caretBrowsing || node->hasEditableStyle())) { |
1091 | VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint(), nullptr); |
1092 | newSelection = VisibleSelection(pos); |
1093 | #if PLATFORM(IOS_FAMILY) |
1094 | // On iOS, selection changes are triggered using platform-specific text interaction gestures rather than |
1095 | // default behavior on click or mouseup. As such, the only time we should allow click events to change the |
1096 | // selection on iOS is when we focus a different editable element, in which case the text interaction |
1097 | // gestures will fail. |
1098 | allowSelectionChanges = m_frame.selection().selection().rootEditableElement() != newSelection.rootEditableElement(); |
1099 | #endif |
1100 | } |
1101 | |
1102 | if (allowSelectionChanges) |
1103 | setSelectionIfNeeded(m_frame.selection(), newSelection); |
1104 | |
1105 | handled = true; |
1106 | } |
1107 | |
1108 | if (event.event().button() == MiddleButton) { |
1109 | // Ignore handled, since we want to paste to where the caret was placed anyway. |
1110 | handled = handlePasteGlobalSelection(event.event()) || handled; |
1111 | } |
1112 | |
1113 | return handled; |
1114 | } |
1115 | |
1116 | #if ENABLE(PAN_SCROLLING) |
1117 | |
1118 | void EventHandler::didPanScrollStart() |
1119 | { |
1120 | m_autoscrollController->didPanScrollStart(); |
1121 | } |
1122 | |
1123 | void EventHandler::didPanScrollStop() |
1124 | { |
1125 | m_autoscrollController->didPanScrollStop(); |
1126 | } |
1127 | |
1128 | void EventHandler::startPanScrolling(RenderElement& renderer) |
1129 | { |
1130 | #if !PLATFORM(IOS_FAMILY) |
1131 | if (!is<RenderBox>(renderer)) |
1132 | return; |
1133 | m_autoscrollController->startPanScrolling(&downcast<RenderBox>(renderer), lastKnownMousePosition()); |
1134 | invalidateClick(); |
1135 | #endif |
1136 | } |
1137 | |
1138 | #endif // ENABLE(PAN_SCROLLING) |
1139 | |
1140 | RenderBox* EventHandler::autoscrollRenderer() const |
1141 | { |
1142 | return m_autoscrollController->autoscrollRenderer(); |
1143 | } |
1144 | |
1145 | void EventHandler::updateAutoscrollRenderer() |
1146 | { |
1147 | m_autoscrollController->updateAutoscrollRenderer(); |
1148 | } |
1149 | |
1150 | bool EventHandler::autoscrollInProgress() const |
1151 | { |
1152 | return m_autoscrollController->autoscrollInProgress(); |
1153 | } |
1154 | |
1155 | bool EventHandler::panScrollInProgress() const |
1156 | { |
1157 | return m_autoscrollController->panScrollInProgress(); |
1158 | } |
1159 | |
1160 | #if ENABLE(DRAG_SUPPORT) |
1161 | DragSourceAction EventHandler::updateDragSourceActionsAllowed() const |
1162 | { |
1163 | Page* page = m_frame.page(); |
1164 | if (!page) |
1165 | return DragSourceActionNone; |
1166 | |
1167 | FrameView* view = m_frame.view(); |
1168 | if (!view) |
1169 | return DragSourceActionNone; |
1170 | |
1171 | return page->dragController().delegateDragSourceAction(view->contentsToRootView(m_mouseDownPos)); |
1172 | } |
1173 | #endif // ENABLE(DRAG_SUPPORT) |
1174 | |
1175 | HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding) const |
1176 | { |
1177 | ASSERT((hitType & HitTestRequest::CollectMultipleElements) || padding.isEmpty()); |
1178 | |
1179 | Ref<Frame> protectedFrame(m_frame); |
1180 | |
1181 | // We always send hitTestResultAtPoint to the main frame if we have one, |
1182 | // otherwise we might hit areas that are obscured by higher frames. |
1183 | if (!m_frame.isMainFrame()) { |
1184 | Frame& mainFrame = m_frame.mainFrame(); |
1185 | FrameView* frameView = m_frame.view(); |
1186 | FrameView* mainView = mainFrame.view(); |
1187 | if (frameView && mainView) { |
1188 | IntPoint mainFramePoint = mainView->rootViewToContents(frameView->contentsToRootView(roundedIntPoint(point))); |
1189 | return mainFrame.eventHandler().hitTestResultAtPoint(mainFramePoint, hitType, padding); |
1190 | } |
1191 | } |
1192 | |
1193 | unsigned nonNegativePaddingWidth = std::max<LayoutUnit>(0, padding.width()).toUnsigned(); |
1194 | unsigned nonNegativePaddingHeight = std::max<LayoutUnit>(0, padding.height()).toUnsigned(); |
1195 | |
1196 | // We should always start hit testing a clean tree. |
1197 | if (auto* frameView = m_frame.view()) |
1198 | frameView->updateLayoutAndStyleIfNeededRecursive(); |
1199 | |
1200 | HitTestResult result(point, nonNegativePaddingHeight, nonNegativePaddingWidth, nonNegativePaddingHeight, nonNegativePaddingWidth); |
1201 | RenderView* renderView = m_frame.contentRenderer(); |
1202 | if (!renderView) |
1203 | return result; |
1204 | |
1205 | // hitTestResultAtPoint is specifically used to hitTest into all frames, thus it always allows child frame content. |
1206 | HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent); |
1207 | renderView->hitTest(request, result); |
1208 | if (!request.readOnly()) |
1209 | m_frame.document()->updateHoverActiveState(request, result.targetElement()); |
1210 | |
1211 | if (request.disallowsUserAgentShadowContent()) |
1212 | result.setToNonUserAgentShadowAncestor(); |
1213 | |
1214 | return result; |
1215 | } |
1216 | |
1217 | void EventHandler::stopAutoscrollTimer(bool rendererIsBeingDestroyed) |
1218 | { |
1219 | m_autoscrollController->stopAutoscrollTimer(rendererIsBeingDestroyed); |
1220 | } |
1221 | |
1222 | bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode) |
1223 | { |
1224 | Node* node = startingNode; |
1225 | |
1226 | if (!node) |
1227 | node = m_frame.document()->focusedElement(); |
1228 | |
1229 | if (!node) |
1230 | node = m_mousePressNode.get(); |
1231 | |
1232 | if (node) { |
1233 | auto r = node->renderer(); |
1234 | if (r && !r->isListBox() && r->enclosingBox().scroll(direction, granularity)) { |
1235 | setFrameWasScrolledByUser(); |
1236 | return true; |
1237 | } |
1238 | } |
1239 | |
1240 | return false; |
1241 | } |
1242 | |
1243 | bool EventHandler::logicalScrollOverflow(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode) |
1244 | { |
1245 | Node* node = startingNode; |
1246 | |
1247 | if (!node) |
1248 | node = m_frame.document()->focusedElement(); |
1249 | |
1250 | if (!node) |
1251 | node = m_mousePressNode.get(); |
1252 | |
1253 | if (node) { |
1254 | auto r = node->renderer(); |
1255 | if (r && !r->isListBox() && r->enclosingBox().logicalScroll(direction, granularity)) { |
1256 | setFrameWasScrolledByUser(); |
1257 | return true; |
1258 | } |
1259 | } |
1260 | |
1261 | return false; |
1262 | } |
1263 | |
1264 | bool EventHandler::scrollRecursively(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode) |
1265 | { |
1266 | Ref<Frame> protectedFrame(m_frame); |
1267 | |
1268 | // The layout needs to be up to date to determine if we can scroll. We may be |
1269 | // here because of an onLoad event, in which case the final layout hasn't been performed yet. |
1270 | m_frame.document()->updateLayoutIgnorePendingStylesheets(); |
1271 | if (scrollOverflow(direction, granularity, startingNode)) |
1272 | return true; |
1273 | Frame* frame = &m_frame; |
1274 | FrameView* view = frame->view(); |
1275 | if (view && view->scroll(direction, granularity)) |
1276 | return true; |
1277 | frame = frame->tree().parent(); |
1278 | if (!frame) |
1279 | return false; |
1280 | return frame->eventHandler().scrollRecursively(direction, granularity, m_frame.ownerElement()); |
1281 | } |
1282 | |
1283 | bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode) |
1284 | { |
1285 | Ref<Frame> protectedFrame(m_frame); |
1286 | |
1287 | // The layout needs to be up to date to determine if we can scroll. We may be |
1288 | // here because of an onLoad event, in which case the final layout hasn't been performed yet. |
1289 | m_frame.document()->updateLayoutIgnorePendingStylesheets(); |
1290 | if (logicalScrollOverflow(direction, granularity, startingNode)) |
1291 | return true; |
1292 | Frame* frame = &m_frame; |
1293 | FrameView* view = frame->view(); |
1294 | |
1295 | bool scrolled = false; |
1296 | #if PLATFORM(COCOA) |
1297 | // Mac also resets the scroll position in the inline direction. |
1298 | if (granularity == ScrollByDocument && view && view->logicalScroll(ScrollInlineDirectionBackward, ScrollByDocument)) |
1299 | scrolled = true; |
1300 | #endif |
1301 | if (view && view->logicalScroll(direction, granularity)) |
1302 | scrolled = true; |
1303 | |
1304 | if (scrolled) |
1305 | return true; |
1306 | |
1307 | frame = frame->tree().parent(); |
1308 | if (!frame) |
1309 | return false; |
1310 | |
1311 | return frame->eventHandler().logicalScrollRecursively(direction, granularity, m_frame.ownerElement()); |
1312 | } |
1313 | |
1314 | IntPoint EventHandler::lastKnownMousePosition() const |
1315 | { |
1316 | return m_lastKnownMousePosition; |
1317 | } |
1318 | |
1319 | Frame* EventHandler::subframeForHitTestResult(const MouseEventWithHitTestResults& hitTestResult) |
1320 | { |
1321 | if (!hitTestResult.isOverWidget()) |
1322 | return 0; |
1323 | return subframeForTargetNode(hitTestResult.targetNode()); |
1324 | } |
1325 | |
1326 | Frame* EventHandler::subframeForTargetNode(Node* node) |
1327 | { |
1328 | if (!node) |
1329 | return nullptr; |
1330 | |
1331 | auto renderer = node->renderer(); |
1332 | if (!is<RenderWidget>(renderer)) |
1333 | return nullptr; |
1334 | |
1335 | Widget* widget = downcast<RenderWidget>(*renderer).widget(); |
1336 | if (!is<FrameView>(widget)) |
1337 | return nullptr; |
1338 | |
1339 | return &downcast<FrameView>(*widget).frame(); |
1340 | } |
1341 | |
1342 | #if ENABLE(CURSOR_SUPPORT) |
1343 | static bool isSubmitImage(Node* node) |
1344 | { |
1345 | return is<HTMLInputElement>(node) && downcast<HTMLInputElement>(*node).isImageButton(); |
1346 | } |
1347 | |
1348 | // Returns true if the node's editable block is not current focused for editing |
1349 | static bool nodeIsNotBeingEdited(const Node& node, const Frame& frame) |
1350 | { |
1351 | return frame.selection().selection().rootEditableElement() != node.rootEditableElement(); |
1352 | } |
1353 | |
1354 | bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey) |
1355 | { |
1356 | if (!node) |
1357 | return false; |
1358 | |
1359 | bool editable = node->hasEditableStyle(); |
1360 | bool editableLinkEnabled = false; |
1361 | |
1362 | // If the link is editable, then we need to check the settings to see whether or not the link should be followed |
1363 | if (editable) { |
1364 | switch (m_frame.settings().editableLinkBehavior()) { |
1365 | default: |
1366 | case EditableLinkDefaultBehavior: |
1367 | case EditableLinkAlwaysLive: |
1368 | editableLinkEnabled = true; |
1369 | break; |
1370 | |
1371 | case EditableLinkNeverLive: |
1372 | editableLinkEnabled = false; |
1373 | break; |
1374 | |
1375 | case EditableLinkLiveWhenNotFocused: |
1376 | editableLinkEnabled = nodeIsNotBeingEdited(*node, m_frame) || shiftKey; |
1377 | break; |
1378 | |
1379 | case EditableLinkOnlyLiveWithShiftKey: |
1380 | editableLinkEnabled = shiftKey; |
1381 | break; |
1382 | } |
1383 | } |
1384 | |
1385 | return ((isOverLink || isSubmitImage(node)) && (!editable || editableLinkEnabled)); |
1386 | } |
1387 | |
1388 | void EventHandler::cursorUpdateTimerFired() |
1389 | { |
1390 | ASSERT(m_frame.document()); |
1391 | updateCursor(); |
1392 | } |
1393 | |
1394 | void EventHandler::updateCursor() |
1395 | { |
1396 | if (m_mousePositionIsUnknown) |
1397 | return; |
1398 | |
1399 | FrameView* view = m_frame.view(); |
1400 | if (!view) |
1401 | return; |
1402 | |
1403 | RenderView* renderView = view->renderView(); |
1404 | if (!renderView) |
1405 | return; |
1406 | |
1407 | if (!view->shouldSetCursor()) |
1408 | return; |
1409 | |
1410 | bool shiftKey; |
1411 | bool ctrlKey; |
1412 | bool altKey; |
1413 | bool metaKey; |
1414 | PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey); |
1415 | |
1416 | HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::AllowFrameScrollbars); |
1417 | HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); |
1418 | renderView->hitTest(request, result); |
1419 | |
1420 | updateCursor(*view, result, shiftKey); |
1421 | } |
1422 | |
1423 | void EventHandler::updateCursor(FrameView& view, const HitTestResult& result, bool shiftKey) |
1424 | { |
1425 | if (auto optionalCursor = selectCursor(result, shiftKey)) { |
1426 | m_currentMouseCursor = WTFMove(optionalCursor.value()); |
1427 | view.setCursor(m_currentMouseCursor); |
1428 | } |
1429 | } |
1430 | |
1431 | Optional<Cursor> EventHandler::selectCursor(const HitTestResult& result, bool shiftKey) |
1432 | { |
1433 | if (m_resizeLayer && m_resizeLayer->inResizeMode()) |
1434 | return WTF::nullopt; |
1435 | |
1436 | if (!m_frame.page()) |
1437 | return WTF::nullopt; |
1438 | |
1439 | #if ENABLE(PAN_SCROLLING) |
1440 | if (m_frame.mainFrame().eventHandler().panScrollInProgress()) |
1441 | return WTF::nullopt; |
1442 | #endif |
1443 | |
1444 | Ref<Frame> protectedFrame(m_frame); |
1445 | |
1446 | // Use always pointer cursor for scrollbars. |
1447 | if (result.scrollbar()) { |
1448 | #if ENABLE(CURSOR_VISIBILITY) |
1449 | cancelAutoHideCursorTimer(); |
1450 | #endif |
1451 | return pointerCursor(); |
1452 | } |
1453 | |
1454 | Node* node = result.targetNode(); |
1455 | if (!node) |
1456 | return WTF::nullopt; |
1457 | |
1458 | auto renderer = node->renderer(); |
1459 | auto* style = renderer ? &renderer->style() : nullptr; |
1460 | bool horizontalText = !style || style->isHorizontalWritingMode(); |
1461 | const Cursor& iBeam = horizontalText ? iBeamCursor() : verticalTextCursor(); |
1462 | |
1463 | #if ENABLE(CURSOR_VISIBILITY) |
1464 | if (style && style->cursorVisibility() == CursorVisibility::AutoHide) |
1465 | startAutoHideCursorTimer(); |
1466 | else |
1467 | cancelAutoHideCursorTimer(); |
1468 | #endif |
1469 | |
1470 | if (renderer) { |
1471 | Cursor overrideCursor; |
1472 | switch (renderer->getCursor(roundedIntPoint(result.localPoint()), overrideCursor)) { |
1473 | case SetCursorBasedOnStyle: |
1474 | break; |
1475 | case SetCursor: |
1476 | return overrideCursor; |
1477 | case DoNotSetCursor: |
1478 | return WTF::nullopt; |
1479 | } |
1480 | } |
1481 | |
1482 | if (style && style->cursors()) { |
1483 | const CursorList* cursors = style->cursors(); |
1484 | for (unsigned i = 0; i < cursors->size(); ++i) { |
1485 | StyleImage* styleImage = (*cursors)[i].image(); |
1486 | if (!styleImage) |
1487 | continue; |
1488 | CachedImage* cachedImage = styleImage->cachedImage(); |
1489 | if (!cachedImage) |
1490 | continue; |
1491 | float scale = styleImage->imageScaleFactor(); |
1492 | // Get hotspot and convert from logical pixels to physical pixels. |
1493 | IntPoint hotSpot = (*cursors)[i].hotSpot(); |
1494 | FloatSize size = cachedImage->imageForRenderer(renderer)->size(); |
1495 | if (cachedImage->errorOccurred()) |
1496 | continue; |
1497 | // Limit the size of cursors (in UI pixels) so that they cannot be |
1498 | // used to cover UI elements in chrome. |
1499 | size.scale(1 / scale); |
1500 | if (size.width() > maximumCursorSize || size.height() > maximumCursorSize) |
1501 | continue; |
1502 | |
1503 | Image* image = cachedImage->imageForRenderer(renderer); |
1504 | #if ENABLE(MOUSE_CURSOR_SCALE) |
1505 | // Ensure no overflow possible in calculations above. |
1506 | if (scale < minimumCursorScale) |
1507 | continue; |
1508 | return Cursor(image, hotSpot, scale); |
1509 | #else |
1510 | ASSERT(scale == 1); |
1511 | return Cursor(image, hotSpot); |
1512 | #endif // ENABLE(MOUSE_CURSOR_SCALE) |
1513 | } |
1514 | } |
1515 | |
1516 | // During selection, use an I-beam regardless of the content beneath the cursor. |
1517 | // If a drag may be starting or we're capturing mouse events for a particular node, don't treat this as a selection. |
1518 | if (m_mousePressed |
1519 | && m_mouseDownMayStartSelect |
1520 | #if ENABLE(DRAG_SUPPORT) |
1521 | && !m_mouseDownMayStartDrag |
1522 | #endif |
1523 | && m_frame.selection().isCaretOrRange() |
1524 | && !m_capturingMouseEventsElement) |
1525 | return iBeam; |
1526 | |
1527 | switch (style ? style->cursor() : CursorType::Auto) { |
1528 | case CursorType::Auto: { |
1529 | bool editable = node->hasEditableStyle(); |
1530 | |
1531 | if (useHandCursor(node, result.isOverLink(), shiftKey)) |
1532 | return handCursor(); |
1533 | |
1534 | bool inResizer = false; |
1535 | if (renderer) { |
1536 | if (RenderLayer* layer = renderer->enclosingLayer()) { |
1537 | if (FrameView* view = m_frame.view()) |
1538 | inResizer = layer->isPointInResizeControl(view->windowToContents(roundedIntPoint(result.localPoint()))); |
1539 | } |
1540 | } |
1541 | |
1542 | if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !result.scrollbar()) |
1543 | return iBeam; |
1544 | return pointerCursor(); |
1545 | } |
1546 | case CursorType::Default: |
1547 | return pointerCursor(); |
1548 | case CursorType::None: |
1549 | return noneCursor(); |
1550 | case CursorType::ContextMenu: |
1551 | return contextMenuCursor(); |
1552 | case CursorType::Help: |
1553 | return helpCursor(); |
1554 | case CursorType::Pointer: |
1555 | return handCursor(); |
1556 | case CursorType::Progress: |
1557 | return progressCursor(); |
1558 | case CursorType::Wait: |
1559 | return waitCursor(); |
1560 | case CursorType::Cell: |
1561 | return cellCursor(); |
1562 | case CursorType::Crosshair: |
1563 | return crossCursor(); |
1564 | case CursorType::Text: |
1565 | return iBeamCursor(); |
1566 | case CursorType::VerticalText: |
1567 | return verticalTextCursor(); |
1568 | case CursorType::Alias: |
1569 | return aliasCursor(); |
1570 | case CursorType::Copy: |
1571 | return copyCursor(); |
1572 | case CursorType::Move: |
1573 | return moveCursor(); |
1574 | case CursorType::NoDrop: |
1575 | return noDropCursor(); |
1576 | case CursorType::NotAllowed: |
1577 | return notAllowedCursor(); |
1578 | case CursorType::Grab: |
1579 | return grabCursor(); |
1580 | case CursorType::Grabbing: |
1581 | return grabbingCursor(); |
1582 | case CursorType::EResize: |
1583 | return eastResizeCursor(); |
1584 | case CursorType::NResize: |
1585 | return northResizeCursor(); |
1586 | case CursorType::NEResize: |
1587 | return northEastResizeCursor(); |
1588 | case CursorType::NWResize: |
1589 | return northWestResizeCursor(); |
1590 | case CursorType::SResize: |
1591 | return southResizeCursor(); |
1592 | case CursorType::SEResize: |
1593 | return southEastResizeCursor(); |
1594 | case CursorType::SWResize: |
1595 | return southWestResizeCursor(); |
1596 | case CursorType::WResize: |
1597 | return westResizeCursor(); |
1598 | case CursorType::EWResize: |
1599 | return eastWestResizeCursor(); |
1600 | case CursorType::NSResize: |
1601 | return northSouthResizeCursor(); |
1602 | case CursorType::NESWResize: |
1603 | return northEastSouthWestResizeCursor(); |
1604 | case CursorType::NWSEResize: |
1605 | return northWestSouthEastResizeCursor(); |
1606 | case CursorType::ColumnResize: |
1607 | return columnResizeCursor(); |
1608 | case CursorType::RowResize: |
1609 | return rowResizeCursor(); |
1610 | case CursorType::AllScroll: |
1611 | return moveCursor(); |
1612 | case CursorType::ZoomIn: |
1613 | return zoomInCursor(); |
1614 | case CursorType::ZoomOut: |
1615 | return zoomOutCursor(); |
1616 | } |
1617 | return pointerCursor(); |
1618 | } |
1619 | #endif // ENABLE(CURSOR_SUPPORT) |
1620 | |
1621 | #if ENABLE(CURSOR_VISIBILITY) |
1622 | void EventHandler::startAutoHideCursorTimer() |
1623 | { |
1624 | Page* page = m_frame.page(); |
1625 | if (!page) |
1626 | return; |
1627 | |
1628 | m_autoHideCursorTimer.startOneShot(page->settings().timeWithoutMouseMovementBeforeHidingControls()); |
1629 | |
1630 | #if !ENABLE(IOS_TOUCH_EVENTS) |
1631 | // The fake mouse move event screws up the auto-hide feature (by resetting the auto-hide timer) |
1632 | // so cancel any pending fake mouse moves. |
1633 | if (m_fakeMouseMoveEventTimer.isActive()) |
1634 | m_fakeMouseMoveEventTimer.stop(); |
1635 | #endif |
1636 | } |
1637 | |
1638 | void EventHandler::cancelAutoHideCursorTimer() |
1639 | { |
1640 | if (m_autoHideCursorTimer.isActive()) |
1641 | m_autoHideCursorTimer.stop(); |
1642 | } |
1643 | |
1644 | void EventHandler::autoHideCursorTimerFired() |
1645 | { |
1646 | FrameView* view = m_frame.view(); |
1647 | if (!view || !view->isActive()) |
1648 | return; |
1649 | |
1650 | if (auto page = m_frame.page()) |
1651 | page->chrome().setCursorHiddenUntilMouseMoves(true); |
1652 | } |
1653 | #endif |
1654 | |
1655 | static LayoutPoint documentPointForWindowPoint(Frame& frame, const IntPoint& windowPoint) |
1656 | { |
1657 | FrameView* view = frame.view(); |
1658 | // FIXME: Is it really OK to use the wrong coordinates here when view is 0? |
1659 | // Historically the code would just crash; this is clearly no worse than that. |
1660 | return view ? view->windowToContents(windowPoint) : windowPoint; |
1661 | } |
1662 | |
1663 | static Scrollbar* scrollbarForMouseEvent(const MouseEventWithHitTestResults& mouseEvent, FrameView* view) |
1664 | { |
1665 | if (view) { |
1666 | if (auto* scrollbar = view->scrollbarAtPoint(mouseEvent.event().position())) |
1667 | return scrollbar; |
1668 | } |
1669 | return mouseEvent.scrollbar(); |
1670 | |
1671 | } |
1672 | |
1673 | bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& platformMouseEvent) |
1674 | { |
1675 | Ref<Frame> protectedFrame(m_frame); |
1676 | RefPtr<FrameView> protector(m_frame.view()); |
1677 | |
1678 | if (InspectorInstrumentation::handleMousePress(m_frame)) { |
1679 | invalidateClick(); |
1680 | return true; |
1681 | } |
1682 | |
1683 | #if ENABLE(POINTER_LOCK) |
1684 | if (m_frame.page()->pointerLockController().isLocked()) { |
1685 | m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousedownEvent); |
1686 | return true; |
1687 | } |
1688 | #endif |
1689 | |
1690 | if (m_frame.page()->pageOverlayController().handleMouseEvent(platformMouseEvent)) |
1691 | return true; |
1692 | |
1693 | #if ENABLE(TOUCH_EVENTS) |
1694 | bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent); |
1695 | if (defaultPrevented) |
1696 | return true; |
1697 | #endif |
1698 | |
1699 | UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); |
1700 | |
1701 | // FIXME (bug 68185): this call should be made at another abstraction layer |
1702 | m_frame.loader().resetMultipleFormSubmissionProtection(); |
1703 | |
1704 | #if !ENABLE(IOS_TOUCH_EVENTS) |
1705 | cancelFakeMouseMoveEvent(); |
1706 | #endif |
1707 | m_mousePressed = true; |
1708 | m_capturesDragging = true; |
1709 | setLastKnownMousePosition(platformMouseEvent); |
1710 | m_mouseDownTimestamp = platformMouseEvent.timestamp(); |
1711 | #if ENABLE(DRAG_SUPPORT) |
1712 | m_mouseDownMayStartDrag = false; |
1713 | #endif |
1714 | m_mouseDownMayStartSelect = false; |
1715 | m_mouseDownMayStartAutoscroll = false; |
1716 | if (FrameView* view = m_frame.view()) |
1717 | m_mouseDownPos = view->windowToContents(platformMouseEvent.position()); |
1718 | else { |
1719 | invalidateClick(); |
1720 | return false; |
1721 | } |
1722 | m_mouseDownWasInSubframe = false; |
1723 | |
1724 | HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent); |
1725 | // Save the document point we generate in case the window coordinate is invalidated by what happens |
1726 | // when we dispatch the event. |
1727 | LayoutPoint documentPoint = documentPointForWindowPoint(m_frame, platformMouseEvent.position()); |
1728 | MouseEventWithHitTestResults mouseEvent = m_frame.document()->prepareMouseEvent(request, documentPoint, platformMouseEvent); |
1729 | |
1730 | if (!mouseEvent.targetNode()) { |
1731 | invalidateClick(); |
1732 | return false; |
1733 | } |
1734 | |
1735 | m_mousePressNode = mouseEvent.targetNode(); |
1736 | m_frame.document()->setFocusNavigationStartingNode(mouseEvent.targetNode()); |
1737 | |
1738 | Scrollbar* scrollbar = scrollbarForMouseEvent(mouseEvent, m_frame.view()); |
1739 | updateLastScrollbarUnderMouse(scrollbar, SetOrClearLastScrollbar::Set); |
1740 | bool passedToScrollbar = scrollbar && passMousePressEventToScrollbar(mouseEvent, scrollbar); |
1741 | |
1742 | if (!passedToScrollbar) { |
1743 | RefPtr<Frame> subframe = subframeForHitTestResult(mouseEvent); |
1744 | if (subframe && passMousePressEventToSubframe(mouseEvent, subframe.get())) { |
1745 | // Start capturing future events for this frame. We only do this if we didn't clear |
1746 | // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop. |
1747 | m_capturesDragging = subframe->eventHandler().capturesDragging(); |
1748 | if (m_mousePressed && m_capturesDragging) { |
1749 | m_capturingMouseEventsElement = subframe->ownerElement(); |
1750 | m_eventHandlerWillResetCapturingMouseEventsElement = true; |
1751 | } |
1752 | invalidateClick(); |
1753 | return true; |
1754 | } |
1755 | } |
1756 | |
1757 | #if ENABLE(PAN_SCROLLING) |
1758 | // We store whether pan scrolling is in progress before calling stopAutoscrollTimer() |
1759 | // because it will set m_autoscrollType to NoAutoscroll on return. |
1760 | bool isPanScrollInProgress = m_frame.mainFrame().eventHandler().panScrollInProgress(); |
1761 | stopAutoscrollTimer(); |
1762 | if (isPanScrollInProgress) { |
1763 | // We invalidate the click when exiting pan scrolling so that we don't inadvertently navigate |
1764 | // away from the current page (e.g. the click was on a hyperlink). See <rdar://problem/6095023>. |
1765 | invalidateClick(); |
1766 | return true; |
1767 | } |
1768 | #endif |
1769 | |
1770 | m_clickCount = platformMouseEvent.clickCount(); |
1771 | m_clickNode = mouseEvent.targetNode(); |
1772 | |
1773 | if (!m_clickNode) { |
1774 | invalidateClick(); |
1775 | return false; |
1776 | } |
1777 | |
1778 | if (FrameView* view = m_frame.view()) { |
1779 | RenderLayer* layer = m_clickNode->renderer() ? m_clickNode->renderer()->enclosingLayer() : 0; |
1780 | IntPoint p = view->windowToContents(platformMouseEvent.position()); |
1781 | if (layer && layer->isPointInResizeControl(p)) { |
1782 | layer->setInResizeMode(true); |
1783 | m_resizeLayer = layer; |
1784 | m_offsetFromResizeCorner = layer->offsetFromResizeCorner(p); |
1785 | invalidateClick(); |
1786 | return true; |
1787 | } |
1788 | } |
1789 | |
1790 | m_frame.selection().setCaretBlinkingSuspended(true); |
1791 | |
1792 | bool swallowEvent = !dispatchMouseEvent(eventNames().mousedownEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true); |
1793 | m_capturesDragging = !swallowEvent || mouseEvent.scrollbar(); |
1794 | |
1795 | // If the hit testing originally determined the event was in a scrollbar, refetch the MouseEventWithHitTestResults |
1796 | // in case the scrollbar widget was destroyed when the mouse event was handled. |
1797 | if (mouseEvent.scrollbar()) { |
1798 | const bool wasLastScrollBar = mouseEvent.scrollbar() == m_lastScrollbarUnderMouse; |
1799 | mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent); |
1800 | if (wasLastScrollBar && mouseEvent.scrollbar() != m_lastScrollbarUnderMouse) |
1801 | m_lastScrollbarUnderMouse = nullptr; |
1802 | } |
1803 | |
1804 | if (!swallowEvent) { |
1805 | // Refetch the event target node if it currently is the shadow node inside an <input> element. |
1806 | // If a mouse event handler changes the input element type to one that has a widget associated, |
1807 | // we'd like to EventHandler::handleMousePressEvent to pass the event to the widget and thus the |
1808 | // event target node can't still be the shadow node. |
1809 | if (is<ShadowRoot>(*mouseEvent.targetNode()) && is<HTMLInputElement>(*downcast<ShadowRoot>(*mouseEvent.targetNode()).host())) |
1810 | mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent); |
1811 | } |
1812 | |
1813 | if (!swallowEvent) { |
1814 | if (passedToScrollbar) |
1815 | swallowEvent = true; |
1816 | else |
1817 | swallowEvent = handleMousePressEvent(mouseEvent); |
1818 | } |
1819 | return swallowEvent; |
1820 | } |
1821 | |
1822 | // This method only exists for platforms that don't know how to deliver |
1823 | bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& platformMouseEvent) |
1824 | { |
1825 | Ref<Frame> protectedFrame(m_frame); |
1826 | RefPtr<FrameView> protector(m_frame.view()); |
1827 | |
1828 | m_frame.selection().setCaretBlinkingSuspended(false); |
1829 | |
1830 | UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); |
1831 | |
1832 | #if ENABLE(POINTER_LOCK) |
1833 | if (m_frame.page()->pointerLockController().isLocked()) { |
1834 | m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent); |
1835 | return true; |
1836 | } |
1837 | #endif |
1838 | |
1839 | // We get this instead of a second mouse-up |
1840 | m_mousePressed = false; |
1841 | setLastKnownMousePosition(platformMouseEvent); |
1842 | |
1843 | HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent); |
1844 | MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); |
1845 | Frame* subframe = subframeForHitTestResult(mouseEvent); |
1846 | if (m_eventHandlerWillResetCapturingMouseEventsElement) |
1847 | m_capturingMouseEventsElement = nullptr; |
1848 | if (subframe && passMousePressEventToSubframe(mouseEvent, subframe)) |
1849 | return true; |
1850 | |
1851 | m_clickCount = platformMouseEvent.clickCount(); |
1852 | bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false); |
1853 | |
1854 | bool swallowClickEvent = platformMouseEvent.button() != RightButton && mouseEvent.targetNode() == m_clickNode && !dispatchMouseEvent(eventNames().clickEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true); |
1855 | |
1856 | if (m_lastScrollbarUnderMouse) |
1857 | swallowMouseUpEvent = m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent); |
1858 | |
1859 | bool swallowMouseReleaseEvent = !swallowMouseUpEvent && handleMouseReleaseEvent(mouseEvent); |
1860 | |
1861 | invalidateClick(); |
1862 | |
1863 | return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent; |
1864 | } |
1865 | |
1866 | static ScrollableArea* enclosingScrollableArea(Node* node) |
1867 | { |
1868 | for (auto ancestor = node; ancestor; ancestor = ancestor->parentOrShadowHostNode()) { |
1869 | if (is<HTMLIFrameElement>(*ancestor) || is<HTMLHtmlElement>(*ancestor) || is<HTMLDocument>(*ancestor)) |
1870 | return nullptr; |
1871 | |
1872 | auto renderer = ancestor->renderer(); |
1873 | if (!renderer) |
1874 | continue; |
1875 | |
1876 | if (is<RenderListBox>(*renderer)) |
1877 | return downcast<RenderListBox>(renderer); |
1878 | |
1879 | return renderer->enclosingLayer(); |
1880 | } |
1881 | |
1882 | return nullptr; |
1883 | } |
1884 | |
1885 | bool EventHandler::mouseMoved(const PlatformMouseEvent& event) |
1886 | { |
1887 | Ref<Frame> protectedFrame(m_frame); |
1888 | RefPtr<FrameView> protector(m_frame.view()); |
1889 | MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration); |
1890 | |
1891 | if (m_frame.page() && m_frame.page()->pageOverlayController().handleMouseEvent(event)) |
1892 | return true; |
1893 | |
1894 | HitTestResult hoveredNode = HitTestResult(LayoutPoint()); |
1895 | bool result = handleMouseMoveEvent(event, &hoveredNode); |
1896 | |
1897 | Page* page = m_frame.page(); |
1898 | if (!page) |
1899 | return result; |
1900 | |
1901 | if (auto scrolledArea = enclosingScrollableArea(hoveredNode.innerNode())) { |
1902 | if (FrameView* frameView = m_frame.view()) { |
1903 | if (frameView->containsScrollableArea(scrolledArea)) |
1904 | scrolledArea->mouseMovedInContentArea(); |
1905 | } |
1906 | } |
1907 | |
1908 | if (FrameView* frameView = m_frame.view()) |
1909 | frameView->mouseMovedInContentArea(); |
1910 | |
1911 | hoveredNode.setToNonUserAgentShadowAncestor(); |
1912 | page->chrome().mouseDidMoveOverElement(hoveredNode, event.modifierFlags()); |
1913 | page->chrome().setToolTip(hoveredNode); |
1914 | return result; |
1915 | } |
1916 | |
1917 | bool EventHandler::passMouseMovedEventToScrollbars(const PlatformMouseEvent& event) |
1918 | { |
1919 | HitTestResult hoveredNode; |
1920 | return handleMouseMoveEvent(event, &hoveredNode, true); |
1921 | } |
1922 | |
1923 | bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& platformMouseEvent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars) |
1924 | { |
1925 | #if ENABLE(TOUCH_EVENTS) |
1926 | bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent); |
1927 | if (defaultPrevented) |
1928 | return true; |
1929 | #endif |
1930 | |
1931 | Ref<Frame> protectedFrame(m_frame); |
1932 | RefPtr<FrameView> protector(m_frame.view()); |
1933 | |
1934 | #if ENABLE(POINTER_LOCK) |
1935 | if (m_frame.page()->pointerLockController().isLocked()) { |
1936 | m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousemoveEvent); |
1937 | return true; |
1938 | } |
1939 | #endif |
1940 | |
1941 | setLastKnownMousePosition(platformMouseEvent); |
1942 | |
1943 | if (m_hoverTimer.isActive()) |
1944 | m_hoverTimer.stop(); |
1945 | |
1946 | #if ENABLE(CURSOR_SUPPORT) |
1947 | m_cursorUpdateTimer.stop(); |
1948 | #endif |
1949 | |
1950 | #if !ENABLE(IOS_TOUCH_EVENTS) |
1951 | cancelFakeMouseMoveEvent(); |
1952 | #endif |
1953 | |
1954 | if (m_svgPan) { |
1955 | downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition)); |
1956 | return true; |
1957 | } |
1958 | |
1959 | if (m_frameSetBeingResized) |
1960 | return !dispatchMouseEvent(eventNames().mousemoveEvent, m_frameSetBeingResized.get(), false, 0, platformMouseEvent, false); |
1961 | |
1962 | // On iOS, our scrollbars are managed by UIKit. |
1963 | #if !PLATFORM(IOS_FAMILY) |
1964 | // Send events right to a scrollbar if the mouse is pressed. |
1965 | if (m_lastScrollbarUnderMouse && m_mousePressed) |
1966 | return m_lastScrollbarUnderMouse->mouseMoved(platformMouseEvent); |
1967 | #endif |
1968 | |
1969 | HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::AllowFrameScrollbars; |
1970 | if (m_mousePressed) |
1971 | hitType |= HitTestRequest::Active; |
1972 | else if (onlyUpdateScrollbars) { |
1973 | // Mouse events should be treated as "read-only" if we're updating only scrollbars. This |
1974 | // means that :hover and :active freeze in the state they were in, rather than updating |
1975 | // for nodes the mouse moves while the window is not key (which will be the case if |
1976 | // onlyUpdateScrollbars is true). |
1977 | hitType |= HitTestRequest::ReadOnly; |
1978 | } |
1979 | |
1980 | #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) |
1981 | // Treat any mouse move events as readonly if the user is currently touching the screen. |
1982 | if (m_touchPressed) |
1983 | hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly; |
1984 | #endif |
1985 | HitTestRequest request(hitType); |
1986 | MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); |
1987 | if (hoveredNode) |
1988 | *hoveredNode = mouseEvent.hitTestResult(); |
1989 | |
1990 | if (m_resizeLayer && m_resizeLayer->inResizeMode()) |
1991 | m_resizeLayer->resize(platformMouseEvent, m_offsetFromResizeCorner); |
1992 | else { |
1993 | Scrollbar* scrollbar = mouseEvent.scrollbar(); |
1994 | updateLastScrollbarUnderMouse(scrollbar, m_mousePressed ? SetOrClearLastScrollbar::Clear : SetOrClearLastScrollbar::Set); |
1995 | |
1996 | // On iOS, our scrollbars are managed by UIKit. |
1997 | #if !PLATFORM(IOS_FAMILY) |
1998 | if (!m_mousePressed && scrollbar) |
1999 | scrollbar->mouseMoved(platformMouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. |
2000 | #endif |
2001 | if (onlyUpdateScrollbars) { |
2002 | if (shouldSendMouseEventsToInactiveWindows()) |
2003 | updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, FireMouseOverOut::Yes); |
2004 | |
2005 | return true; |
2006 | } |
2007 | } |
2008 | |
2009 | bool swallowEvent = false; |
2010 | RefPtr<Frame> newSubframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent); |
2011 | |
2012 | // We want mouseouts to happen first, from the inside out. First send a move event to the last subframe so that it will fire mouseouts. |
2013 | if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree().isDescendantOf(&m_frame) && m_lastMouseMoveEventSubframe != newSubframe) |
2014 | passMouseMoveEventToSubframe(mouseEvent, m_lastMouseMoveEventSubframe.get()); |
2015 | |
2016 | if (newSubframe) { |
2017 | // Update over/out state before passing the event to the subframe. |
2018 | updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, FireMouseOverOut::Yes); |
2019 | |
2020 | // Event dispatch in updateMouseEventTargetNode may have caused the subframe of the target |
2021 | // node to be detached from its FrameView, in which case the event should not be passed. |
2022 | if (newSubframe->view()) |
2023 | swallowEvent |= passMouseMoveEventToSubframe(mouseEvent, newSubframe.get(), hoveredNode); |
2024 | } |
2025 | |
2026 | if (!newSubframe || mouseEvent.scrollbar()) { |
2027 | #if ENABLE(CURSOR_SUPPORT) |
2028 | if (auto* view = m_frame.view()) |
2029 | updateCursor(*view, mouseEvent.hitTestResult(), platformMouseEvent.shiftKey()); |
2030 | #endif |
2031 | } |
2032 | |
2033 | m_lastMouseMoveEventSubframe = newSubframe; |
2034 | |
2035 | if (swallowEvent) |
2036 | return true; |
2037 | |
2038 | swallowEvent = !dispatchMouseEvent(eventNames().mousemoveEvent, mouseEvent.targetNode(), false, 0, platformMouseEvent, true); |
2039 | #if ENABLE(DRAG_SUPPORT) |
2040 | if (!swallowEvent) |
2041 | swallowEvent = handleMouseDraggedEvent(mouseEvent); |
2042 | #endif |
2043 | |
2044 | return swallowEvent; |
2045 | } |
2046 | |
2047 | bool EventHandler::shouldSendMouseEventsToInactiveWindows() const |
2048 | { |
2049 | #if PLATFORM(GTK) |
2050 | return true; |
2051 | #endif |
2052 | return false; |
2053 | } |
2054 | |
2055 | void EventHandler::invalidateClick() |
2056 | { |
2057 | m_clickCount = 0; |
2058 | m_clickNode = nullptr; |
2059 | } |
2060 | |
2061 | static Node* targetNodeForClickEvent(Node* mousePressNode, Node* mouseReleaseNode) |
2062 | { |
2063 | if (!mousePressNode || !mouseReleaseNode) |
2064 | return nullptr; |
2065 | |
2066 | if (mousePressNode == mouseReleaseNode) |
2067 | return mouseReleaseNode; |
2068 | |
2069 | // If mousePressNode and mouseReleaseNode differ, we should fire the event at their common ancestor if there is one. |
2070 | if (&mousePressNode->document() == &mouseReleaseNode->document()) { |
2071 | if (auto* commonAncestor = Range::commonAncestorContainer(mousePressNode, mouseReleaseNode)) |
2072 | return commonAncestor; |
2073 | } |
2074 | |
2075 | Element* mouseReleaseShadowHost = mouseReleaseNode->shadowHost(); |
2076 | if (mouseReleaseShadowHost && mouseReleaseShadowHost == mousePressNode->shadowHost()) { |
2077 | // We want to dispatch the click to the shadow tree host element to give listeners the illusion that the |
2078 | // shadom tree is a single element. For example, we want to give the illusion that <input type="range"> |
2079 | // is a single element even though it is a composition of multiple shadom tree elements. |
2080 | return mouseReleaseShadowHost; |
2081 | } |
2082 | return nullptr; |
2083 | } |
2084 | |
2085 | bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& platformMouseEvent) |
2086 | { |
2087 | Ref<Frame> protectedFrame(m_frame); |
2088 | RefPtr<FrameView> protector(m_frame.view()); |
2089 | |
2090 | m_frame.selection().setCaretBlinkingSuspended(false); |
2091 | |
2092 | #if ENABLE(POINTER_LOCK) |
2093 | if (m_frame.page()->pointerLockController().isLocked()) { |
2094 | m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent); |
2095 | return true; |
2096 | } |
2097 | #endif |
2098 | |
2099 | if (m_frame.page()->pageOverlayController().handleMouseEvent(platformMouseEvent)) |
2100 | return true; |
2101 | |
2102 | #if ENABLE(TOUCH_EVENTS) |
2103 | bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent); |
2104 | if (defaultPrevented) |
2105 | return true; |
2106 | #endif |
2107 | |
2108 | UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); |
2109 | |
2110 | #if ENABLE(PAN_SCROLLING) |
2111 | m_autoscrollController->handleMouseReleaseEvent(platformMouseEvent); |
2112 | #endif |
2113 | |
2114 | m_mousePressed = false; |
2115 | setLastKnownMousePosition(platformMouseEvent); |
2116 | |
2117 | if (m_svgPan) { |
2118 | m_svgPan = false; |
2119 | downcast<SVGDocument>(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition)); |
2120 | return true; |
2121 | } |
2122 | |
2123 | if (m_frameSetBeingResized) |
2124 | return !dispatchMouseEvent(eventNames().mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, platformMouseEvent, false); |
2125 | |
2126 | // If an immediate action began or was completed using this series of mouse events, then we should send mouseup to |
2127 | // the DOM and return now so that we don't perform our own default behaviors. |
2128 | if (m_immediateActionStage == ImmediateActionStage::ActionCompleted || m_immediateActionStage == ImmediateActionStage::ActionUpdated || m_immediateActionStage == ImmediateActionStage::ActionCancelledAfterUpdate) { |
2129 | m_immediateActionStage = ImmediateActionStage::None; |
2130 | return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), true, m_clickCount, platformMouseEvent, false); |
2131 | } |
2132 | m_immediateActionStage = ImmediateActionStage::None; |
2133 | |
2134 | if (m_lastScrollbarUnderMouse) { |
2135 | invalidateClick(); |
2136 | m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent); |
2137 | bool cancelable = true; |
2138 | bool setUnder = false; |
2139 | return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), cancelable, m_clickCount, platformMouseEvent, setUnder); |
2140 | } |
2141 | |
2142 | HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent); |
2143 | MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); |
2144 | Frame* subframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent); |
2145 | if (m_eventHandlerWillResetCapturingMouseEventsElement) |
2146 | m_capturingMouseEventsElement = nullptr; |
2147 | if (subframe && passMouseReleaseEventToSubframe(mouseEvent, subframe)) |
2148 | return true; |
2149 | |
2150 | bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false); |
2151 | |
2152 | bool = platformMouseEvent.button() == RightButton; |
2153 | |
2154 | Node* nodeToClick = targetNodeForClickEvent(m_clickNode.get(), mouseEvent.targetNode()); |
2155 | bool swallowClickEvent = m_clickCount > 0 && !contextMenuEvent && nodeToClick && !dispatchMouseEvent(eventNames().clickEvent, nodeToClick, true, m_clickCount, platformMouseEvent, true); |
2156 | |
2157 | if (m_resizeLayer) { |
2158 | m_resizeLayer->setInResizeMode(false); |
2159 | m_resizeLayer = nullptr; |
2160 | } |
2161 | |
2162 | bool swallowMouseReleaseEvent = false; |
2163 | if (!swallowMouseUpEvent) |
2164 | swallowMouseReleaseEvent = handleMouseReleaseEvent(mouseEvent); |
2165 | |
2166 | invalidateClick(); |
2167 | |
2168 | return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent; |
2169 | } |
2170 | |
2171 | #if ENABLE(MOUSE_FORCE_EVENTS) |
2172 | bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& event) |
2173 | { |
2174 | Ref<Frame> protectedFrame(m_frame); |
2175 | RefPtr<FrameView> protector(m_frame.view()); |
2176 | |
2177 | #if ENABLE(POINTER_LOCK) |
2178 | if (m_frame.page()->pointerLockController().isLocked()) { |
2179 | m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcechangedEvent); |
2180 | if (event.type() == PlatformEvent::MouseForceDown) |
2181 | m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcedownEvent); |
2182 | if (event.type() == PlatformEvent::MouseForceUp) |
2183 | m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforceupEvent); |
2184 | return true; |
2185 | } |
2186 | #endif |
2187 | |
2188 | setLastKnownMousePosition(event); |
2189 | |
2190 | HitTestRequest::HitTestRequestType hitType = HitTestRequest::DisallowUserAgentShadowContent; |
2191 | |
2192 | if (event.force()) |
2193 | hitType |= HitTestRequest::Active; |
2194 | |
2195 | HitTestRequest request(hitType); |
2196 | MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event); |
2197 | |
2198 | bool swallowedEvent = !dispatchMouseEvent(eventNames().webkitmouseforcechangedEvent, mouseEvent.targetNode(), false, 0, event, false); |
2199 | if (event.type() == PlatformEvent::MouseForceDown) |
2200 | swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforcedownEvent, mouseEvent.targetNode(), false, 0, event, false); |
2201 | if (event.type() == PlatformEvent::MouseForceUp) |
2202 | swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforceupEvent, mouseEvent.targetNode(), false, 0, event, false); |
2203 | |
2204 | return swallowedEvent; |
2205 | } |
2206 | #else |
2207 | bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& ) |
2208 | { |
2209 | return false; |
2210 | } |
2211 | #endif // #if ENABLE(MOUSE_FORCE_EVENTS) |
2212 | |
2213 | bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& platformMouseEvent) |
2214 | { |
2215 | // If the event was a middle click, attempt to copy global selection in after |
2216 | // the newly set caret position. |
2217 | // |
2218 | // This code is called from either the mouse up or mouse down handling. There |
2219 | // is some debate about when the global selection is pasted: |
2220 | // xterm: pastes on up. |
2221 | // GTK: pastes on down. |
2222 | // Qt: pastes on up. |
2223 | // Firefox: pastes on up. |
2224 | // Chromium: pastes on up. |
2225 | // |
2226 | // There is something of a webcompat angle to this well, as highlighted by |
2227 | // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on |
2228 | // down then the text is pasted just before the onclick handler runs and |
2229 | // clears the text box. So it's important this happens after the event |
2230 | // handlers have been fired. |
2231 | #if PLATFORM(GTK) |
2232 | if (platformMouseEvent.type() != PlatformEvent::MousePressed) |
2233 | return false; |
2234 | #else |
2235 | if (platformMouseEvent.type() != PlatformEvent::MouseReleased) |
2236 | return false; |
2237 | #endif |
2238 | |
2239 | if (!m_frame.page()) |
2240 | return false; |
2241 | Frame& focusFrame = m_frame.page()->focusController().focusedOrMainFrame(); |
2242 | // Do not paste here if the focus was moved somewhere else. |
2243 | if (&m_frame == &focusFrame && m_frame.editor().client()->supportsGlobalSelection()) |
2244 | return m_frame.editor().command("PasteGlobalSelection"_s ).execute(); |
2245 | |
2246 | return false; |
2247 | } |
2248 | |
2249 | #if ENABLE(DRAG_SUPPORT) |
2250 | |
2251 | bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dragTarget, const PlatformMouseEvent& event, DataTransfer& dataTransfer) |
2252 | { |
2253 | Ref<Frame> protectedFrame(m_frame); |
2254 | FrameView* view = m_frame.view(); |
2255 | |
2256 | // FIXME: We might want to dispatch a dragleave even if the view is gone. |
2257 | if (!view) |
2258 | return false; |
2259 | |
2260 | view->disableLayerFlushThrottlingTemporarilyForInteraction(); |
2261 | // FIXME: Use MouseEvent::create which takes PlatformMouseEvent. |
2262 | Ref<MouseEvent> me = MouseEvent::create(eventType, Event::CanBubble::Yes, Event::IsCancelable::Yes, Event::IsComposed::Yes, |
2263 | event.timestamp().approximateMonotonicTime(), &m_frame.windowProxy(), 0, |
2264 | event.globalPosition(), event.position(), |
2265 | #if ENABLE(POINTER_LOCK) |
2266 | event.movementDelta(), |
2267 | #else |
2268 | { }, |
2269 | #endif |
2270 | event.modifiers(), 0, 0, nullptr, event.force(), NoTap, &dataTransfer); |
2271 | |
2272 | dragTarget.dispatchEvent(me); |
2273 | return me->defaultPrevented(); |
2274 | } |
2275 | |
2276 | static bool targetIsFrame(Node* target, Frame*& frame) |
2277 | { |
2278 | if (!is<HTMLFrameElementBase>(target)) |
2279 | return false; |
2280 | |
2281 | frame = downcast<HTMLFrameElementBase>(*target).contentFrame(); |
2282 | return true; |
2283 | } |
2284 | |
2285 | static DragOperation convertDropZoneOperationToDragOperation(const String& dragOperation) |
2286 | { |
2287 | if (dragOperation == "copy" ) |
2288 | return DragOperationCopy; |
2289 | if (dragOperation == "move" ) |
2290 | return DragOperationMove; |
2291 | if (dragOperation == "link" ) |
2292 | return DragOperationLink; |
2293 | return DragOperationNone; |
2294 | } |
2295 | |
2296 | static String convertDragOperationToDropZoneOperation(DragOperation operation) |
2297 | { |
2298 | switch (operation) { |
2299 | case DragOperationCopy: |
2300 | return "copy"_s ; |
2301 | case DragOperationMove: |
2302 | return "move"_s ; |
2303 | case DragOperationLink: |
2304 | return "link"_s ; |
2305 | default: |
2306 | return "copy"_s ; |
2307 | } |
2308 | } |
2309 | |
2310 | static bool hasDropZoneType(DataTransfer& dataTransfer, const String& keyword) |
2311 | { |
2312 | if (keyword.startsWith("file:" )) |
2313 | return dataTransfer.hasFileOfType(keyword.substring(5)); |
2314 | |
2315 | if (keyword.startsWith("string:" )) |
2316 | return dataTransfer.hasStringOfType(keyword.substring(7)); |
2317 | |
2318 | return false; |
2319 | } |
2320 | |
2321 | static bool findDropZone(Node& target, DataTransfer& dataTransfer) |
2322 | { |
2323 | RefPtr<Element> element = is<Element>(target) ? &downcast<Element>(target) : target.parentElement(); |
2324 | for (; element; element = element->parentElement()) { |
2325 | SpaceSplitString keywords(element->attributeWithoutSynchronization(webkitdropzoneAttr), true); |
2326 | bool matched = false; |
2327 | DragOperation dragOperation = DragOperationNone; |
2328 | for (unsigned i = 0, size = keywords.size(); i < size; ++i) { |
2329 | DragOperation op = convertDropZoneOperationToDragOperation(keywords[i]); |
2330 | if (op != DragOperationNone) { |
2331 | if (dragOperation == DragOperationNone) |
2332 | dragOperation = op; |
2333 | } else |
2334 | matched = matched || hasDropZoneType(dataTransfer, keywords[i].string()); |
2335 | if (matched && dragOperation != DragOperationNone) |
2336 | break; |
2337 | } |
2338 | if (matched) { |
2339 | dataTransfer.setDropEffect(convertDragOperationToDropZoneOperation(dragOperation)); |
2340 | return true; |
2341 | } |
2342 | } |
2343 | return false; |
2344 | } |
2345 | |
2346 | EventHandler::DragTargetResponse EventHandler::dispatchDragEnterOrDragOverEvent(const AtomicString& eventType, Element& target, const PlatformMouseEvent& event, |
2347 | std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles) |
2348 | { |
2349 | auto dataTransfer = DataTransfer::createForUpdatingDropTarget(target.document(), WTFMove(pasteboard), sourceOperation, draggingFiles); |
2350 | bool accept = dispatchDragEvent(eventType, target, event, dataTransfer.get()); |
2351 | if (!accept) |
2352 | accept = findDropZone(target, dataTransfer); |
2353 | dataTransfer->makeInvalidForSecurity(); |
2354 | if (accept && !dataTransfer->dropEffectIsUninitialized()) |
2355 | return { true, dataTransfer->destinationOperation() }; |
2356 | return { accept, WTF::nullopt }; |
2357 | } |
2358 | |
2359 | EventHandler::DragTargetResponse EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, const std::function<std::unique_ptr<Pasteboard>()>& makePasteboard, DragOperation sourceOperation, bool draggingFiles) |
2360 | { |
2361 | Ref<Frame> protectedFrame(m_frame); |
2362 | if (!m_frame.view()) |
2363 | return { }; |
2364 | |
2365 | HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); |
2366 | MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event); |
2367 | |
2368 | RefPtr<Element> newTarget; |
2369 | if (Node* targetNode = mouseEvent.targetNode()) { |
2370 | // Drag events should never go to non-element nodes (following IE, and proper mouseover/out dispatch) |
2371 | if (!is<Element>(*targetNode)) |
2372 | newTarget = targetNode->parentOrShadowHostElement(); |
2373 | else |
2374 | newTarget = downcast<Element>(targetNode); |
2375 | } |
2376 | |
2377 | m_autoscrollController->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp()); |
2378 | |
2379 | DragTargetResponse response; |
2380 | if (m_dragTarget != newTarget) { |
2381 | // FIXME: this ordering was explicitly chosen to match WinIE. However, |
2382 | // it is sometimes incorrect when dragging within subframes, as seen with |
2383 | // LayoutTests/fast/events/drag-in-frames.html. |
2384 | // |
2385 | // 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>. |
2386 | Frame* targetFrame; |
2387 | if (targetIsFrame(newTarget.get(), targetFrame)) { |
2388 | if (targetFrame) |
2389 | response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles); |
2390 | } else if (newTarget) { |
2391 | // As per section 7.9.4 of the HTML 5 spec., we must always fire a drag event before firing a dragenter, dragleave, or dragover event. |
2392 | if (dragState().source && dragState().shouldDispatchEvents) |
2393 | dispatchDragSrcEvent(eventNames().dragEvent, event); |
2394 | response = dispatchDragEnterOrDragOverEvent(eventNames().dragenterEvent, *newTarget, event, makePasteboard(), sourceOperation, draggingFiles); |
2395 | } |
2396 | |
2397 | if (targetIsFrame(m_dragTarget.get(), targetFrame)) { |
2398 | // FIXME: Recursing again here doesn't make sense if the newTarget and m_dragTarget were in the same frame. |
2399 | if (targetFrame) |
2400 | response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles); |
2401 | } else if (m_dragTarget) { |
2402 | auto dataTransfer = DataTransfer::createForUpdatingDropTarget(m_dragTarget->document(), makePasteboard(), sourceOperation, draggingFiles); |
2403 | dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, dataTransfer.get()); |
2404 | dataTransfer->makeInvalidForSecurity(); |
2405 | } |
2406 | |
2407 | if (newTarget) { |
2408 | // We do not explicitly call dispatchDragEvent here because it could ultimately result in the appearance that |
2409 | // two dragover events fired. So, we mark that we should only fire a dragover event on the next call to this function. |
2410 | m_shouldOnlyFireDragOverEvent = true; |
2411 | } |
2412 | } else { |
2413 | Frame* targetFrame; |
2414 | if (targetIsFrame(newTarget.get(), targetFrame)) { |
2415 | if (targetFrame) |
2416 | response = targetFrame->eventHandler().updateDragAndDrop(event, makePasteboard, sourceOperation, draggingFiles); |
2417 | } else if (newTarget) { |
2418 | // Note, when dealing with sub-frames, we may need to fire only a dragover event as a drag event may have been fired earlier. |
2419 | if (!m_shouldOnlyFireDragOverEvent && dragState().source && dragState().shouldDispatchEvents) |
2420 | dispatchDragSrcEvent(eventNames().dragEvent, event); |
2421 | response = dispatchDragEnterOrDragOverEvent(eventNames().dragoverEvent, *newTarget, event, makePasteboard(), sourceOperation, draggingFiles); |
2422 | m_shouldOnlyFireDragOverEvent = false; |
2423 | } |
2424 | } |
2425 | m_dragTarget = WTFMove(newTarget); |
2426 | return response; |
2427 | } |
2428 | |
2429 | void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles) |
2430 | { |
2431 | Ref<Frame> protectedFrame(m_frame); |
2432 | |
2433 | Frame* targetFrame; |
2434 | if (targetIsFrame(m_dragTarget.get(), targetFrame)) { |
2435 | if (targetFrame) |
2436 | targetFrame->eventHandler().cancelDragAndDrop(event, WTFMove(pasteboard), sourceOperation, draggingFiles); |
2437 | } else if (m_dragTarget) { |
2438 | if (dragState().source && dragState().shouldDispatchEvents) |
2439 | dispatchDragSrcEvent(eventNames().dragEvent, event); |
2440 | |
2441 | auto dataTransfer = DataTransfer::createForUpdatingDropTarget(m_dragTarget->document(), WTFMove(pasteboard), sourceOperation, draggingFiles); |
2442 | dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, dataTransfer.get()); |
2443 | dataTransfer->makeInvalidForSecurity(); |
2444 | } |
2445 | clearDragState(); |
2446 | } |
2447 | |
2448 | bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, std::unique_ptr<Pasteboard>&& pasteboard, DragOperation sourceOperation, bool draggingFiles) |
2449 | { |
2450 | Ref<Frame> protectedFrame(m_frame); |
2451 | |
2452 | Frame* targetFrame; |
2453 | bool preventedDefault = false; |
2454 | if (targetIsFrame(m_dragTarget.get(), targetFrame)) { |
2455 | if (targetFrame) |
2456 | preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, WTFMove(pasteboard), sourceOperation, draggingFiles); |
2457 | } else if (m_dragTarget) { |
2458 | auto dataTransfer = DataTransfer::createForDrop(m_dragTarget->document(), WTFMove(pasteboard), sourceOperation, draggingFiles); |
2459 | preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, dataTransfer); |
2460 | dataTransfer->makeInvalidForSecurity(); |
2461 | } |
2462 | clearDragState(); |
2463 | return preventedDefault; |
2464 | } |
2465 | |
2466 | void EventHandler::clearDragState() |
2467 | { |
2468 | stopAutoscrollTimer(); |
2469 | m_dragTarget = nullptr; |
2470 | m_capturingMouseEventsElement = nullptr; |
2471 | m_shouldOnlyFireDragOverEvent = false; |
2472 | #if PLATFORM(COCOA) |
2473 | m_sendingEventToSubview = false; |
2474 | #endif |
2475 | } |
2476 | |
2477 | #endif // ENABLE(DRAG_SUPPORT) |
2478 | |
2479 | void EventHandler::setCapturingMouseEventsElement(Element* element) |
2480 | { |
2481 | m_capturingMouseEventsElement = element; |
2482 | m_eventHandlerWillResetCapturingMouseEventsElement = false; |
2483 | } |
2484 | |
2485 | MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mouseEvent) |
2486 | { |
2487 | Ref<Frame> protectedFrame(m_frame); |
2488 | ASSERT(m_frame.document()); |
2489 | return m_frame.document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mouseEvent.position()), mouseEvent); |
2490 | } |
2491 | |
2492 | static bool hierarchyHasCapturingEventListeners(Element* element, const AtomicString& pointerEventName, const AtomicString& compatibilityMouseEventName) |
2493 | { |
2494 | for (ContainerNode* curr = element; curr; curr = curr->parentInComposedTree()) { |
2495 | if (curr->hasCapturingEventListeners(pointerEventName) || curr->hasCapturingEventListeners(compatibilityMouseEventName)) |
2496 | return true; |
2497 | } |
2498 | return false; |
2499 | } |
2500 | |
2501 | void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& platformMouseEvent, FireMouseOverOut fireMouseOverOut) |
2502 | { |
2503 | Ref<Frame> protectedFrame(m_frame); |
2504 | Element* targetElement = nullptr; |
2505 | |
2506 | // If we're capturing, we always go right to that element. |
2507 | if (m_capturingMouseEventsElement) |
2508 | targetElement = m_capturingMouseEventsElement.get(); |
2509 | else if (targetNode) { |
2510 | // If the target node is a non-element, dispatch on the parent. <rdar://problem/4196646> |
2511 | while (targetNode && !is<Element>(*targetNode)) |
2512 | targetNode = targetNode->parentInComposedTree(); |
2513 | targetElement = downcast<Element>(targetNode); |
2514 | } |
2515 | |
2516 | m_elementUnderMouse = targetElement; |
2517 | |
2518 | // Fire mouseout/mouseover if the mouse has shifted to a different node. |
2519 | if (fireMouseOverOut == FireMouseOverOut::Yes) { |
2520 | auto scrollableAreaForLastNode = enclosingScrollableArea(m_lastElementUnderMouse.get()); |
2521 | auto scrollableAreaForNodeUnderMouse = enclosingScrollableArea(m_elementUnderMouse.get()); |
2522 | Page* page = m_frame.page(); |
2523 | |
2524 | if (m_lastElementUnderMouse && (!m_elementUnderMouse || &m_elementUnderMouse->document() != m_frame.document())) { |
2525 | // The mouse has moved between frames. |
2526 | if (Frame* frame = m_lastElementUnderMouse->document().frame()) { |
2527 | if (FrameView* frameView = frame->view()) |
2528 | frameView->mouseExitedContentArea(); |
2529 | } |
2530 | } else if (page && (scrollableAreaForLastNode && (!scrollableAreaForNodeUnderMouse || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) { |
2531 | // The mouse has moved between layers. |
2532 | if (Frame* frame = m_lastElementUnderMouse->document().frame()) { |
2533 | if (FrameView* frameView = frame->view()) { |
2534 | if (frameView->containsScrollableArea(scrollableAreaForLastNode)) |
2535 | scrollableAreaForLastNode->mouseExitedContentArea(); |
2536 | } |
2537 | } |
2538 | } |
2539 | |
2540 | if (m_elementUnderMouse && (!m_lastElementUnderMouse || &m_lastElementUnderMouse->document() != m_frame.document())) { |
2541 | // The mouse has moved between frames. |
2542 | if (Frame* frame = m_elementUnderMouse->document().frame()) { |
2543 | if (FrameView* frameView = frame->view()) |
2544 | frameView->mouseEnteredContentArea(); |
2545 | } |
2546 | } else if (page && (scrollableAreaForNodeUnderMouse && (!scrollableAreaForLastNode || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) { |
2547 | // The mouse has moved between layers. |
2548 | if (Frame* frame = m_elementUnderMouse->document().frame()) { |
2549 | if (FrameView* frameView = frame->view()) { |
2550 | if (frameView->containsScrollableArea(scrollableAreaForNodeUnderMouse)) |
2551 | scrollableAreaForNodeUnderMouse->mouseEnteredContentArea(); |
2552 | } |
2553 | } |
2554 | } |
2555 | |
2556 | if (m_lastElementUnderMouse && &m_lastElementUnderMouse->document() != m_frame.document()) { |
2557 | m_lastElementUnderMouse = nullptr; |
2558 | m_lastScrollbarUnderMouse = nullptr; |
2559 | } |
2560 | |
2561 | if (m_lastElementUnderMouse != m_elementUnderMouse) { |
2562 | // mouseenter and mouseleave events are only dispatched if there is a capturing eventhandler on an ancestor |
2563 | // or a normal eventhandler on the element itself (they don't bubble). |
2564 | // This optimization is necessary since these events can cause O(n^2) capturing event-handler checks. |
2565 | bool hasCapturingMouseEnterListener = hierarchyHasCapturingEventListeners(m_elementUnderMouse.get(), eventNames().pointerenterEvent, eventNames().mouseenterEvent); |
2566 | bool hasCapturingMouseLeaveListener = hierarchyHasCapturingEventListeners(m_lastElementUnderMouse.get(), eventNames().pointerleaveEvent, eventNames().mouseleaveEvent); |
2567 | |
2568 | Vector<Ref<Element>, 32> leftElementsChain; |
2569 | for (Element* element = m_lastElementUnderMouse.get(); element; element = element->parentElementInComposedTree()) |
2570 | leftElementsChain.append(*element); |
2571 | Vector<Ref<Element>, 32> enteredElementsChain; |
2572 | for (Element* element = m_elementUnderMouse.get(); element; element = element->parentElementInComposedTree()) |
2573 | enteredElementsChain.append(*element); |
2574 | |
2575 | if (!leftElementsChain.isEmpty() && !enteredElementsChain.isEmpty() && leftElementsChain.last().ptr() == enteredElementsChain.last().ptr()) { |
2576 | size_t minHeight = std::min(leftElementsChain.size(), enteredElementsChain.size()); |
2577 | size_t i; |
2578 | for (i = 0; i < minHeight; ++i) { |
2579 | if (leftElementsChain[leftElementsChain.size() - i - 1].ptr() != enteredElementsChain[enteredElementsChain.size() - i - 1].ptr()) |
2580 | break; |
2581 | } |
2582 | leftElementsChain.shrink(leftElementsChain.size() - i); |
2583 | enteredElementsChain.shrink(enteredElementsChain.size() - i); |
2584 | } |
2585 | |
2586 | if (m_lastElementUnderMouse) |
2587 | m_lastElementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoutEvent, 0, m_elementUnderMouse.get()); |
2588 | |
2589 | for (auto& chain : leftElementsChain) { |
2590 | if (hasCapturingMouseLeaveListener || chain->hasEventListeners(eventNames().pointerleaveEvent) || chain->hasEventListeners(eventNames().mouseleaveEvent)) |
2591 | chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseleaveEvent, 0, m_elementUnderMouse.get()); |
2592 | } |
2593 | |
2594 | if (m_elementUnderMouse) |
2595 | m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoverEvent, 0, m_lastElementUnderMouse.get()); |
2596 | |
2597 | for (auto& chain : enteredElementsChain) { |
2598 | if (hasCapturingMouseEnterListener || chain->hasEventListeners(eventNames().pointerenterEvent) || chain->hasEventListeners(eventNames().mouseenterEvent)) |
2599 | chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseenterEvent, 0, m_lastElementUnderMouse.get()); |
2600 | } |
2601 | } |
2602 | m_lastElementUnderMouse = m_elementUnderMouse; |
2603 | } |
2604 | } |
2605 | |
2606 | bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& platformMouseEvent, bool setUnder) |
2607 | { |
2608 | Ref<Frame> protectedFrame(m_frame); |
2609 | |
2610 | if (auto* view = m_frame.view()) |
2611 | view->disableLayerFlushThrottlingTemporarilyForInteraction(); |
2612 | |
2613 | updateMouseEventTargetNode(targetNode, platformMouseEvent, setUnder ? FireMouseOverOut::Yes : FireMouseOverOut::No); |
2614 | |
2615 | if (m_elementUnderMouse && !m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventType, clickCount)) |
2616 | return false; |
2617 | |
2618 | if (eventType != eventNames().mousedownEvent) |
2619 | return true; |
2620 | |
2621 | // If clicking on a frame scrollbar, do not make any change to which element is focused. |
2622 | auto* view = m_frame.view(); |
2623 | if (view && view->scrollbarAtPoint(platformMouseEvent.position())) |
2624 | return true; |
2625 | |
2626 | // The layout needs to be up to date to determine if an element is focusable. |
2627 | m_frame.document()->updateLayoutIgnorePendingStylesheets(); |
2628 | |
2629 | // Remove focus from the currently focused element when a link or button is clicked. |
2630 | // This is expected by some sites that rely on change event handlers running |
2631 | // from form fields before the button click is processed, behavior that was inherited |
2632 | // from the user interface of Windows, where pushing a button moves focus to the button. |
2633 | |
2634 | // Walk up the DOM tree to search for an element to focus. |
2635 | RefPtr<Element> element; |
2636 | for (element = m_elementUnderMouse.get(); element; element = element->parentElementInComposedTree()) { |
2637 | if (element->isMouseFocusable()) |
2638 | break; |
2639 | } |
2640 | |
2641 | // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus an |
2642 | // element on mouse down if it's selected and inside a focused element. It will be |
2643 | // focused if the user does a mouseup over it, however, because the mouseup |
2644 | // will set a selection inside it, which will also set the focused element. |
2645 | if (element && m_frame.selection().isRange()) { |
2646 | if (auto range = m_frame.selection().toNormalizedRange()) { |
2647 | auto result = range->compareNode(*element); |
2648 | if (!result.hasException() && result.releaseReturnValue() == Range::NODE_INSIDE && element->isDescendantOf(m_frame.document()->focusedElement())) |
2649 | return true; |
2650 | } |
2651 | } |
2652 | |
2653 | // Only change the focus when clicking scrollbars if it can be transferred to a mouse focusable node. |
2654 | if (!element && isInsideScrollbar(platformMouseEvent.position())) |
2655 | return false; |
2656 | |
2657 | // If focus shift is blocked, we eat the event. |
2658 | auto* page = m_frame.page(); |
2659 | if (page && !page->focusController().setFocusedElement(element.get(), m_frame)) |
2660 | return false; |
2661 | |
2662 | return true; |
2663 | } |
2664 | |
2665 | bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const |
2666 | { |
2667 | if (RenderView* renderView = m_frame.contentRenderer()) { |
2668 | HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); |
2669 | HitTestResult result(windowPoint); |
2670 | renderView->hitTest(request, result); |
2671 | return result.scrollbar(); |
2672 | } |
2673 | |
2674 | return false; |
2675 | } |
2676 | |
2677 | #if !USE(GLIB) |
2678 | |
2679 | bool EventHandler::shouldSwapScrollDirection(const HitTestResult&, const PlatformWheelEvent&) const |
2680 | { |
2681 | return false; |
2682 | } |
2683 | |
2684 | #endif |
2685 | |
2686 | #if !PLATFORM(MAC) |
2687 | |
2688 | void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent&, const HitTestResult&, RefPtr<Element>&, RefPtr<ContainerNode>&, WeakPtr<ScrollableArea>&, bool&) |
2689 | { |
2690 | } |
2691 | |
2692 | void EventHandler::platformRecordWheelEvent(const PlatformWheelEvent& event) |
2693 | { |
2694 | if (auto* page = m_frame.page()) |
2695 | page->wheelEventDeltaFilter()->updateFromDelta(FloatSize(event.deltaX(), event.deltaY())); |
2696 | } |
2697 | |
2698 | bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& event, ContainerNode*, const WeakPtr<ScrollableArea>&) |
2699 | { |
2700 | Ref<Frame> protectedFrame(m_frame); |
2701 | |
2702 | // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed. |
2703 | FrameView* view = m_frame.view(); |
2704 | |
2705 | bool didHandleEvent = view ? view->wheelEvent(event) : false; |
2706 | m_isHandlingWheelEvent = false; |
2707 | return didHandleEvent; |
2708 | } |
2709 | |
2710 | bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode*) |
2711 | { |
2712 | return true; |
2713 | } |
2714 | |
2715 | void EventHandler::platformNotifyIfEndGesture(const PlatformWheelEvent&, const WeakPtr<ScrollableArea>&) |
2716 | { |
2717 | } |
2718 | |
2719 | void EventHandler::clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent&) |
2720 | { |
2721 | clearLatchedState(); |
2722 | } |
2723 | |
2724 | #if !PLATFORM(IOS_FAMILY) |
2725 | |
2726 | IntPoint EventHandler::targetPositionInWindowForSelectionAutoscroll() const |
2727 | { |
2728 | return m_lastKnownMousePosition; |
2729 | } |
2730 | |
2731 | #endif // !PLATFORM(IOS_FAMILY) |
2732 | |
2733 | #endif // !PLATFORM(MAC) |
2734 | |
2735 | #if !PLATFORM(IOS_FAMILY) |
2736 | |
2737 | bool EventHandler::shouldUpdateAutoscroll() |
2738 | { |
2739 | return mousePressed(); |
2740 | } |
2741 | |
2742 | #endif // !PLATFORM(IOS_FAMILY) |
2743 | |
2744 | Widget* EventHandler::widgetForEventTarget(Element* eventTarget) |
2745 | { |
2746 | if (!eventTarget) |
2747 | return nullptr; |
2748 | |
2749 | auto* target = eventTarget->renderer(); |
2750 | if (!is<RenderWidget>(target)) |
2751 | return nullptr; |
2752 | |
2753 | return downcast<RenderWidget>(*target).widget(); |
2754 | } |
2755 | |
2756 | static WeakPtr<Widget> widgetForElement(const Element& element) |
2757 | { |
2758 | auto target = element.renderer(); |
2759 | if (!is<RenderWidget>(target) || !downcast<RenderWidget>(*target).widget()) |
2760 | return { }; |
2761 | |
2762 | return makeWeakPtr(*downcast<RenderWidget>(*target).widget()); |
2763 | } |
2764 | |
2765 | bool EventHandler::completeWidgetWheelEvent(const PlatformWheelEvent& event, const WeakPtr<Widget>& widget, const WeakPtr<ScrollableArea>& scrollableArea, ContainerNode* scrollableContainer) |
2766 | { |
2767 | m_isHandlingWheelEvent = false; |
2768 | |
2769 | // We do another check on the widget because the event handler can run JS which results in the frame getting destroyed. |
2770 | if (!widget) |
2771 | return false; |
2772 | |
2773 | if (scrollableArea) |
2774 | scrollableArea->setScrollShouldClearLatchedState(false); |
2775 | |
2776 | platformNotifyIfEndGesture(event, scrollableArea); |
2777 | |
2778 | if (!widget->platformWidget()) |
2779 | return true; |
2780 | |
2781 | return platformCompletePlatformWidgetWheelEvent(event, *widget.get(), scrollableContainer); |
2782 | } |
2783 | |
2784 | bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event) |
2785 | { |
2786 | RenderView* renderView = m_frame.contentRenderer(); |
2787 | if (!renderView) |
2788 | return false; |
2789 | |
2790 | Ref<Frame> protectedFrame(m_frame); |
2791 | RefPtr<FrameView> protector(m_frame.view()); |
2792 | |
2793 | FrameView* view = m_frame.view(); |
2794 | if (!view) |
2795 | return false; |
2796 | |
2797 | #if ENABLE(POINTER_LOCK) |
2798 | if (m_frame.page()->pointerLockController().isLocked()) { |
2799 | m_frame.page()->pointerLockController().dispatchLockedWheelEvent(event); |
2800 | return true; |
2801 | } |
2802 | #endif |
2803 | |
2804 | m_isHandlingWheelEvent = true; |
2805 | setFrameWasScrolledByUser(); |
2806 | |
2807 | HitTestRequest request; |
2808 | HitTestResult result(view->windowToContents(event.position())); |
2809 | renderView->hitTest(request, result); |
2810 | |
2811 | RefPtr<Element> element = result.targetElement(); |
2812 | RefPtr<ContainerNode> scrollableContainer; |
2813 | WeakPtr<ScrollableArea> scrollableArea; |
2814 | bool isOverWidget = result.isOverWidget(); |
2815 | platformPrepareForWheelEvents(event, result, element, scrollableContainer, scrollableArea, isOverWidget); |
2816 | |
2817 | #if PLATFORM(MAC) |
2818 | if (event.phase() == PlatformWheelEventPhaseNone && event.momentumPhase() == PlatformWheelEventPhaseNone && m_frame.page()) |
2819 | m_frame.page()->resetLatchingState(); |
2820 | #endif |
2821 | |
2822 | // FIXME: It should not be necessary to do this mutation here. |
2823 | // Instead, the handlers should know convert vertical scrolls appropriately. |
2824 | PlatformWheelEvent adjustedEvent = shouldSwapScrollDirection(result, event) ? event.copySwappingDirection() : event; |
2825 | platformRecordWheelEvent(adjustedEvent); |
2826 | |
2827 | if (element) { |
2828 | if (isOverWidget) { |
2829 | if (WeakPtr<Widget> widget = widgetForElement(*element)) { |
2830 | if (widgetDidHandleWheelEvent(event, *widget.get())) |
2831 | return completeWidgetWheelEvent(adjustedEvent, widget, scrollableArea, scrollableContainer.get()); |
2832 | } |
2833 | } |
2834 | |
2835 | if (!element->dispatchWheelEvent(adjustedEvent)) { |
2836 | m_isHandlingWheelEvent = false; |
2837 | if (scrollableArea && scrollableArea->scrollShouldClearLatchedState()) { |
2838 | // Web developer is controlling scrolling, so don't attempt to latch. |
2839 | clearLatchedState(); |
2840 | scrollableArea->setScrollShouldClearLatchedState(false); |
2841 | } |
2842 | |
2843 | platformNotifyIfEndGesture(adjustedEvent, scrollableArea); |
2844 | return true; |
2845 | } |
2846 | } |
2847 | |
2848 | if (scrollableArea) |
2849 | scrollableArea->setScrollShouldClearLatchedState(false); |
2850 | |
2851 | bool handledEvent = platformCompleteWheelEvent(adjustedEvent, scrollableContainer.get(), scrollableArea); |
2852 | platformNotifyIfEndGesture(adjustedEvent, scrollableArea); |
2853 | return handledEvent; |
2854 | } |
2855 | |
2856 | void EventHandler::clearLatchedState() |
2857 | { |
2858 | auto* page = m_frame.page(); |
2859 | if (!page) |
2860 | return; |
2861 | |
2862 | #if PLATFORM(MAC) |
2863 | page->resetLatchingState(); |
2864 | #endif |
2865 | if (auto filter = page->wheelEventDeltaFilter()) |
2866 | filter->endFilteringDeltas(); |
2867 | } |
2868 | |
2869 | void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent& wheelEvent) |
2870 | { |
2871 | if (!startNode) |
2872 | return; |
2873 | |
2874 | auto protectedFrame = makeRef(m_frame); |
2875 | |
2876 | FloatSize filteredPlatformDelta(wheelEvent.deltaX(), wheelEvent.deltaY()); |
2877 | FloatSize filteredVelocity; |
2878 | if (auto platformWheelEvent = wheelEvent.underlyingPlatformEvent()) { |
2879 | filteredPlatformDelta.setWidth(platformWheelEvent->deltaX()); |
2880 | filteredPlatformDelta.setHeight(platformWheelEvent->deltaY()); |
2881 | } |
2882 | |
2883 | #if PLATFORM(MAC) |
2884 | ScrollLatchingState* latchedState = m_frame.page() ? m_frame.page()->latchingState() : nullptr; |
2885 | Element* stopElement = latchedState ? latchedState->previousWheelScrolledElement() : nullptr; |
2886 | |
2887 | if (m_frame.page() && m_frame.page()->wheelEventDeltaFilter()->isFilteringDeltas()) { |
2888 | filteredPlatformDelta = m_frame.page()->wheelEventDeltaFilter()->filteredDelta(); |
2889 | filteredVelocity = m_frame.page()->wheelEventDeltaFilter()->filteredVelocity(); |
2890 | } |
2891 | #else |
2892 | Element* stopElement = nullptr; |
2893 | #endif |
2894 | |
2895 | if (handleWheelEventInAppropriateEnclosingBox(startNode, wheelEvent, &stopElement, filteredPlatformDelta, filteredVelocity)) |
2896 | wheelEvent.setDefaultHandled(); |
2897 | |
2898 | #if PLATFORM(MAC) |
2899 | if (latchedState && !latchedState->wheelEventElement()) |
2900 | latchedState->setPreviousWheelScrolledElement(stopElement); |
2901 | #endif |
2902 | } |
2903 | |
2904 | #if ENABLE(CONTEXT_MENUS) |
2905 | bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event) |
2906 | { |
2907 | Ref<Frame> protectedFrame(m_frame); |
2908 | |
2909 | Document* doc = m_frame.document(); |
2910 | FrameView* view = m_frame.view(); |
2911 | if (!view) |
2912 | return false; |
2913 | |
2914 | // Caret blinking is normally un-suspended in handleMouseReleaseEvent, but we |
2915 | // won't receive that event once the context menu is up. |
2916 | m_frame.selection().setCaretBlinkingSuspended(false); |
2917 | // Clear mouse press state to avoid initiating a drag while context menu is up. |
2918 | m_mousePressed = false; |
2919 | bool swallowEvent; |
2920 | LayoutPoint viewportPos = view->windowToContents(event.position()); |
2921 | HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent); |
2922 | MouseEventWithHitTestResults mouseEvent = doc->prepareMouseEvent(request, viewportPos, event); |
2923 | |
2924 | // Do not show context menus when clicking on scrollbars. |
2925 | if (mouseEvent.scrollbar() || view->scrollbarAtPoint(event.position())) |
2926 | return false; |
2927 | |
2928 | if (m_frame.editor().behavior().shouldSelectOnContextualMenuClick() |
2929 | && !m_frame.selection().contains(viewportPos) |
2930 | // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse. |
2931 | // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items |
2932 | // available for text selections. But only if we're above text. |
2933 | && (m_frame.selection().selection().isContentEditable() || (mouseEvent.targetNode() && mouseEvent.targetNode()->isTextNode()))) { |
2934 | m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection |
2935 | selectClosestContextualWordOrLinkFromMouseEvent(mouseEvent); |
2936 | } |
2937 | |
2938 | swallowEvent = !dispatchMouseEvent(eventNames().contextmenuEvent, mouseEvent.targetNode(), true, 0, event, false); |
2939 | |
2940 | return swallowEvent; |
2941 | } |
2942 | |
2943 | bool EventHandler::sendContextMenuEventForKey() |
2944 | { |
2945 | Ref<Frame> protectedFrame(m_frame); |
2946 | |
2947 | FrameView* view = m_frame.view(); |
2948 | if (!view) |
2949 | return false; |
2950 | |
2951 | Document* doc = m_frame.document(); |
2952 | if (!doc) |
2953 | return false; |
2954 | |
2955 | // Clear mouse press state to avoid initiating a drag while context menu is up. |
2956 | m_mousePressed = false; |
2957 | |
2958 | static const int = 1; |
2959 | |
2960 | #if OS(WINDOWS) |
2961 | int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT); |
2962 | #else |
2963 | int rightAligned = 0; |
2964 | #endif |
2965 | IntPoint location; |
2966 | |
2967 | Element* focusedElement = doc->focusedElement(); |
2968 | const VisibleSelection& selection = m_frame.selection().selection(); |
2969 | Position start = selection.start(); |
2970 | |
2971 | if (start.deprecatedNode() && (selection.rootEditableElement() || selection.isRange())) { |
2972 | RefPtr<Range> selectionRange = selection.toNormalizedRange(); |
2973 | IntRect firstRect = m_frame.editor().firstRectForRange(selectionRange.get()); |
2974 | |
2975 | int x = rightAligned ? firstRect.maxX() : firstRect.x(); |
2976 | // In a multiline edit, firstRect.maxY() would endup on the next line, so -1. |
2977 | int y = firstRect.maxY() ? firstRect.maxY() - 1 : 0; |
2978 | location = IntPoint(x, y); |
2979 | } else if (focusedElement) { |
2980 | RenderBoxModelObject* box = focusedElement->renderBoxModelObject(); |
2981 | if (!box) |
2982 | return false; |
2983 | |
2984 | IntRect boundingBoxRect = box->absoluteBoundingBoxRect(true); |
2985 | location = IntPoint(boundingBoxRect.x(), boundingBoxRect.maxY() - 1); |
2986 | } else { |
2987 | location = IntPoint( |
2988 | rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin, |
2989 | kContextMenuMargin); |
2990 | } |
2991 | |
2992 | m_frame.view()->setCursor(pointerCursor()); |
2993 | |
2994 | IntPoint position = view->contentsToRootView(location); |
2995 | IntPoint globalPosition = view->hostWindow()->rootViewToScreen(IntRect(position, IntSize())).location(); |
2996 | |
2997 | Node* targetNode = doc->focusedElement(); |
2998 | if (!targetNode) |
2999 | targetNode = doc; |
3000 | |
3001 | // Use the focused node as the target for hover and active. |
3002 | HitTestResult result(position); |
3003 | result.setInnerNode(targetNode); |
3004 | doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent, result.targetElement()); |
3005 | |
3006 | // The contextmenu event is a mouse event even when invoked using the keyboard. |
3007 | // This is required for web compatibility. |
3008 | |
3009 | #if OS(WINDOWS) |
3010 | PlatformEvent::Type eventType = PlatformEvent::MouseReleased; |
3011 | #else |
3012 | PlatformEvent::Type eventType = PlatformEvent::MousePressed; |
3013 | #endif |
3014 | |
3015 | PlatformMouseEvent platformMouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WallTime::now(), ForceAtClick, NoTap); |
3016 | |
3017 | return sendContextMenuEvent(platformMouseEvent); |
3018 | } |
3019 | #endif // ENABLE(CONTEXT_MENUS) |
3020 | |
3021 | void EventHandler::scheduleHoverStateUpdate() |
3022 | { |
3023 | if (!m_hoverTimer.isActive()) |
3024 | m_hoverTimer.startOneShot(0_s); |
3025 | } |
3026 | |
3027 | #if ENABLE(CURSOR_SUPPORT) |
3028 | void EventHandler::scheduleCursorUpdate() |
3029 | { |
3030 | if (!m_cursorUpdateTimer.isActive()) |
3031 | m_cursorUpdateTimer.startOneShot(cursorUpdateInterval); |
3032 | } |
3033 | #endif |
3034 | |
3035 | void EventHandler::dispatchFakeMouseMoveEventSoon() |
3036 | { |
3037 | #if !ENABLE(IOS_TOUCH_EVENTS) |
3038 | if (m_mousePressed) |
3039 | return; |
3040 | |
3041 | if (m_mousePositionIsUnknown) |
3042 | return; |
3043 | |
3044 | if (Page* page = m_frame.page()) { |
3045 | if (!page->chrome().client().shouldDispatchFakeMouseMoveEvents()) |
3046 | return; |
3047 | } |
3048 | |
3049 | // If the content has ever taken longer than fakeMouseMoveShortInterval we |
3050 | // reschedule the timer and use a longer time. This will cause the content |
3051 | // to receive these moves only after the user is done scrolling, reducing |
3052 | // pauses during the scroll. |
3053 | if (m_fakeMouseMoveEventTimer.isActive()) |
3054 | m_fakeMouseMoveEventTimer.stop(); |
3055 | m_fakeMouseMoveEventTimer.startOneShot(m_maxMouseMovedDuration > fakeMouseMoveDurationThreshold ? fakeMouseMoveLongInterval : fakeMouseMoveShortInterval); |
3056 | #endif |
3057 | } |
3058 | |
3059 | void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad) |
3060 | { |
3061 | #if ENABLE(IOS_TOUCH_EVENTS) |
3062 | UNUSED_PARAM(quad); |
3063 | #else |
3064 | FrameView* view = m_frame.view(); |
3065 | if (!view) |
3066 | return; |
3067 | |
3068 | if (!quad.containsPoint(view->windowToContents(m_lastKnownMousePosition))) |
3069 | return; |
3070 | |
3071 | dispatchFakeMouseMoveEventSoon(); |
3072 | #endif |
3073 | } |
3074 | |
3075 | #if !ENABLE(IOS_TOUCH_EVENTS) |
3076 | void EventHandler::cancelFakeMouseMoveEvent() |
3077 | { |
3078 | m_fakeMouseMoveEventTimer.stop(); |
3079 | } |
3080 | |
3081 | void EventHandler::fakeMouseMoveEventTimerFired() |
3082 | { |
3083 | ASSERT(!m_mousePressed); |
3084 | |
3085 | FrameView* view = m_frame.view(); |
3086 | if (!view) |
3087 | return; |
3088 | |
3089 | if (!m_frame.page() || !m_frame.page()->isVisible() || !m_frame.page()->focusController().isActive()) |
3090 | return; |
3091 | |
3092 | bool shiftKey; |
3093 | bool ctrlKey; |
3094 | bool altKey; |
3095 | bool metaKey; |
3096 | PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey); |
3097 | PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, WallTime::now(), 0, NoTap); |
3098 | mouseMoved(fakeMouseMoveEvent); |
3099 | } |
3100 | #endif // !ENABLE(IOS_TOUCH_EVENTS) |
3101 | |
3102 | void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet) |
3103 | { |
3104 | m_frameSetBeingResized = frameSet; |
3105 | } |
3106 | |
3107 | void EventHandler::resizeLayerDestroyed() |
3108 | { |
3109 | ASSERT(m_resizeLayer); |
3110 | m_resizeLayer = nullptr; |
3111 | } |
3112 | |
3113 | void EventHandler::hoverTimerFired() |
3114 | { |
3115 | m_hoverTimer.stop(); |
3116 | |
3117 | ASSERT(m_frame.document()); |
3118 | |
3119 | Ref<Frame> protectedFrame(m_frame); |
3120 | |
3121 | if (RenderView* renderView = m_frame.contentRenderer()) { |
3122 | if (FrameView* view = m_frame.view()) { |
3123 | HitTestRequest request(HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent); |
3124 | HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); |
3125 | renderView->hitTest(request, result); |
3126 | m_frame.document()->updateHoverActiveState(request, result.targetElement()); |
3127 | } |
3128 | } |
3129 | } |
3130 | |
3131 | bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& event) |
3132 | { |
3133 | // FIXME: Ignoring the state of Shift key is what neither IE nor Firefox do. |
3134 | // IE matches lower and upper case access keys regardless of Shift key state - but if both upper and |
3135 | // lower case variants are present in a document, the correct element is matched based on Shift key state. |
3136 | // Firefox only matches an access key if Shift is not pressed, and does that case-insensitively. |
3137 | ASSERT(!accessKeyModifiers().contains(PlatformEvent::Modifier::ShiftKey)); |
3138 | |
3139 | if ((event.modifiers() - PlatformEvent::Modifier::ShiftKey) != accessKeyModifiers()) |
3140 | return false; |
3141 | auto* element = m_frame.document()->elementForAccessKey(event.unmodifiedText()); |
3142 | if (!element) |
3143 | return false; |
3144 | element->accessKeyAction(false); |
3145 | return true; |
3146 | } |
3147 | |
3148 | #if !PLATFORM(MAC) |
3149 | bool EventHandler::needsKeyboardEventDisambiguationQuirks() const |
3150 | { |
3151 | return false; |
3152 | } |
3153 | #endif |
3154 | |
3155 | #if ENABLE(FULLSCREEN_API) |
3156 | bool EventHandler::isKeyEventAllowedInFullScreen(const PlatformKeyboardEvent& keyEvent) const |
3157 | { |
3158 | Document* document = m_frame.document(); |
3159 | if (document->fullscreenManager().isFullscreenKeyboardInputAllowed()) |
3160 | return true; |
3161 | |
3162 | if (keyEvent.type() == PlatformKeyboardEvent::Char) { |
3163 | if (keyEvent.text().length() != 1) |
3164 | return false; |
3165 | UChar character = keyEvent.text()[0]; |
3166 | return character == ' '; |
3167 | } |
3168 | |
3169 | int keyCode = keyEvent.windowsVirtualKeyCode(); |
3170 | return (keyCode >= VK_BACK && keyCode <= VK_CAPITAL) |
3171 | || (keyCode >= VK_SPACE && keyCode <= VK_DELETE) |
3172 | || (keyCode >= VK_OEM_1 && keyCode <= VK_OEM_PLUS) |
3173 | || (keyCode >= VK_MULTIPLY && keyCode <= VK_OEM_8); |
3174 | } |
3175 | #endif |
3176 | |
3177 | bool EventHandler::keyEvent(const PlatformKeyboardEvent& keyEvent) |
3178 | { |
3179 | Document* topDocument = m_frame.document() ? &m_frame.document()->topDocument() : nullptr; |
3180 | MonotonicTime savedLastHandledUserGestureTimestamp; |
3181 | bool savedUserDidInteractWithPage = topDocument ? topDocument->userDidInteractWithPage() : false; |
3182 | |
3183 | if (m_frame.document()) |
3184 | savedLastHandledUserGestureTimestamp = m_frame.document()->lastHandledUserGestureTimestamp(); |
3185 | |
3186 | bool wasHandled = internalKeyEvent(keyEvent); |
3187 | |
3188 | // If the key event was not handled, do not treat it as user interaction with the page. |
3189 | if (topDocument) { |
3190 | if (!wasHandled) |
3191 | topDocument->setUserDidInteractWithPage(savedUserDidInteractWithPage); |
3192 | else |
3193 | ResourceLoadObserver::shared().logUserInteractionWithReducedTimeResolution(*topDocument); |
3194 | } |
3195 | |
3196 | if (!wasHandled && m_frame.document()) |
3197 | m_frame.document()->updateLastHandledUserGestureTimestamp(savedLastHandledUserGestureTimestamp); |
3198 | |
3199 | return wasHandled; |
3200 | } |
3201 | |
3202 | void EventHandler::capsLockStateMayHaveChanged() const |
3203 | { |
3204 | auto* focusedElement = m_frame.document()->focusedElement(); |
3205 | if (!is<HTMLInputElement>(focusedElement)) |
3206 | return; |
3207 | downcast<HTMLInputElement>(*focusedElement).capsLockStateMayHaveChanged(); |
3208 | } |
3209 | |
3210 | bool EventHandler::internalKeyEvent(const PlatformKeyboardEvent& initialKeyEvent) |
3211 | { |
3212 | Ref<Frame> protectedFrame(m_frame); |
3213 | RefPtr<FrameView> protector(m_frame.view()); |
3214 | |
3215 | LOG(Editing, "EventHandler %p keyEvent (text %s keyIdentifier %s)" , this, initialKeyEvent.text().utf8().data(), initialKeyEvent.keyIdentifier().utf8().data()); |
3216 | |
3217 | #if ENABLE(POINTER_LOCK) |
3218 | if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE && m_frame.page()->pointerLockController().element()) { |
3219 | m_frame.page()->pointerLockController().requestPointerUnlockAndForceCursorVisible(); |
3220 | } |
3221 | #endif |
3222 | |
3223 | if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) { |
3224 | if (auto* page = m_frame.page()) { |
3225 | if (auto* validationMessageClient = page->validationMessageClient()) |
3226 | validationMessageClient->hideAnyValidationMessage(); |
3227 | } |
3228 | } |
3229 | |
3230 | #if ENABLE(FULLSCREEN_API) |
3231 | if (m_frame.document()->fullscreenManager().isFullscreen()) { |
3232 | if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) { |
3233 | m_frame.document()->fullscreenManager().cancelFullscreen(); |
3234 | return true; |
3235 | } |
3236 | |
3237 | if (!isKeyEventAllowedInFullScreen(initialKeyEvent)) |
3238 | return false; |
3239 | } |
3240 | #endif |
3241 | |
3242 | if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL) |
3243 | capsLockStateMayHaveChanged(); |
3244 | |
3245 | #if ENABLE(PAN_SCROLLING) |
3246 | if (m_frame.mainFrame().eventHandler().panScrollInProgress()) { |
3247 | // If a key is pressed while the panScroll is in progress then we want to stop |
3248 | if (initialKeyEvent.type() == PlatformEvent::KeyDown || initialKeyEvent.type() == PlatformEvent::RawKeyDown) |
3249 | stopAutoscrollTimer(); |
3250 | |
3251 | // If we were in panscroll mode, we swallow the key event |
3252 | return true; |
3253 | } |
3254 | #endif |
3255 | |
3256 | // Check for cases where we are too early for events -- possible unmatched key up |
3257 | // from pressing return in the location bar. |
3258 | RefPtr<Element> element = eventTargetElementForDocument(m_frame.document()); |
3259 | if (!element) |
3260 | return false; |
3261 | |
3262 | UserGestureType gestureType = UserGestureType::Other; |
3263 | if (initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) |
3264 | gestureType = UserGestureType::EscapeKey; |
3265 | |
3266 | UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document(), gestureType, UserGestureIndicator::ProcessInteractionStyle::Delayed); |
3267 | UserTypingGestureIndicator typingGestureIndicator(m_frame); |
3268 | |
3269 | if (FrameView* view = m_frame.view()) |
3270 | view->disableLayerFlushThrottlingTemporarilyForInteraction(); |
3271 | |
3272 | // FIXME (bug 68185): this call should be made at another abstraction layer |
3273 | m_frame.loader().resetMultipleFormSubmissionProtection(); |
3274 | |
3275 | // In IE, access keys are special, they are handled after default keydown processing, but cannot be canceled - this is hard to match. |
3276 | // On Mac OS X, we process them before dispatching keydown, as the default keydown handler implements Emacs key bindings, which may conflict |
3277 | // with access keys. Then we dispatch keydown, but suppress its default handling. |
3278 | // On Windows, WebKit explicitly calls handleAccessKey() instead of dispatching a keypress event for WM_SYSCHAR messages. |
3279 | // Other platforms currently match either Mac or Windows behavior, depending on whether they send combined KeyDown events. |
3280 | bool matchedAnAccessKey = false; |
3281 | if (initialKeyEvent.type() == PlatformEvent::KeyDown) |
3282 | matchedAnAccessKey = handleAccessKey(initialKeyEvent); |
3283 | |
3284 | // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch. |
3285 | if (initialKeyEvent.type() == PlatformEvent::KeyUp || initialKeyEvent.type() == PlatformEvent::Char) |
3286 | return !element->dispatchKeyEvent(initialKeyEvent); |
3287 | |
3288 | bool backwardCompatibilityMode = needsKeyboardEventDisambiguationQuirks(); |
3289 | |
3290 | PlatformKeyboardEvent keyDownEvent = initialKeyEvent; |
3291 | if (keyDownEvent.type() != PlatformEvent::RawKeyDown) |
3292 | keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown, backwardCompatibilityMode); |
3293 | auto keydown = KeyboardEvent::create(keyDownEvent, &m_frame.windowProxy()); |
3294 | if (matchedAnAccessKey) |
3295 | keydown->preventDefault(); |
3296 | keydown->setTarget(element); |
3297 | |
3298 | if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) { |
3299 | element->dispatchEvent(keydown); |
3300 | // If frame changed as a result of keydown dispatch, then return true to avoid sending a subsequent keypress message to the new frame. |
3301 | bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame(); |
3302 | return keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame; |
3303 | } |
3304 | |
3305 | // Run input method in advance of DOM event handling. This may result in the IM |
3306 | // modifying the page prior the keydown event, but this behaviour is necessary |
3307 | // in order to match IE: |
3308 | // 1. preventing default handling of keydown and keypress events has no effect on IM input; |
3309 | // 2. if an input method handles the event, its keyCode is set to 229 in keydown event. |
3310 | m_frame.editor().handleInputMethodKeydown(keydown.get()); |
3311 | |
3312 | bool handledByInputMethod = keydown->defaultHandled(); |
3313 | |
3314 | if (handledByInputMethod) { |
3315 | keyDownEvent.setWindowsVirtualKeyCode(CompositionEventKeyCode); |
3316 | keydown = KeyboardEvent::create(keyDownEvent, &m_frame.windowProxy()); |
3317 | keydown->setTarget(element); |
3318 | keydown->setIsDefaultEventHandlerIgnored(); |
3319 | } |
3320 | |
3321 | if (accessibilityPreventsEventPropagation(keydown)) |
3322 | keydown->stopPropagation(); |
3323 | |
3324 | element->dispatchEvent(keydown); |
3325 | if (handledByInputMethod) |
3326 | return true; |
3327 | |
3328 | // If frame changed as a result of keydown dispatch, then return early to avoid sending a subsequent keypress message to the new frame. |
3329 | bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame(); |
3330 | bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame; |
3331 | if (keydownResult && !backwardCompatibilityMode) |
3332 | return keydownResult; |
3333 | |
3334 | // Focus may have changed during keydown handling, so refetch element. |
3335 | // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original element. |
3336 | if (!keydownResult) { |
3337 | element = eventTargetElementForDocument(m_frame.document()); |
3338 | if (!element) |
3339 | return false; |
3340 | } |
3341 | |
3342 | PlatformKeyboardEvent keyPressEvent = initialKeyEvent; |
3343 | keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Char, backwardCompatibilityMode); |
3344 | if (keyPressEvent.text().isEmpty()) |
3345 | return keydownResult; |
3346 | auto keypress = KeyboardEvent::create(keyPressEvent, &m_frame.windowProxy()); |
3347 | keypress->setTarget(element); |
3348 | if (keydownResult) |
3349 | keypress->preventDefault(); |
3350 | #if PLATFORM(COCOA) |
3351 | keypress->keypressCommands() = keydown->keypressCommands(); |
3352 | #endif |
3353 | element->dispatchEvent(keypress); |
3354 | |
3355 | return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled(); |
3356 | } |
3357 | |
3358 | static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier) |
3359 | { |
3360 | static NeverDestroyed<AtomicString> Down("Down" , AtomicString::ConstructFromLiteral); |
3361 | static NeverDestroyed<AtomicString> Up("Up" , AtomicString::ConstructFromLiteral); |
3362 | static NeverDestroyed<AtomicString> Left("Left" , AtomicString::ConstructFromLiteral); |
3363 | static NeverDestroyed<AtomicString> Right("Right" , AtomicString::ConstructFromLiteral); |
3364 | |
3365 | FocusDirection retVal = FocusDirectionNone; |
3366 | |
3367 | if (keyIdentifier == Down) |
3368 | retVal = FocusDirectionDown; |
3369 | else if (keyIdentifier == Up) |
3370 | retVal = FocusDirectionUp; |
3371 | else if (keyIdentifier == Left) |
3372 | retVal = FocusDirectionLeft; |
3373 | else if (keyIdentifier == Right) |
3374 | retVal = FocusDirectionRight; |
3375 | |
3376 | return retVal; |
3377 | } |
3378 | |
3379 | static void setInitialKeyboardSelection(Frame& frame, SelectionDirection direction) |
3380 | { |
3381 | Document* document = frame.document(); |
3382 | if (!document) |
3383 | return; |
3384 | |
3385 | FrameSelection& selection = frame.selection(); |
3386 | |
3387 | if (!selection.isNone()) |
3388 | return; |
3389 | |
3390 | Element* focusedElement = document->focusedElement(); |
3391 | VisiblePosition visiblePosition; |
3392 | |
3393 | switch (direction) { |
3394 | case DirectionBackward: |
3395 | case DirectionLeft: |
3396 | if (focusedElement) |
3397 | visiblePosition = VisiblePosition(positionBeforeNode(focusedElement)); |
3398 | else |
3399 | visiblePosition = endOfDocument(document); |
3400 | break; |
3401 | case DirectionForward: |
3402 | case DirectionRight: |
3403 | if (focusedElement) |
3404 | visiblePosition = VisiblePosition(positionAfterNode(focusedElement)); |
3405 | else |
3406 | visiblePosition = startOfDocument(document); |
3407 | break; |
3408 | } |
3409 | |
3410 | AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false }); |
3411 | selection.setSelection(visiblePosition, FrameSelection::defaultSetSelectionOptions(UserTriggered), intent); |
3412 | } |
3413 | |
3414 | static void handleKeyboardSelectionMovement(Frame& frame, KeyboardEvent& event) |
3415 | { |
3416 | FrameSelection& selection = frame.selection(); |
3417 | |
3418 | bool isCommanded = event.getModifierState("Meta" ); |
3419 | bool isOptioned = event.getModifierState("Alt" ); |
3420 | bool isSelection = !selection.isNone(); |
3421 | |
3422 | FrameSelection::EAlteration alternation = event.getModifierState("Shift" ) ? FrameSelection::AlterationExtend : FrameSelection::AlterationMove; |
3423 | SelectionDirection direction = DirectionForward; |
3424 | TextGranularity granularity = CharacterGranularity; |
3425 | |
3426 | switch (focusDirectionForKey(event.keyIdentifier())) { |
3427 | case FocusDirectionNone: |
3428 | return; |
3429 | case FocusDirectionForward: |
3430 | case FocusDirectionBackward: |
3431 | ASSERT_NOT_REACHED(); |
3432 | return; |
3433 | case FocusDirectionUp: |
3434 | direction = DirectionBackward; |
3435 | granularity = isCommanded ? DocumentBoundary : LineGranularity; |
3436 | break; |
3437 | case FocusDirectionDown: |
3438 | direction = DirectionForward; |
3439 | granularity = isCommanded ? DocumentBoundary : LineGranularity; |
3440 | break; |
3441 | case FocusDirectionLeft: |
3442 | direction = DirectionLeft; |
3443 | granularity = (isCommanded) ? LineBoundary : (isOptioned) ? WordGranularity : CharacterGranularity; |
3444 | break; |
3445 | case FocusDirectionRight: |
3446 | direction = DirectionRight; |
3447 | granularity = (isCommanded) ? LineBoundary : (isOptioned) ? WordGranularity : CharacterGranularity; |
3448 | break; |
3449 | } |
3450 | |
3451 | if (isSelection) |
3452 | selection.modify(alternation, direction, granularity, UserTriggered); |
3453 | else |
3454 | setInitialKeyboardSelection(frame, direction); |
3455 | |
3456 | event.setDefaultHandled(); |
3457 | } |
3458 | |
3459 | void EventHandler::handleKeyboardSelectionMovementForAccessibility(KeyboardEvent& event) |
3460 | { |
3461 | if (event.type() == eventNames().keydownEvent) { |
3462 | if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) |
3463 | handleKeyboardSelectionMovement(m_frame, event); |
3464 | } |
3465 | } |
3466 | |
3467 | bool EventHandler::accessibilityPreventsEventPropagation(KeyboardEvent& event) |
3468 | { |
3469 | #if PLATFORM(COCOA) |
3470 | if (!AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) |
3471 | return false; |
3472 | |
3473 | if (!m_frame.settings().preventKeyboardDOMEventDispatch()) |
3474 | return false; |
3475 | |
3476 | // Check for key events that are relevant to accessibility: tab and arrows keys that change focus |
3477 | if (event.keyIdentifier() == "U+0009" ) |
3478 | return true; |
3479 | FocusDirection direction = focusDirectionForKey(event.keyIdentifier()); |
3480 | if (direction != FocusDirectionNone) |
3481 | return true; |
3482 | #else |
3483 | UNUSED_PARAM(event); |
3484 | #endif |
3485 | return false; |
3486 | } |
3487 | |
3488 | void EventHandler::defaultKeyboardEventHandler(KeyboardEvent& event) |
3489 | { |
3490 | Ref<Frame> protectedFrame(m_frame); |
3491 | |
3492 | if (event.type() == eventNames().keydownEvent) { |
3493 | m_frame.editor().handleKeyboardEvent(event); |
3494 | if (event.defaultHandled()) |
3495 | return; |
3496 | if (event.keyIdentifier() == "U+0009" ) |
3497 | defaultTabEventHandler(event); |
3498 | else if (event.keyIdentifier() == "U+0008" ) |
3499 | defaultBackspaceEventHandler(event); |
3500 | else { |
3501 | FocusDirection direction = focusDirectionForKey(event.keyIdentifier()); |
3502 | if (direction != FocusDirectionNone) |
3503 | defaultArrowEventHandler(direction, event); |
3504 | } |
3505 | |
3506 | handleKeyboardSelectionMovementForAccessibility(event); |
3507 | } |
3508 | if (event.type() == eventNames().keypressEvent) { |
3509 | m_frame.editor().handleKeyboardEvent(event); |
3510 | if (event.defaultHandled()) |
3511 | return; |
3512 | if (event.charCode() == ' ') |
3513 | defaultSpaceEventHandler(event); |
3514 | } |
3515 | } |
3516 | |
3517 | #if ENABLE(DRAG_SUPPORT) |
3518 | bool EventHandler::dragHysteresisExceeded(const IntPoint& floatDragViewportLocation) const |
3519 | { |
3520 | FloatPoint dragViewportLocation(floatDragViewportLocation.x(), floatDragViewportLocation.y()); |
3521 | return dragHysteresisExceeded(dragViewportLocation); |
3522 | } |
3523 | |
3524 | bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation) const |
3525 | { |
3526 | int threshold = GeneralDragHysteresis; |
3527 | switch (dragState().type) { |
3528 | case DragSourceActionSelection: |
3529 | threshold = TextDragHysteresis; |
3530 | break; |
3531 | case DragSourceActionImage: |
3532 | #if ENABLE(ATTACHMENT_ELEMENT) |
3533 | case DragSourceActionAttachment: |
3534 | #endif |
3535 | threshold = ImageDragHysteresis; |
3536 | break; |
3537 | case DragSourceActionLink: |
3538 | threshold = LinkDragHysteresis; |
3539 | break; |
3540 | #if ENABLE(INPUT_TYPE_COLOR) |
3541 | case DragSourceActionColor: |
3542 | threshold = ColorDragHystersis; |
3543 | break; |
3544 | #endif |
3545 | case DragSourceActionDHTML: |
3546 | break; |
3547 | case DragSourceActionNone: |
3548 | case DragSourceActionAny: |
3549 | ASSERT_NOT_REACHED(); |
3550 | } |
3551 | |
3552 | return mouseMovementExceedsThreshold(dragViewportLocation, threshold); |
3553 | } |
3554 | |
3555 | void EventHandler::invalidateDataTransfer() |
3556 | { |
3557 | if (!dragState().dataTransfer) |
3558 | return; |
3559 | dragState().dataTransfer->makeInvalidForSecurity(); |
3560 | dragState().dataTransfer = nullptr; |
3561 | } |
3562 | |
3563 | static void removeDraggedContentDocumentMarkersFromAllFramesInPage(Page& page) |
3564 | { |
3565 | for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) { |
3566 | if (auto* document = frame->document()) |
3567 | document->markers().removeMarkers(DocumentMarker::DraggedContent); |
3568 | } |
3569 | |
3570 | if (auto* mainFrameRenderer = page.mainFrame().contentRenderer()) |
3571 | mainFrameRenderer->repaintRootContents(); |
3572 | } |
3573 | |
3574 | void EventHandler::dragCancelled() |
3575 | { |
3576 | #if ENABLE(DATA_INTERACTION) |
3577 | if (auto* page = m_frame.page()) |
3578 | removeDraggedContentDocumentMarkersFromAllFramesInPage(*page); |
3579 | #endif |
3580 | } |
3581 | |
3582 | void EventHandler::didStartDrag() |
3583 | { |
3584 | #if ENABLE(DATA_INTERACTION) |
3585 | auto dragSource = dragState().source; |
3586 | if (!dragSource) |
3587 | return; |
3588 | |
3589 | auto* renderer = dragSource->renderer(); |
3590 | if (!renderer) |
3591 | return; |
3592 | |
3593 | RefPtr<Range> draggedContentRange; |
3594 | if (dragState().type & DragSourceActionSelection) |
3595 | draggedContentRange = m_frame.selection().selection().toNormalizedRange(); |
3596 | else { |
3597 | Position startPosition(dragSource.get(), Position::PositionIsBeforeAnchor); |
3598 | Position endPosition(dragSource.get(), Position::PositionIsAfterAnchor); |
3599 | draggedContentRange = Range::create(dragSource->document(), startPosition, endPosition); |
3600 | } |
3601 | |
3602 | if (draggedContentRange) { |
3603 | draggedContentRange->ownerDocument().markers().addDraggedContentMarker(*draggedContentRange); |
3604 | if (auto* renderer = m_frame.contentRenderer()) |
3605 | renderer->repaintRootContents(); |
3606 | } |
3607 | #endif |
3608 | } |
3609 | |
3610 | void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation, MayExtendDragSession mayExtendDragSession) |
3611 | { |
3612 | // Send a hit test request so that RenderLayer gets a chance to update the :hover and :active pseudoclasses. |
3613 | HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent); |
3614 | prepareMouseEvent(request, event); |
3615 | |
3616 | if (dragState().source && dragState().shouldDispatchEvents) { |
3617 | dragState().dataTransfer->setDestinationOperation(operation); |
3618 | dispatchDragSrcEvent(eventNames().dragendEvent, event); |
3619 | } |
3620 | invalidateDataTransfer(); |
3621 | |
3622 | if (mayExtendDragSession == MayExtendDragSession::No) { |
3623 | if (auto* page = m_frame.page()) |
3624 | removeDraggedContentDocumentMarkersFromAllFramesInPage(*page); |
3625 | } |
3626 | |
3627 | dragState().source = nullptr; |
3628 | // In case the drag was ended due to an escape key press we need to ensure |
3629 | // that consecutive mousemove events don't reinitiate the drag and drop. |
3630 | m_mouseDownMayStartDrag = false; |
3631 | } |
3632 | |
3633 | void EventHandler::updateDragStateAfterEditDragIfNeeded(Element& rootEditableElement) |
3634 | { |
3635 | // If inserting the dragged contents removed the drag source, we still want to fire dragend at the root editable element. |
3636 | if (dragState().source && !dragState().source->isConnected()) |
3637 | dragState().source = &rootEditableElement; |
3638 | } |
3639 | |
3640 | void EventHandler::dispatchDragSrcEvent(const AtomicString& eventType, const PlatformMouseEvent& event) |
3641 | { |
3642 | ASSERT(dragState().dataTransfer); |
3643 | dispatchDragEvent(eventType, *dragState().source, event, *dragState().dataTransfer); |
3644 | } |
3645 | |
3646 | bool EventHandler::dispatchDragStartEventOnSourceElement(DataTransfer& dataTransfer) |
3647 | { |
3648 | return !dispatchDragEvent(eventNames().dragstartEvent, *dragState().source, m_mouseDown, dataTransfer) && !m_frame.selection().selection().isInPasswordField(); |
3649 | } |
3650 | |
3651 | static bool ExactlyOneBitSet(DragSourceAction n) |
3652 | { |
3653 | return n && !(n & (n - 1)); |
3654 | } |
3655 | |
3656 | RefPtr<Element> EventHandler::draggedElement() const |
3657 | { |
3658 | return dragState().source; |
3659 | } |
3660 | |
3661 | bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis) |
3662 | { |
3663 | if (event.event().button() != LeftButton || event.event().type() != PlatformEvent::MouseMoved) { |
3664 | // If we allowed the other side of the bridge to handle a drag |
3665 | // last time, then m_mousePressed might still be set. So we |
3666 | // clear it now to make sure the next move after a drag |
3667 | // doesn't look like a drag. |
3668 | m_mousePressed = false; |
3669 | return false; |
3670 | } |
3671 | |
3672 | Ref<Frame> protectedFrame(m_frame); |
3673 | |
3674 | if (eventLoopHandleMouseDragged(event)) |
3675 | return true; |
3676 | |
3677 | // Careful that the drag starting logic stays in sync with eventMayStartDrag() |
3678 | |
3679 | if (m_mouseDownMayStartDrag && !dragState().source) { |
3680 | dragState().shouldDispatchEvents = (updateDragSourceActionsAllowed() & DragSourceActionDHTML); |
3681 | |
3682 | // try to find an element that wants to be dragged |
3683 | HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); |
3684 | HitTestResult result(m_mouseDownPos); |
3685 | m_frame.contentRenderer()->hitTest(request, result); |
3686 | if (m_frame.page()) |
3687 | dragState().source = m_frame.page()->dragController().draggableElement(&m_frame, result.targetElement(), m_mouseDownPos, dragState()); |
3688 | |
3689 | if (!dragState().source) |
3690 | m_mouseDownMayStartDrag = false; // no element is draggable |
3691 | else |
3692 | m_dragMayStartSelectionInstead = (dragState().type & DragSourceActionSelection); |
3693 | } |
3694 | |
3695 | // For drags starting in the selection, the user must wait between the mousedown and mousedrag, |
3696 | // or else we bail on the dragging stuff and allow selection to occur |
3697 | if (m_mouseDownMayStartDrag && m_dragMayStartSelectionInstead && (dragState().type & DragSourceActionSelection) && event.event().timestamp() - m_mouseDownTimestamp < TextDragDelay) { |
3698 | ASSERT(event.event().type() == PlatformEvent::MouseMoved); |
3699 | if ((dragState().type & DragSourceActionImage)) { |
3700 | // ... unless the mouse is over an image, then we start dragging just the image |
3701 | dragState().type = DragSourceActionImage; |
3702 | } else if (!(dragState().type & (DragSourceActionDHTML | DragSourceActionLink))) { |
3703 | // ... but only bail if we're not over an unselectable element. |
3704 | m_mouseDownMayStartDrag = false; |
3705 | dragState().source = nullptr; |
3706 | // ... but if this was the first click in the window, we don't even want to start selection |
3707 | if (eventActivatedView(event.event())) |
3708 | m_mouseDownMayStartSelect = false; |
3709 | } else { |
3710 | // Prevent the following case from occuring: |
3711 | // 1. User starts a drag immediately after mouse down over an unselectable element. |
3712 | // 2. We enter this block and decided that since we're over an unselectable element, don't cancel the drag. |
3713 | // 3. The drag gets resolved as a potential selection drag below /but/ we haven't exceeded the drag hysteresis yet. |
3714 | // 4. We enter this block again, and since it's now marked as a selection drag, we cancel the drag. |
3715 | m_dragMayStartSelectionInstead = false; |
3716 | } |
3717 | } |
3718 | |
3719 | if (!m_mouseDownMayStartDrag) |
3720 | return !mouseDownMayStartSelect() && !m_mouseDownMayStartAutoscroll; |
3721 | ASSERT(dragState().source); |
3722 | |
3723 | if (!ExactlyOneBitSet(dragState().type)) { |
3724 | ASSERT(dragState().type & DragSourceActionSelection); |
3725 | ASSERT(ExactlyOneBitSet(static_cast<DragSourceAction>(dragState().type & ~DragSourceActionSelection))); |
3726 | |
3727 | dragState().type = DragSourceActionSelection; |
3728 | } |
3729 | |
3730 | // We are starting a text/image/url drag, so the cursor should be an arrow |
3731 | if (FrameView* view = m_frame.view()) { |
3732 | // FIXME <rdar://7577595>: Custom cursors aren't supported during drag and drop (default to pointer). |
3733 | view->setCursor(pointerCursor()); |
3734 | } |
3735 | |
3736 | if (checkDragHysteresis == ShouldCheckDragHysteresis && !dragHysteresisExceeded(event.event().position())) |
3737 | return true; |
3738 | |
3739 | // Once we're past the hysteresis point, we don't want to treat this gesture as a click |
3740 | invalidateClick(); |
3741 | |
3742 | DragOperation srcOp = DragOperationNone; |
3743 | |
3744 | // This does work only if we missed a dragEnd. Do it anyway, just to make sure the old dataTransfer gets numbed. |
3745 | invalidateDataTransfer(); |
3746 | |
3747 | dragState().dataTransfer = DataTransfer::createForDrag(); |
3748 | HasNonDefaultPasteboardData hasNonDefaultPasteboardData = HasNonDefaultPasteboardData::No; |
3749 | |
3750 | if (dragState().shouldDispatchEvents) { |
3751 | ASSERT(dragState().source); |
3752 | auto dragStartDataTransfer = DataTransfer::createForDragStartEvent(dragState().source->document()); |
3753 | m_mouseDownMayStartDrag = dispatchDragStartEventOnSourceElement(dragStartDataTransfer); |
3754 | hasNonDefaultPasteboardData = dragStartDataTransfer->pasteboard().hasData() ? HasNonDefaultPasteboardData::Yes : HasNonDefaultPasteboardData::No; |
3755 | dragState().dataTransfer->moveDragState(WTFMove(dragStartDataTransfer)); |
3756 | |
3757 | if (dragState().source && dragState().type == DragSourceActionDHTML && !dragState().dataTransfer->hasDragImage()) { |
3758 | dragState().source->document().updateStyleIfNeeded(); |
3759 | if (auto* renderer = dragState().source->renderer()) { |
3760 | auto absolutePosition = renderer->localToAbsolute(); |
3761 | auto delta = m_mouseDownPos - roundedIntPoint(absolutePosition); |
3762 | dragState().dataTransfer->setDragImage(dragState().source.get(), delta.width(), delta.height()); |
3763 | } else { |
3764 | dispatchDragSrcEvent(eventNames().dragendEvent, event.event()); |
3765 | m_mouseDownMayStartDrag = false; |
3766 | invalidateDataTransfer(); |
3767 | dragState().source = nullptr; |
3768 | return true; |
3769 | } |
3770 | } |
3771 | |
3772 | dragState().dataTransfer->makeInvalidForSecurity(); |
3773 | |
3774 | if (m_mouseDownMayStartDrag) { |
3775 | // Gather values from DHTML element, if it set any. |
3776 | srcOp = dragState().dataTransfer->sourceOperation(); |
3777 | |
3778 | // Yuck, a draggedImage:moveTo: message can be fired as a result of kicking off the |
3779 | // drag with dragImage! Because of that dumb reentrancy, we may think we've not |
3780 | // started the drag when that happens. So we have to assume it's started before we kick it off. |
3781 | dragState().dataTransfer->setDragHasStarted(); |
3782 | } |
3783 | } |
3784 | |
3785 | if (m_mouseDownMayStartDrag) { |
3786 | Page* page = m_frame.page(); |
3787 | m_didStartDrag = page && page->dragController().startDrag(m_frame, dragState(), srcOp, event.event(), m_mouseDownPos, hasNonDefaultPasteboardData); |
3788 | // In WebKit2 we could re-enter this code and start another drag. |
3789 | // On OS X this causes problems with the ownership of the pasteboard and the promised types. |
3790 | if (m_didStartDrag) { |
3791 | m_mouseDownMayStartDrag = false; |
3792 | return true; |
3793 | } |
3794 | if (dragState().source && dragState().shouldDispatchEvents) { |
3795 | // Drag was canned at the last minute. We owe dragSource a dragend event. |
3796 | dispatchDragSrcEvent(eventNames().dragendEvent, event.event()); |
3797 | m_mouseDownMayStartDrag = false; |
3798 | } |
3799 | } |
3800 | |
3801 | if (!m_mouseDownMayStartDrag) { |
3802 | // Something failed to start the drag, clean up. |
3803 | invalidateDataTransfer(); |
3804 | dragState().source = nullptr; |
3805 | } |
3806 | |
3807 | // No more default handling (like selection), whether we're past the hysteresis bounds or not |
3808 | return true; |
3809 | } |
3810 | #endif // ENABLE(DRAG_SUPPORT) |
3811 | |
3812 | bool EventHandler::mouseMovementExceedsThreshold(const FloatPoint& viewportLocation, int pointsThreshold) const |
3813 | { |
3814 | FrameView* view = m_frame.view(); |
3815 | if (!view) |
3816 | return false; |
3817 | IntPoint location = view->windowToContents(flooredIntPoint(viewportLocation)); |
3818 | IntSize delta = location - m_mouseDownPos; |
3819 | |
3820 | return abs(delta.width()) >= pointsThreshold || abs(delta.height()) >= pointsThreshold; |
3821 | } |
3822 | |
3823 | bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEvent, TextEventInputType inputType) |
3824 | { |
3825 | LOG(Editing, "EventHandler %p handleTextInputEvent (text %s)" , this, text.utf8().data()); |
3826 | |
3827 | // Platforms should differentiate real commands like selectAll from text input in disguise (like insertNewline), |
3828 | // and avoid dispatching text input events from keydown default handlers. |
3829 | ASSERT(!is<KeyboardEvent>(underlyingEvent) || downcast<KeyboardEvent>(*underlyingEvent).type() == eventNames().keypressEvent); |
3830 | |
3831 | Ref<Frame> protectedFrame(m_frame); |
3832 | |
3833 | EventTarget* target; |
3834 | if (underlyingEvent) |
3835 | target = underlyingEvent->target(); |
3836 | else |
3837 | target = eventTargetElementForDocument(m_frame.document()); |
3838 | if (!target) |
3839 | return false; |
3840 | |
3841 | if (FrameView* view = m_frame.view()) |
3842 | view->disableLayerFlushThrottlingTemporarilyForInteraction(); |
3843 | |
3844 | auto event = TextEvent::create(&m_frame.windowProxy(), text, inputType); |
3845 | event->setUnderlyingEvent(underlyingEvent); |
3846 | |
3847 | target->dispatchEvent(event); |
3848 | return event->defaultHandled(); |
3849 | } |
3850 | |
3851 | bool EventHandler::isKeyboardOptionTab(KeyboardEvent& event) |
3852 | { |
3853 | return (event.type() == eventNames().keydownEvent || event.type() == eventNames().keypressEvent) |
3854 | && event.altKey() |
3855 | && event.keyIdentifier() == "U+0009" ; |
3856 | } |
3857 | |
3858 | bool EventHandler::eventInvertsTabsToLinksClientCallResult(KeyboardEvent& event) |
3859 | { |
3860 | #if PLATFORM(COCOA) |
3861 | return isKeyboardOptionTab(event); |
3862 | #else |
3863 | UNUSED_PARAM(event); |
3864 | return false; |
3865 | #endif |
3866 | } |
3867 | |
3868 | bool EventHandler::tabsToLinks(KeyboardEvent* event) const |
3869 | { |
3870 | // FIXME: This function needs a better name. It can be called for keypresses other than Tab when spatial navigation is enabled. |
3871 | |
3872 | Page* page = m_frame.page(); |
3873 | if (!page) |
3874 | return false; |
3875 | |
3876 | bool tabsToLinksClientCallResult = page->chrome().client().keyboardUIMode() & KeyboardAccessTabsToLinks; |
3877 | return (event && eventInvertsTabsToLinksClientCallResult(*event)) ? !tabsToLinksClientCallResult : tabsToLinksClientCallResult; |
3878 | } |
3879 | |
3880 | void EventHandler::defaultTextInputEventHandler(TextEvent& event) |
3881 | { |
3882 | if (m_frame.editor().handleTextEvent(event)) |
3883 | event.setDefaultHandled(); |
3884 | } |
3885 | |
3886 | |
3887 | void EventHandler::defaultSpaceEventHandler(KeyboardEvent& event) |
3888 | { |
3889 | Ref<Frame> protectedFrame(m_frame); |
3890 | |
3891 | ASSERT(event.type() == eventNames().keypressEvent); |
3892 | |
3893 | if (event.ctrlKey() || event.metaKey() || event.altKey() || event.altGraphKey()) |
3894 | return; |
3895 | |
3896 | ScrollLogicalDirection direction = event.shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward; |
3897 | if (logicalScrollOverflow(direction, ScrollByPage)) { |
3898 | event.setDefaultHandled(); |
3899 | return; |
3900 | } |
3901 | |
3902 | FrameView* view = m_frame.view(); |
3903 | if (!view) |
3904 | return; |
3905 | |
3906 | if (view->logicalScroll(direction, ScrollByPage)) |
3907 | event.setDefaultHandled(); |
3908 | } |
3909 | |
3910 | void EventHandler::defaultBackspaceEventHandler(KeyboardEvent& event) |
3911 | { |
3912 | ASSERT(event.type() == eventNames().keydownEvent); |
3913 | |
3914 | if (event.ctrlKey() || event.metaKey() || event.altKey() || event.altGraphKey()) |
3915 | return; |
3916 | |
3917 | if (!m_frame.editor().behavior().shouldNavigateBackOnBackspace()) |
3918 | return; |
3919 | |
3920 | Page* page = m_frame.page(); |
3921 | if (!page) |
3922 | return; |
3923 | |
3924 | if (!m_frame.settings().backspaceKeyNavigationEnabled()) |
3925 | return; |
3926 | |
3927 | bool handledEvent = false; |
3928 | |
3929 | if (event.shiftKey()) |
3930 | handledEvent = page->backForward().goForward(); |
3931 | else |
3932 | handledEvent = page->backForward().goBack(); |
3933 | |
3934 | if (handledEvent) |
3935 | event.setDefaultHandled(); |
3936 | } |
3937 | |
3938 | |
3939 | void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, KeyboardEvent& event) |
3940 | { |
3941 | ASSERT(event.type() == eventNames().keydownEvent); |
3942 | |
3943 | if (event.ctrlKey() || event.metaKey() || event.altGraphKey() || event.shiftKey()) |
3944 | return; |
3945 | |
3946 | Page* page = m_frame.page(); |
3947 | if (!page) |
3948 | return; |
3949 | |
3950 | if (!isSpatialNavigationEnabled(&m_frame)) |
3951 | return; |
3952 | |
3953 | // Arrows and other possible directional navigation keys can be used in design |
3954 | // mode editing. |
3955 | if (m_frame.document()->inDesignMode()) |
3956 | return; |
3957 | |
3958 | if (page->focusController().advanceFocus(focusDirection, &event)) |
3959 | event.setDefaultHandled(); |
3960 | } |
3961 | |
3962 | void EventHandler::defaultTabEventHandler(KeyboardEvent& event) |
3963 | { |
3964 | Ref<Frame> protectedFrame(m_frame); |
3965 | |
3966 | ASSERT(event.type() == eventNames().keydownEvent); |
3967 | |
3968 | // We should only advance focus on tabs if no special modifier keys are held down. |
3969 | if (event.ctrlKey() || event.metaKey() || event.altGraphKey()) |
3970 | return; |
3971 | |
3972 | Page* page = m_frame.page(); |
3973 | if (!page) |
3974 | return; |
3975 | if (!page->tabKeyCyclesThroughElements()) |
3976 | return; |
3977 | |
3978 | FocusDirection focusDirection = event.shiftKey() ? FocusDirectionBackward : FocusDirectionForward; |
3979 | |
3980 | // Tabs can be used in design mode editing. |
3981 | if (m_frame.document()->inDesignMode()) |
3982 | return; |
3983 | |
3984 | if (page->focusController().advanceFocus(focusDirection, &event)) |
3985 | event.setDefaultHandled(); |
3986 | } |
3987 | |
3988 | void EventHandler::sendScrollEvent() |
3989 | { |
3990 | Ref<Frame> protectedFrame(m_frame); |
3991 | setFrameWasScrolledByUser(); |
3992 | if (m_frame.view() && m_frame.document()) |
3993 | m_frame.document()->eventQueue().enqueueOrDispatchScrollEvent(*m_frame.document()); |
3994 | } |
3995 | |
3996 | void EventHandler::setFrameWasScrolledByUser() |
3997 | { |
3998 | FrameView* v = m_frame.view(); |
3999 | if (v) |
4000 | v->setWasScrolledByUser(true); |
4001 | } |
4002 | |
4003 | bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mouseEvent, Scrollbar* scrollbar) |
4004 | { |
4005 | if (!scrollbar || !scrollbar->enabled()) |
4006 | return false; |
4007 | setFrameWasScrolledByUser(); |
4008 | return scrollbar->mouseDown(mouseEvent.event()); |
4009 | } |
4010 | |
4011 | // If scrollbar (under mouse) is different from last, send a mouse exited. |
4012 | void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, SetOrClearLastScrollbar setOrClear) |
4013 | { |
4014 | if (m_lastScrollbarUnderMouse != scrollbar) { |
4015 | // Send mouse exited to the old scrollbar. |
4016 | if (m_lastScrollbarUnderMouse) |
4017 | m_lastScrollbarUnderMouse->mouseExited(); |
4018 | |
4019 | // Send mouse entered if we're setting a new scrollbar. |
4020 | if (scrollbar && setOrClear == SetOrClearLastScrollbar::Set) { |
4021 | scrollbar->mouseEntered(); |
4022 | m_lastScrollbarUnderMouse = makeWeakPtr(*scrollbar); |
4023 | } else |
4024 | m_lastScrollbarUnderMouse = nullptr; |
4025 | } |
4026 | } |
4027 | |
4028 | #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) |
4029 | static const AtomicString& eventNameForTouchPointState(PlatformTouchPoint::State state) |
4030 | { |
4031 | switch (state) { |
4032 | case PlatformTouchPoint::TouchReleased: |
4033 | return eventNames().touchendEvent; |
4034 | case PlatformTouchPoint::TouchCancelled: |
4035 | return eventNames().touchcancelEvent; |
4036 | case PlatformTouchPoint::TouchPressed: |
4037 | return eventNames().touchstartEvent; |
4038 | case PlatformTouchPoint::TouchMoved: |
4039 | return eventNames().touchmoveEvent; |
4040 | case PlatformTouchPoint::TouchStationary: |
4041 | // TouchStationary state is not converted to touch events, so fall through to assert. |
4042 | default: |
4043 | ASSERT_NOT_REACHED(); |
4044 | return emptyAtom(); |
4045 | } |
4046 | } |
4047 | |
4048 | static HitTestResult hitTestResultInFrame(Frame* frame, const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType) |
4049 | { |
4050 | HitTestResult result(point); |
4051 | |
4052 | if (!frame || !frame->contentRenderer()) |
4053 | return result; |
4054 | |
4055 | if (frame->view()) { |
4056 | IntRect rect = frame->view()->visibleContentRect(); |
4057 | if (!rect.contains(roundedIntPoint(point))) |
4058 | return result; |
4059 | } |
4060 | frame->contentRenderer()->hitTest(HitTestRequest(hitType), result); |
4061 | return result; |
4062 | } |
4063 | |
4064 | bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) |
4065 | { |
4066 | Ref<Frame> protectedFrame(m_frame); |
4067 | |
4068 | // First build up the lists to use for the 'touches', 'targetTouches' and 'changedTouches' attributes |
4069 | // in the JS event. See http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/ |
4070 | // for an overview of how these lists fit together. |
4071 | |
4072 | // Holds the complete set of touches on the screen and will be used as the 'touches' list in the JS event. |
4073 | RefPtr<TouchList> touches = TouchList::create(); |
4074 | |
4075 | // A different view on the 'touches' list above, filtered and grouped by event target. Used for the |
4076 | // 'targetTouches' list in the JS event. |
4077 | typedef HashMap<EventTarget*, RefPtr<TouchList>> TargetTouchesMap; |
4078 | TargetTouchesMap touchesByTarget; |
4079 | |
4080 | // Array of touches per state, used to assemble the 'changedTouches' list in the JS event. |
4081 | typedef HashSet<RefPtr<EventTarget>> EventTargetSet; |
4082 | struct { |
4083 | // The touches corresponding to the particular change state this struct instance represents. |
4084 | RefPtr<TouchList> m_touches; |
4085 | // Set of targets involved in m_touches. |
4086 | EventTargetSet m_targets; |
4087 | } changedTouches[PlatformTouchPoint::TouchStateEnd]; |
4088 | |
4089 | const Vector<PlatformTouchPoint>& points = event.touchPoints(); |
4090 | |
4091 | UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); |
4092 | |
4093 | bool freshTouchEvents = true; |
4094 | bool allTouchReleased = true; |
4095 | for (auto& point : points) { |
4096 | if (point.state() != PlatformTouchPoint::TouchPressed) |
4097 | freshTouchEvents = false; |
4098 | if (point.state() != PlatformTouchPoint::TouchReleased && point.state() != PlatformTouchPoint::TouchCancelled) |
4099 | allTouchReleased = false; |
4100 | } |
4101 | |
4102 | for (auto& point : points) { |
4103 | PlatformTouchPoint::State pointState = point.state(); |
4104 | LayoutPoint pagePoint = documentPointForWindowPoint(m_frame, point.pos()); |
4105 | |
4106 | HitTestRequest::HitTestRequestType hitType = HitTestRequest::TouchEvent; |
4107 | // The HitTestRequest types used for mouse events map quite adequately |
4108 | // to touch events. Note that in addition to meaning that the hit test |
4109 | // should affect the active state of the current node if necessary, |
4110 | // HitTestRequest::Active signifies that the hit test is taking place |
4111 | // with the mouse (or finger in this case) being pressed. |
4112 | switch (pointState) { |
4113 | case PlatformTouchPoint::TouchPressed: |
4114 | hitType |= HitTestRequest::Active; |
4115 | break; |
4116 | case PlatformTouchPoint::TouchMoved: |
4117 | hitType |= HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::ReadOnly; |
4118 | break; |
4119 | case PlatformTouchPoint::TouchReleased: |
4120 | case PlatformTouchPoint::TouchCancelled: |
4121 | hitType |= HitTestRequest::Release; |
4122 | break; |
4123 | case PlatformTouchPoint::TouchStationary: |
4124 | hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly; |
4125 | break; |
4126 | default: |
4127 | ASSERT_NOT_REACHED(); |
4128 | break; |
4129 | } |
4130 | |
4131 | if (shouldGesturesTriggerActive()) |
4132 | hitType |= HitTestRequest::ReadOnly; |
4133 | |
4134 | // Increment the platform touch id by 1 to avoid storing a key of 0 in the hashmap. |
4135 | unsigned touchPointTargetKey = point.id() + 1; |
4136 | RefPtr<EventTarget> touchTarget; |
4137 | if (pointState == PlatformTouchPoint::TouchPressed) { |
4138 | HitTestResult result; |
4139 | if (freshTouchEvents) { |
4140 | result = hitTestResultAtPoint(pagePoint, hitType); |
4141 | m_originatingTouchPointTargetKey = touchPointTargetKey; |
4142 | } else if (m_originatingTouchPointDocument.get() && m_originatingTouchPointDocument->frame()) { |
4143 | LayoutPoint pagePointInOriginatingDocument = documentPointForWindowPoint(*m_originatingTouchPointDocument->frame(), point.pos()); |
4144 | result = hitTestResultInFrame(m_originatingTouchPointDocument->frame(), pagePointInOriginatingDocument, hitType); |
4145 | if (!result.innerNode()) |
4146 | continue; |
4147 | } else |
4148 | continue; |
4149 | |
4150 | Element* element = result.targetElement(); |
4151 | ASSERT(element); |
4152 | |
4153 | if (element && InspectorInstrumentation::handleTouchEvent(m_frame, *element)) |
4154 | return true; |
4155 | |
4156 | Document& doc = element->document(); |
4157 | // Record the originating touch document even if it does not have a touch listener. |
4158 | if (freshTouchEvents) { |
4159 | m_originatingTouchPointDocument = &doc; |
4160 | freshTouchEvents = false; |
4161 | } |
4162 | if (!doc.hasTouchEventHandlers()) |
4163 | continue; |
4164 | m_originatingTouchPointTargets.set(touchPointTargetKey, element); |
4165 | touchTarget = element; |
4166 | } else if (pointState == PlatformTouchPoint::TouchReleased || pointState == PlatformTouchPoint::TouchCancelled) { |
4167 | // No need to perform a hit-test since we only need to unset :hover and :active states. |
4168 | if (!shouldGesturesTriggerActive() && allTouchReleased) |
4169 | m_frame.document()->updateHoverActiveState(hitType, 0); |
4170 | if (touchPointTargetKey == m_originatingTouchPointTargetKey) |
4171 | m_originatingTouchPointTargetKey = 0; |
4172 | |
4173 | // The target should be the original target for this touch, so get it from the hashmap. As it's a release or cancel |
4174 | // we also remove it from the map. |
4175 | touchTarget = m_originatingTouchPointTargets.take(touchPointTargetKey); |
4176 | } else |
4177 | // No hittest is performed on move or stationary, since the target is not allowed to change anyway. |
4178 | touchTarget = m_originatingTouchPointTargets.get(touchPointTargetKey); |
4179 | |
4180 | if (!is<Node>(touchTarget)) |
4181 | continue; |
4182 | auto& document = downcast<Node>(*touchTarget).document(); |
4183 | if (!document.hasTouchEventHandlers()) |
4184 | continue; |
4185 | auto* targetFrame = document.frame(); |
4186 | if (!targetFrame) |
4187 | continue; |
4188 | |
4189 | if (&m_frame != targetFrame) { |
4190 | // pagePoint should always be relative to the target elements containing frame. |
4191 | pagePoint = documentPointForWindowPoint(*targetFrame, point.pos()); |
4192 | } |
4193 | |
4194 | float scaleFactor = targetFrame->pageZoomFactor() * targetFrame->frameScaleFactor(); |
4195 | |
4196 | int adjustedPageX = lroundf(pagePoint.x() / scaleFactor); |
4197 | int adjustedPageY = lroundf(pagePoint.y() / scaleFactor); |
4198 | |
4199 | auto touch = Touch::create(targetFrame, touchTarget.get(), point.id(), |
4200 | point.screenPos().x(), point.screenPos().y(), adjustedPageX, adjustedPageY, |
4201 | point.radiusX(), point.radiusY(), point.rotationAngle(), point.force()); |
4202 | |
4203 | // Ensure this target's touch list exists, even if it ends up empty, so it can always be passed to TouchEvent::Create below. |
4204 | TargetTouchesMap::iterator targetTouchesIterator = touchesByTarget.find(touchTarget.get()); |
4205 | if (targetTouchesIterator == touchesByTarget.end()) |
4206 | targetTouchesIterator = touchesByTarget.set(touchTarget.get(), TouchList::create()).iterator; |
4207 | |
4208 | // touches and targetTouches should only contain information about touches still on the screen, so if this point is |
4209 | // released or cancelled it will only appear in the changedTouches list. |
4210 | if (pointState != PlatformTouchPoint::TouchReleased && pointState != PlatformTouchPoint::TouchCancelled) { |
4211 | touches->append(touch.copyRef()); |
4212 | targetTouchesIterator->value->append(touch.copyRef()); |
4213 | } |
4214 | |
4215 | // Now build up the correct list for changedTouches. |
4216 | // Note that any touches that are in the TouchStationary state (e.g. if |
4217 | // the user had several points touched but did not move them all) should |
4218 | // never be in the changedTouches list so we do not handle them explicitly here. |
4219 | // See https://bugs.webkit.org/show_bug.cgi?id=37609 for further discussion |
4220 | // about the TouchStationary state. |
4221 | if (pointState != PlatformTouchPoint::TouchStationary) { |
4222 | ASSERT(pointState < PlatformTouchPoint::TouchStateEnd); |
4223 | if (!changedTouches[pointState].m_touches) |
4224 | changedTouches[pointState].m_touches = TouchList::create(); |
4225 | changedTouches[pointState].m_touches->append(WTFMove(touch)); |
4226 | changedTouches[pointState].m_targets.add(touchTarget); |
4227 | } |
4228 | } |
4229 | m_touchPressed = touches->length() > 0; |
4230 | if (allTouchReleased) |
4231 | m_originatingTouchPointDocument = nullptr; |
4232 | |
4233 | // Now iterate the changedTouches list and m_targets within it, sending events to the targets as required. |
4234 | bool swallowedEvent = false; |
4235 | RefPtr<TouchList> emptyList = TouchList::create(); |
4236 | for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; ++state) { |
4237 | if (!changedTouches[state].m_touches) |
4238 | continue; |
4239 | |
4240 | // When sending a touch cancel event, use empty touches and targetTouches lists. |
4241 | bool isTouchCancelEvent = (state == PlatformTouchPoint::TouchCancelled); |
4242 | RefPtr<TouchList>& effectiveTouches(isTouchCancelEvent ? emptyList : touches); |
4243 | const AtomicString& stateName(eventNameForTouchPointState(static_cast<PlatformTouchPoint::State>(state))); |
4244 | |
4245 | for (auto& target : changedTouches[state].m_targets) { |
4246 | ASSERT(is<Node>(target)); |
4247 | |
4248 | RefPtr<TouchList> targetTouches(isTouchCancelEvent ? emptyList : touchesByTarget.get(target.get())); |
4249 | ASSERT(targetTouches); |
4250 | |
4251 | Ref<TouchEvent> touchEvent = TouchEvent::create(effectiveTouches.get(), targetTouches.get(), changedTouches[state].m_touches.get(), |
4252 | stateName, downcast<Node>(*target).document().windowProxy(), { }, event.modifiers()); |
4253 | target->dispatchEvent(touchEvent); |
4254 | swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled(); |
4255 | } |
4256 | } |
4257 | |
4258 | return swallowedEvent; |
4259 | } |
4260 | #endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) |
4261 | |
4262 | #if ENABLE(TOUCH_EVENTS) |
4263 | bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent& platformMouseEvent) |
4264 | { |
4265 | #if ENABLE(IOS_TOUCH_EVENTS) |
4266 | UNUSED_PARAM(platformMouseEvent); |
4267 | return false; |
4268 | #else |
4269 | if (!m_frame.settings().isTouchEventEmulationEnabled()) |
4270 | return false; |
4271 | |
4272 | PlatformEvent::Type eventType = platformMouseEvent.type(); |
4273 | if (eventType != PlatformEvent::MouseMoved && eventType != PlatformEvent::MousePressed && eventType != PlatformEvent::MouseReleased) |
4274 | return false; |
4275 | |
4276 | HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent); |
4277 | MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); |
4278 | if (mouseEvent.scrollbar() || subframeForHitTestResult(mouseEvent)) |
4279 | return false; |
4280 | |
4281 | // The order is important. This check should follow the subframe test: http://webkit.org/b/111292. |
4282 | if (eventType == PlatformEvent::MouseMoved && !m_touchPressed) |
4283 | return true; |
4284 | |
4285 | SyntheticSingleTouchEvent touchEvent(platformMouseEvent); |
4286 | return handleTouchEvent(touchEvent); |
4287 | #endif |
4288 | } |
4289 | #endif // ENABLE(TOUCH_EVENTS) |
4290 | |
4291 | void EventHandler::setLastKnownMousePosition(const PlatformMouseEvent& event) |
4292 | { |
4293 | m_mousePositionIsUnknown = false; |
4294 | m_lastKnownMousePosition = event.position(); |
4295 | m_lastKnownMouseGlobalPosition = event.globalPosition(); |
4296 | } |
4297 | |
4298 | void EventHandler::setImmediateActionStage(ImmediateActionStage stage) |
4299 | { |
4300 | m_immediateActionStage = stage; |
4301 | } |
4302 | |
4303 | #if !PLATFORM(COCOA) |
4304 | OptionSet<PlatformEvent::Modifier> EventHandler::accessKeyModifiers() |
4305 | { |
4306 | return PlatformEvent::Modifier::AltKey; |
4307 | } |
4308 | |
4309 | bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe) |
4310 | { |
4311 | subframe->eventHandler().handleMousePressEvent(mev.event()); |
4312 | return true; |
4313 | } |
4314 | |
4315 | bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe) |
4316 | { |
4317 | subframe->eventHandler().handleMouseReleaseEvent(mev.event()); |
4318 | return true; |
4319 | } |
4320 | |
4321 | bool EventHandler::widgetDidHandleWheelEvent(const PlatformWheelEvent& event, Widget& widget) |
4322 | { |
4323 | if (!is<FrameView>(widget)) |
4324 | return false; |
4325 | |
4326 | return downcast<FrameView>(widget).frame().eventHandler().handleWheelEvent(event); |
4327 | } |
4328 | |
4329 | bool EventHandler::tabsToAllFormControls(KeyboardEvent*) const |
4330 | { |
4331 | // We always allow tabs to all controls |
4332 | return true; |
4333 | } |
4334 | |
4335 | bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget) |
4336 | { |
4337 | return passMouseDownEventToWidget(renderWidget->widget()); |
4338 | } |
4339 | |
4340 | bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event) |
4341 | { |
4342 | // Figure out which view to send the event to. |
4343 | RenderObject* target = event.targetNode() ? event.targetNode()->renderer() : nullptr; |
4344 | if (!is<RenderWidget>(target)) |
4345 | return false; |
4346 | return passMouseDownEventToWidget(downcast<RenderWidget>(*target).widget()); |
4347 | } |
4348 | |
4349 | bool EventHandler::passMouseDownEventToWidget(Widget*) |
4350 | { |
4351 | notImplemented(); |
4352 | return false; |
4353 | } |
4354 | |
4355 | void EventHandler::focusDocumentView() |
4356 | { |
4357 | if (Page* page = m_frame.page()) |
4358 | page->focusController().setFocusedFrame(&m_frame); |
4359 | } |
4360 | #endif // !PLATFORM(COCOA) |
4361 | |
4362 | #if !PLATFORM(COCOA) && !PLATFORM(WIN) |
4363 | bool EventHandler::eventActivatedView(const PlatformMouseEvent&) const |
4364 | { |
4365 | notImplemented(); |
4366 | return false; |
4367 | } |
4368 | |
4369 | bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode) |
4370 | { |
4371 | subframe->eventHandler().handleMouseMoveEvent(mev.event(), hoveredNode); |
4372 | return true; |
4373 | } |
4374 | #endif // !PLATFORM(COCOA) && !PLATFORM(WIN) |
4375 | |
4376 | } // namespace WebCore |
4377 | |