Drag event DataTransfer has unexpected types "dyn.ah62d4..."
[WebKit-https.git] / Source / WebCore / page / ios / EventHandlerIOS.mm
1 /*
2  * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "EventHandler.h"
28
29 #import "AXObjectCache.h"
30 #import "Chrome.h"
31 #import "ChromeClient.h"
32 #import "DataTransfer.h"
33 #import "DragState.h"
34 #import "FocusController.h"
35 #import "Frame.h"
36 #import "FrameView.h"
37 #import "KeyboardEvent.h"
38 #import "MouseEventWithHitTestResults.h"
39 #import "Page.h"
40 #import "Pasteboard.h"
41 #import "PlatformEventFactoryIOS.h"
42 #import "PlatformKeyboardEvent.h"
43 #import "RenderWidget.h"
44 #import "WAKView.h"
45 #import "WAKWindow.h"
46 #import "WebEvent.h"
47 #import <wtf/BlockObjCExceptions.h>
48 #import <wtf/NeverDestroyed.h>
49 #import <wtf/Noncopyable.h>
50 #import <wtf/SetForScope.h>
51
52 #if ENABLE(IOS_TOUCH_EVENTS)
53 #import <WebKitAdditions/EventHandlerIOSTouch.cpp>
54 #endif
55
56 namespace WebCore {
57
58 static RetainPtr<WebEvent>& currentEventSlot()
59 {
60     static NeverDestroyed<RetainPtr<WebEvent>> event;
61     return event;
62 }
63
64 WebEvent *EventHandler::currentEvent()
65 {
66     return currentEventSlot().get();
67 }
68
69 class CurrentEventScope {
70     WTF_MAKE_NONCOPYABLE(CurrentEventScope);
71 public:
72     CurrentEventScope(WebEvent *);
73     ~CurrentEventScope();
74
75 private:
76     RetainPtr<WebEvent> m_savedCurrentEvent;
77 #ifndef NDEBUG
78     RetainPtr<WebEvent> m_event;
79 #endif
80 };
81
82 inline CurrentEventScope::CurrentEventScope(WebEvent *event)
83     : m_savedCurrentEvent(currentEventSlot())
84 #ifndef NDEBUG
85     , m_event(event)
86 #endif
87 {
88     currentEventSlot() = event;
89 }
90
91 inline CurrentEventScope::~CurrentEventScope()
92 {
93     ASSERT(currentEventSlot() == m_event);
94     currentEventSlot() = m_savedCurrentEvent;
95 }
96
97 bool EventHandler::wheelEvent(WebEvent *event)
98 {
99     Page* page = m_frame.page();
100     if (!page)
101         return false;
102
103     CurrentEventScope scope(event);
104
105     bool eventWasHandled = handleWheelEvent(PlatformEventFactory::createPlatformWheelEvent(event));
106     event.wasHandled = eventWasHandled;
107     return eventWasHandled;
108 }
109
110 #if ENABLE(IOS_TOUCH_EVENTS)
111
112 bool EventHandler::dispatchSimulatedTouchEvent(IntPoint location)
113 {
114     bool handled = handleTouchEvent(PlatformEventFactory::createPlatformSimulatedTouchEvent(PlatformEvent::TouchStart, location));
115     handled |= handleTouchEvent(PlatformEventFactory::createPlatformSimulatedTouchEvent(PlatformEvent::TouchEnd, location));
116     return handled;
117 }
118     
119 void EventHandler::touchEvent(WebEvent *event)
120 {
121     CurrentEventScope scope(event);
122
123     event.wasHandled = handleTouchEvent(PlatformEventFactory::createPlatformTouchEvent(event));
124 }
125 #endif
126
127 bool EventHandler::tabsToAllFormControls(KeyboardEvent& event) const
128 {
129     Page* page = m_frame.page();
130     if (!page)
131         return false;
132
133     KeyboardUIMode keyboardUIMode = page->chrome().client().keyboardUIMode();
134     bool handlingOptionTab = isKeyboardOptionTab(event);
135
136     // If tab-to-links is off, option-tab always highlights all controls.
137     if ((keyboardUIMode & KeyboardAccessTabsToLinks) == 0 && handlingOptionTab)
138         return true;
139
140     // If system preferences say to include all controls, we always include all controls.
141     if (keyboardUIMode & KeyboardAccessFull)
142         return true;
143
144     // Otherwise tab-to-links includes all controls, unless the sense is flipped via option-tab.
145     if (keyboardUIMode & KeyboardAccessTabsToLinks)
146         return !handlingOptionTab;
147
148     return handlingOptionTab;
149 }
150
151 bool EventHandler::keyEvent(WebEvent *event)
152 {
153     BEGIN_BLOCK_OBJC_EXCEPTIONS;
154
155     ASSERT(event.type == WebEventKeyDown || event.type == WebEventKeyUp);
156
157     CurrentEventScope scope(event);
158     bool eventWasHandled = keyEvent(PlatformEventFactory::createPlatformKeyboardEvent(event));
159     event.wasHandled = eventWasHandled;
160     return eventWasHandled;
161
162     END_BLOCK_OBJC_EXCEPTIONS;
163
164     return false;
165 }
166
167 void EventHandler::focusDocumentView()
168 {
169     Page* page = m_frame.page();
170     if (!page)
171         return;
172
173     Ref<Frame> protectedFrame(m_frame);
174
175     if (FrameView* frameView = m_frame.view()) {
176         if (NSView *documentView = frameView->documentView())
177             page->chrome().focusNSView(documentView);
178     }
179
180     page->focusController().setFocusedFrame(&m_frame);
181 }
182
183 bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event)
184 {
185     // Figure out which view to send the event to.
186     auto* target = event.targetNode() ? event.targetNode()->renderer() : nullptr;
187     if (!is<RenderWidget>(target))
188         return false;
189
190     // Double-click events don't exist in Cocoa. Since passWidgetMouseDownEventToWidget() will
191     // just pass currentEvent down to the widget, we don't want to call it for events that
192     // don't correspond to Cocoa events. The mousedown/ups will have already been passed on as
193     // part of the pressed/released handling.
194     return passMouseDownEventToWidget(downcast<RenderWidget>(*target).widget());
195 }
196
197 bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget)
198 {
199     return passMouseDownEventToWidget(renderWidget->widget());
200 }
201
202 static bool lastEventIsMouseUp()
203 {
204     // Many AppKit widgets run their own event loops and consume events while the mouse is down.
205     // When they finish, currentEvent is the mouseUp that they exited on. We need to update
206     // the WebCore state with this mouseUp, which we never saw. This method lets us detect
207     // that state. Handling this was critical when we used AppKit widgets for form elements.
208     // It's not clear in what cases this is helpful now -- it's possible it can be removed. 
209
210     BEGIN_BLOCK_OBJC_EXCEPTIONS;
211     WebEvent *currentEventAfterHandlingMouseDown = [WAKWindow currentEvent];
212     return currentEventAfterHandlingMouseDown
213         && EventHandler::currentEvent() != currentEventAfterHandlingMouseDown
214         && currentEventAfterHandlingMouseDown.type == WebEventMouseUp
215         && currentEventAfterHandlingMouseDown.timestamp >= EventHandler::currentEvent().timestamp;
216     END_BLOCK_OBJC_EXCEPTIONS;
217
218     return false;
219 }
220
221 bool EventHandler::passMouseDownEventToWidget(Widget* pWidget)
222 {
223     // FIXME: This function always returns true. It should be changed either to return
224     // false in some cases or the return value should be removed.
225
226     RefPtr<Widget> widget = pWidget;
227
228     if (!widget) {
229         LOG_ERROR("hit a RenderWidget without a corresponding Widget, means a frame is half-constructed");
230         return true;
231     }
232
233     // In WebKit2 we will never have a native widget. Just return early and let the regular event handler machinery take care of
234     // dispatching the event.
235     if (!widget->platformWidget())
236         return false;
237
238     BEGIN_BLOCK_OBJC_EXCEPTIONS;
239
240     NSView *nodeView = widget->platformWidget();
241     ASSERT(nodeView);
242     ASSERT([nodeView superview]);
243     NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:currentEvent().locationInWindow fromView:nil]];
244     if (!view) {
245         // We probably hit the border of a RenderWidget
246         return true;
247     }
248
249     Page* page = m_frame.page();
250     if (!page)
251         return true;
252
253     if (page->chrome().client().firstResponder() != view) {
254         // Normally [NSWindow sendEvent:] handles setting the first responder.
255         // But in our case, the event was sent to the view representing the entire web page.
256         if ([view acceptsFirstResponder] && [view needsPanelToBecomeKey])
257             page->chrome().client().makeFirstResponder(view);
258     }
259
260     // We need to "defer loading" while tracking the mouse, because tearing down the
261     // page while an AppKit control is tracking the mouse can cause a crash.
262
263     // FIXME: In theory, WebCore now tolerates tear-down while tracking the
264     // mouse. We should confirm that, and then remove the deferrsLoading
265     // hack entirely.
266
267     bool wasDeferringLoading = page->defersLoading();
268     if (!wasDeferringLoading)
269         page->setDefersLoading(true);
270
271     ASSERT(!m_sendingEventToSubview);
272     m_sendingEventToSubview = true;
273
274     {
275         WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
276         [view mouseDown:currentEvent()];
277     }
278
279     m_sendingEventToSubview = false;
280     
281     if (!wasDeferringLoading)
282         page->setDefersLoading(false);
283
284     // Remember which view we sent the event to, so we can direct the release event properly.
285     m_mouseDownView = view;
286     m_mouseDownWasInSubframe = false;
287
288     // Many AppKit widgets run their own event loops and consume events while the mouse is down.
289     // When they finish, currentEvent is the mouseUp that they exited on.  We need to update
290     // the EventHandler state with this mouseUp, which we never saw.
291     // If this event isn't a mouseUp, we assume that the mouseUp will be coming later.  There
292     // is a hole here if the widget consumes both the mouseUp and subsequent events.
293     if (lastEventIsMouseUp())
294         m_mousePressed = false;
295
296     END_BLOCK_OBJC_EXCEPTIONS;
297
298     return true;
299 }
300
301 // Note that this does the same kind of check as [target isDescendantOf:superview].
302 // There are two differences: This is a lot slower because it has to walk the whole
303 // tree, and this works in cases where the target has already been deallocated.
304 static bool findViewInSubviews(NSView *superview, NSView *target)
305 {
306     BEGIN_BLOCK_OBJC_EXCEPTIONS;
307     NSEnumerator *e = [[superview subviews] objectEnumerator];
308     NSView *subview;
309     while ((subview = [e nextObject])) {
310         if (subview == target || findViewInSubviews(subview, target)) {
311             return true;
312         }
313     }
314     END_BLOCK_OBJC_EXCEPTIONS;
315
316     return false;
317 }
318
319 NSView *EventHandler::mouseDownViewIfStillGood()
320 {
321     // Since we have no way of tracking the lifetime of m_mouseDownView, we have to assume that
322     // it could be deallocated already. We search for it in our subview tree; if we don't find
323     // it, we set it to nil.
324     NSView *mouseDownView = m_mouseDownView;
325     if (!mouseDownView) {
326         return nil;
327     }
328     FrameView* topFrameView = m_frame.view();
329     NSView *topView = topFrameView ? topFrameView->platformWidget() : nil;
330     if (!topView || !findViewInSubviews(topView, mouseDownView)) {
331         m_mouseDownView = nil;
332         return nil;
333     }
334     return mouseDownView;
335 }
336
337 bool EventHandler::eventActivatedView(const PlatformMouseEvent&) const
338 {
339     return false;
340 }
341
342 bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&)
343 {
344     NSView *view = mouseDownViewIfStillGood();
345     if (!view)
346         return false;
347
348     if (!m_mouseDownWasInSubframe) {
349         ASSERT(!m_sendingEventToSubview);
350         m_sendingEventToSubview = true;
351         BEGIN_BLOCK_OBJC_EXCEPTIONS;
352         [view mouseUp:currentEvent()];
353         END_BLOCK_OBJC_EXCEPTIONS;
354         m_sendingEventToSubview = false;
355     }
356  
357     return true;
358 }
359     
360 bool EventHandler::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframe, HitTestResult* hoveredNode)
361 {
362     BEGIN_BLOCK_OBJC_EXCEPTIONS;
363
364     WebEventType currentEventType = currentEvent().type;
365     switch (currentEventType) {
366         case WebEventMouseMoved: {
367             // Since we're passing in currentNSEvent() here, we can call
368             // handleMouseMoveEvent() directly, since the save/restore of
369             // currentNSEvent() that mouseMoved() does would have no effect.
370             ASSERT(!m_sendingEventToSubview);
371             m_sendingEventToSubview = true;
372             subframe->eventHandler().handleMouseMoveEvent(currentPlatformMouseEvent(), hoveredNode);
373             m_sendingEventToSubview = false;
374             return true;
375         }
376         case WebEventMouseDown: {
377             Node* node = event.targetNode();
378             if (!node)
379                 return false;
380             auto* renderer = node->renderer();
381             if (!is<RenderWidget>(renderer))
382                 return false;
383             Widget* widget = downcast<RenderWidget>(*renderer).widget();
384             if (!widget || !widget->isFrameView())
385                 return false;
386             if (!passWidgetMouseDownEventToWidget(downcast<RenderWidget>(renderer)))
387                 return false;
388             m_mouseDownWasInSubframe = true;
389             return true;
390         }
391         case WebEventMouseUp: {
392             if (!m_mouseDownWasInSubframe)
393                 return false;
394             ASSERT(!m_sendingEventToSubview);
395             m_sendingEventToSubview = true;
396             subframe->eventHandler().handleMouseReleaseEvent(currentPlatformMouseEvent());
397             m_sendingEventToSubview = false;
398             return true;
399         }
400         case WebEventKeyDown:
401         case WebEventKeyUp:
402         case WebEventScrollWheel:
403         case WebEventTouchBegin:
404         case WebEventTouchCancel:
405         case WebEventTouchChange:
406         case WebEventTouchEnd:
407             return false;
408     }
409     END_BLOCK_OBJC_EXCEPTIONS;
410
411     return false;
412 }
413
414 bool EventHandler::widgetDidHandleWheelEvent(const PlatformWheelEvent&, Widget& widget)
415 {
416     BEGIN_BLOCK_OBJC_EXCEPTIONS;
417
418     NSView* nodeView = widget.platformWidget();
419     if (!nodeView) {
420         // WK2 code path. No wheel events on iOS anyway.
421         return false;
422     }
423
424     if (currentEvent().type != WebEventScrollWheel || m_sendingEventToSubview)
425         return false;
426
427     ASSERT(nodeView);
428     ASSERT([nodeView superview]);
429     NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:currentEvent().locationInWindow fromView:nil]];
430     if (!view) {
431         // We probably hit the border of a RenderWidget
432         return false;
433     }
434
435     ASSERT(!m_sendingEventToSubview);
436     m_sendingEventToSubview = true;
437     [view scrollWheel:currentEvent()];
438     m_sendingEventToSubview = false;
439     return true;
440
441     END_BLOCK_OBJC_EXCEPTIONS;
442     return false;
443 }
444
445 void EventHandler::mouseDown(WebEvent *event)
446 {
447     FrameView* v = m_frame.view();
448     if (!v || m_sendingEventToSubview)
449         return;
450
451     BEGIN_BLOCK_OBJC_EXCEPTIONS;
452
453     // FIXME: Why is this here? EventHandler::handleMousePressEvent() calls it.
454     m_frame.loader().resetMultipleFormSubmissionProtection();
455
456     m_mouseDownView = nil;
457
458     CurrentEventScope scope(event);
459
460     event.wasHandled = handleMousePressEvent(currentPlatformMouseEvent());
461
462     END_BLOCK_OBJC_EXCEPTIONS;
463 }
464
465 void EventHandler::mouseUp(WebEvent *event)
466 {
467     FrameView* v = m_frame.view();
468     if (!v || m_sendingEventToSubview)
469         return;
470
471     BEGIN_BLOCK_OBJC_EXCEPTIONS;
472
473     CurrentEventScope scope(event);
474
475     event.wasHandled = handleMouseReleaseEvent(currentPlatformMouseEvent());
476
477     m_mouseDownView = nil;
478
479     END_BLOCK_OBJC_EXCEPTIONS;
480 }
481
482 void EventHandler::mouseMoved(WebEvent *event)
483 {
484     // Reject a mouse moved if the button is down - screws up tracking during autoscroll
485     // These happen because WebKit sometimes has to fake up moved events.
486     if (!m_frame.view() || m_mousePressed || m_sendingEventToSubview)
487         return;
488
489     BEGIN_BLOCK_OBJC_EXCEPTIONS;
490
491     m_frame.document()->updateStyleIfNeeded();
492
493     WKBeginObservingContentChanges(true);
494     CurrentEventScope scope(event);
495     event.wasHandled = mouseMoved(currentPlatformMouseEvent());
496     
497     // FIXME: Why is this here?
498     m_frame.document()->updateStyleIfNeeded();
499     WKStopObservingContentChanges();
500
501     END_BLOCK_OBJC_EXCEPTIONS;
502 }
503
504 static bool frameHasPlatformWidget(const Frame& frame)
505 {
506     if (FrameView* frameView = frame.view()) {
507         if (frameView->platformWidget())
508             return true;
509     }
510
511     return false;
512 }
513
514 bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
515 {
516     // WebKit1 code path.
517     if (frameHasPlatformWidget(m_frame))
518         return passSubframeEventToSubframe(mev, subframe);
519
520     // WebKit2 code path.
521     subframe->eventHandler().handleMousePressEvent(mev.event());
522     return true;
523 }
524
525 bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode)
526 {
527     // WebKit1 code path.
528     if (frameHasPlatformWidget(m_frame))
529         return passSubframeEventToSubframe(mev, subframe, hoveredNode);
530
531     subframe->eventHandler().handleMouseMoveEvent(mev.event(), hoveredNode);
532     return true;
533 }
534
535 bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
536 {
537     // WebKit1 code path.
538     if (frameHasPlatformWidget(m_frame))
539         return passSubframeEventToSubframe(mev, subframe);
540
541     // WebKit2 code path.
542     subframe->eventHandler().handleMouseReleaseEvent(mev.event());
543     return true;
544 }
545
546 OptionSet<PlatformEvent::Modifier> EventHandler::accessKeyModifiers()
547 {
548     // Control+Option key combinations are usually unused on Mac OS X, but not when VoiceOver is enabled.
549     // So, we use Control in this case, even though it conflicts with Emacs-style key bindings.
550     // See <https://bugs.webkit.org/show_bug.cgi?id=21107> for more detail.
551     if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
552         return PlatformEvent::Modifier::CtrlKey;
553
554     return { PlatformEvent::Modifier::CtrlKey, PlatformEvent::Modifier::AltKey };
555 }
556
557 PlatformMouseEvent EventHandler::currentPlatformMouseEvent() const
558 {
559     return PlatformEventFactory::createPlatformMouseEvent(currentEvent());
560 }
561
562 #if ENABLE(DRAG_SUPPORT)
563
564 bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&)
565 {
566     return false;
567 }
568
569 bool EventHandler::tryToBeginDataInteractionAtPoint(const IntPoint& clientPosition, const IntPoint&)
570 {
571     Ref<Frame> protectedFrame(m_frame);
572
573     auto* document = m_frame.document();
574     if (!document)
575         return false;
576
577     document->updateLayoutIgnorePendingStylesheets();
578
579     FloatPoint adjustedClientPositionAsFloatPoint(clientPosition);
580     protectedFrame->nodeRespondingToClickEvents(clientPosition, adjustedClientPositionAsFloatPoint);
581     IntPoint adjustedClientPosition = roundedIntPoint(adjustedClientPositionAsFloatPoint);
582     IntPoint adjustedGlobalPosition = protectedFrame->view()->windowToContents(adjustedClientPosition);
583
584     PlatformMouseEvent syntheticMousePressEvent(adjustedClientPosition, adjustedGlobalPosition, LeftButton, PlatformEvent::MousePressed, 1, false, false, false, false, WallTime::now(), 0, NoTap);
585     PlatformMouseEvent syntheticMouseMoveEvent(adjustedClientPosition, adjustedGlobalPosition, LeftButton, PlatformEvent::MouseMoved, 0, false, false, false, false, WallTime::now(), 0, NoTap);
586
587     HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent);
588     auto documentPoint = protectedFrame->view() ? protectedFrame->view()->windowToContents(syntheticMouseMoveEvent.position()) : syntheticMouseMoveEvent.position();
589     auto hitTestedMouseEvent = document->prepareMouseEvent(request, documentPoint, syntheticMouseMoveEvent);
590
591     RefPtr<Frame> subframe = subframeForHitTestResult(hitTestedMouseEvent);
592     if (subframe && subframe->eventHandler().tryToBeginDataInteractionAtPoint(adjustedClientPosition, adjustedGlobalPosition))
593         return true;
594
595     // FIXME: This needs to be refactored, along with handleMousePressEvent and handleMouseMoveEvent, so that state associated only with dragging
596     // lives solely in the DragController, and so that we don't need to pretend that a mouse press and mouse move have already occurred here.
597     m_mouseDownMayStartDrag = eventMayStartDrag(syntheticMousePressEvent);
598     if (!m_mouseDownMayStartDrag)
599         return false;
600
601     SetForScope<bool> mousePressed(m_mousePressed, true);
602     dragState().source = nullptr;
603     m_mouseDownPos = protectedFrame->view()->windowToContents(syntheticMouseMoveEvent.position());
604
605     return handleMouseDraggedEvent(hitTestedMouseEvent, DontCheckDragHysteresis);
606 }
607
608 #endif
609
610 }