Add WK2 event handling path for iOS, and make Mac and iOS code more similar
[WebKit-https.git] / Source / WebCore / page / ios / EventHandlerIOS.mm
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009 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 COMPUTER, 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 COMPUTER, 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 "BlockExceptions.h"
31 #import "Chrome.h"
32 #import "ChromeClient.h"
33 #import "FocusController.h"
34 #import "Frame.h"
35 #import "FrameView.h"
36 #import "KeyboardEvent.h"
37 #import "MouseEventWithHitTestResults.h"
38 #import "Page.h"
39 #import "PlatformEventFactoryIOS.h"
40 #import "PlatformKeyboardEvent.h"
41 #import "RenderWidget.h"
42 #import "WAKView.h"
43 #import "WAKWindow.h"
44 #import "WebEvent.h"
45 #import <wtf/NeverDestroyed.h>
46 #import <wtf/Noncopyable.h>
47
48 #if ENABLE(IOS_TOUCH_EVENTS)
49 #import <WebKitAdditions/EventHandlerIOSTouch.cpp>
50 #endif
51
52 namespace WebCore {
53
54 static RetainPtr<WebEvent>& currentEventSlot()
55 {
56     static NeverDestroyed<RetainPtr<WebEvent>> event;
57     return event;
58 }
59
60 WebEvent *EventHandler::currentEvent()
61 {
62     return currentEventSlot().get();
63 }
64
65 class CurrentEventScope {
66     WTF_MAKE_NONCOPYABLE(CurrentEventScope);
67 public:
68     CurrentEventScope(WebEvent *);
69     ~CurrentEventScope();
70
71 private:
72     RetainPtr<WebEvent> m_savedCurrentEvent;
73 #ifndef NDEBUG
74     RetainPtr<WebEvent> m_event;
75 #endif
76 };
77
78 inline CurrentEventScope::CurrentEventScope(WebEvent *event)
79     : m_savedCurrentEvent(currentEventSlot())
80 #ifndef NDEBUG
81     , m_event(event)
82 #endif
83 {
84     currentEventSlot() = event;
85 }
86
87 inline CurrentEventScope::~CurrentEventScope()
88 {
89     ASSERT(currentEventSlot() == m_event);
90     currentEventSlot() = m_savedCurrentEvent;
91 }
92
93 bool EventHandler::wheelEvent(WebEvent *event)
94 {
95     Page* page = m_frame.page();
96     if (!page)
97         return false;
98
99     CurrentEventScope scope(event);
100
101     bool eventWasHandled = handleWheelEvent(PlatformEventFactory::createPlatformWheelEvent(event));
102     event.wasHandled = eventWasHandled;
103     return eventWasHandled;
104 }
105
106 #if ENABLE(IOS_TOUCH_EVENTS)
107 void EventHandler::touchEvent(WebEvent *event)
108 {
109     CurrentEventScope scope(event);
110
111     event.wasHandled = handleTouchEvent(PlatformEventFactory::createPlatformTouchEvent(event));
112 }
113 #endif
114
115 bool EventHandler::tabsToAllFormControls(KeyboardEvent* event) const
116 {
117     Page* page = m_frame.page();
118     if (!page)
119         return false;
120
121     KeyboardUIMode keyboardUIMode = page->chrome().client().keyboardUIMode();
122     bool handlingOptionTab = isKeyboardOptionTab(event);
123
124     // If tab-to-links is off, option-tab always highlights all controls.
125     if ((keyboardUIMode & KeyboardAccessTabsToLinks) == 0 && handlingOptionTab)
126         return true;
127
128     // If system preferences say to include all controls, we always include all controls.
129     if (keyboardUIMode & KeyboardAccessFull)
130         return true;
131
132     // Otherwise tab-to-links includes all controls, unless the sense is flipped via option-tab.
133     if (keyboardUIMode & KeyboardAccessTabsToLinks)
134         return !handlingOptionTab;
135
136     return handlingOptionTab;
137 }
138
139 bool EventHandler::keyEvent(WebEvent *event)
140 {
141     BEGIN_BLOCK_OBJC_EXCEPTIONS;
142
143     ASSERT(event.type == WebEventKeyDown || event.type == WebEventKeyUp);
144
145     CurrentEventScope scope(event);
146     bool eventWasHandled = keyEvent(PlatformEventFactory::createPlatformKeyboardEvent(event));
147     event.wasHandled = eventWasHandled;
148     return eventWasHandled;
149
150     END_BLOCK_OBJC_EXCEPTIONS;
151
152     return false;
153 }
154
155 void EventHandler::focusDocumentView()
156 {
157     Page* page = m_frame.page();
158     if (!page)
159         return;
160
161     if (FrameView* frameView = m_frame.view()) {
162         if (NSView *documentView = frameView->documentView())
163             page->chrome().focusNSView(documentView);
164     }
165
166     page->focusController().setFocusedFrame(&m_frame);
167 }
168
169 bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event)
170 {
171     // Figure out which view to send the event to.
172     auto target = event.targetNode() ? event.targetNode()->renderer() : nullptr;
173     if (!target || !target->isWidget())
174         return false;
175
176     // Double-click events don't exist in Cocoa. Since passWidgetMouseDownEventToWidget() will
177     // just pass currentEvent down to the widget, we don't want to call it for events that
178     // don't correspond to Cocoa events. The mousedown/ups will have already been passed on as
179     // part of the pressed/released handling.
180     return passMouseDownEventToWidget(toRenderWidget(target)->widget());
181 }
182
183 bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget)
184 {
185     return passMouseDownEventToWidget(renderWidget->widget());
186 }
187
188 static bool lastEventIsMouseUp()
189 {
190     // Many AppKit widgets run their own event loops and consume events while the mouse is down.
191     // When they finish, currentEvent is the mouseUp that they exited on. We need to update
192     // the WebCore state with this mouseUp, which we never saw. This method lets us detect
193     // that state. Handling this was critical when we used AppKit widgets for form elements.
194     // It's not clear in what cases this is helpful now -- it's possible it can be removed. 
195
196     BEGIN_BLOCK_OBJC_EXCEPTIONS;
197     WebEvent *currentEventAfterHandlingMouseDown = [WAKWindow currentEvent];
198     return currentEventAfterHandlingMouseDown
199         && EventHandler::currentEvent() != currentEventAfterHandlingMouseDown
200         && currentEventAfterHandlingMouseDown.type == WebEventMouseUp
201         && currentEventAfterHandlingMouseDown.timestamp >= EventHandler::currentEvent().timestamp;
202     END_BLOCK_OBJC_EXCEPTIONS;
203
204     return false;
205 }
206
207 bool EventHandler::passMouseDownEventToWidget(Widget* pWidget)
208 {
209     // FIXME: This function always returns true. It should be changed either to return
210     // false in some cases or the return value should be removed.
211
212     RefPtr<Widget> widget = pWidget;
213
214     if (!widget) {
215         LOG_ERROR("hit a RenderWidget without a corresponding Widget, means a frame is half-constructed");
216         return true;
217     }
218
219     // In WebKit2 we will never have a native widget. Just return early and let the regular event handler machinery take care of
220     // dispatching the event.
221     if (!widget->platformWidget())
222         return false;
223
224     BEGIN_BLOCK_OBJC_EXCEPTIONS;
225
226     NSView *nodeView = widget->platformWidget();
227     ASSERT(nodeView);
228     ASSERT([nodeView superview]);
229     NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:currentEvent().locationInWindow fromView:nil]];
230     if (!view) {
231         // We probably hit the border of a RenderWidget
232         return true;
233     }
234
235     Page* page = m_frame.page();
236     if (!page)
237         return true;
238
239     if (page->chrome().client().firstResponder() != view) {
240         // Normally [NSWindow sendEvent:] handles setting the first responder.
241         // But in our case, the event was sent to the view representing the entire web page.
242         if ([view acceptsFirstResponder] && [view needsPanelToBecomeKey])
243             page->chrome().client().makeFirstResponder(view);
244     }
245
246     // We need to "defer loading" while tracking the mouse, because tearing down the
247     // page while an AppKit control is tracking the mouse can cause a crash.
248
249     // FIXME: In theory, WebCore now tolerates tear-down while tracking the
250     // mouse. We should confirm that, and then remove the deferrsLoading
251     // hack entirely.
252
253     bool wasDeferringLoading = page->defersLoading();
254     if (!wasDeferringLoading)
255         page->setDefersLoading(true);
256
257     ASSERT(!m_sendingEventToSubview);
258     m_sendingEventToSubview = true;
259
260     {
261         WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
262         [view mouseDown:currentEvent()];
263     }
264
265     m_sendingEventToSubview = false;
266     
267     if (!wasDeferringLoading)
268         page->setDefersLoading(false);
269
270     // Remember which view we sent the event to, so we can direct the release event properly.
271     m_mouseDownView = view;
272     m_mouseDownWasInSubframe = false;
273
274     // Many AppKit widgets run their own event loops and consume events while the mouse is down.
275     // When they finish, currentEvent is the mouseUp that they exited on.  We need to update
276     // the EventHandler state with this mouseUp, which we never saw.
277     // If this event isn't a mouseUp, we assume that the mouseUp will be coming later.  There
278     // is a hole here if the widget consumes both the mouseUp and subsequent events.
279     if (lastEventIsMouseUp())
280         m_mousePressed = false;
281
282     END_BLOCK_OBJC_EXCEPTIONS;
283
284     return true;
285 }
286
287 // Note that this does the same kind of check as [target isDescendantOf:superview].
288 // There are two differences: This is a lot slower because it has to walk the whole
289 // tree, and this works in cases where the target has already been deallocated.
290 static bool findViewInSubviews(NSView *superview, NSView *target)
291 {
292     BEGIN_BLOCK_OBJC_EXCEPTIONS;
293     NSEnumerator *e = [[superview subviews] objectEnumerator];
294     NSView *subview;
295     while ((subview = [e nextObject])) {
296         if (subview == target || findViewInSubviews(subview, target)) {
297             return true;
298         }
299     }
300     END_BLOCK_OBJC_EXCEPTIONS;
301
302     return false;
303 }
304
305 NSView *EventHandler::mouseDownViewIfStillGood()
306 {
307     // Since we have no way of tracking the lifetime of m_mouseDownView, we have to assume that
308     // it could be deallocated already. We search for it in our subview tree; if we don't find
309     // it, we set it to nil.
310     NSView *mouseDownView = m_mouseDownView;
311     if (!mouseDownView) {
312         return nil;
313     }
314     FrameView* topFrameView = m_frame.view();
315     NSView *topView = topFrameView ? topFrameView->platformWidget() : nil;
316     if (!topView || !findViewInSubviews(topView, mouseDownView)) {
317         m_mouseDownView = nil;
318         return nil;
319     }
320     return mouseDownView;
321 }
322
323 bool EventHandler::eventActivatedView(const PlatformMouseEvent&) const
324 {
325     return false;
326 }
327
328 bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&)
329 {
330     NSView *view = mouseDownViewIfStillGood();
331     if (!view)
332         return false;
333
334     if (!m_mouseDownWasInSubframe) {
335         ASSERT(!m_sendingEventToSubview);
336         m_sendingEventToSubview = true;
337         BEGIN_BLOCK_OBJC_EXCEPTIONS;
338         [view mouseUp:currentEvent()];
339         END_BLOCK_OBJC_EXCEPTIONS;
340         m_sendingEventToSubview = false;
341     }
342  
343     return true;
344 }
345     
346 bool EventHandler::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframe, HitTestResult* hoveredNode)
347 {
348     BEGIN_BLOCK_OBJC_EXCEPTIONS;
349
350     WebEventType currentEventType = currentEvent().type;
351     switch (currentEventType) {
352         case WebEventMouseMoved: {
353             // Since we're passing in currentNSEvent() here, we can call
354             // handleMouseMoveEvent() directly, since the save/restore of
355             // currentNSEvent() that mouseMoved() does would have no effect.
356             ASSERT(!m_sendingEventToSubview);
357             m_sendingEventToSubview = true;
358             subframe->eventHandler().handleMouseMoveEvent(currentPlatformMouseEvent(), hoveredNode);
359             m_sendingEventToSubview = false;
360             return true;
361         }
362         case WebEventMouseDown: {
363             Node* node = event.targetNode();
364             if (!node)
365                 return false;
366             auto renderer = node->renderer();
367             if (!renderer || !renderer->isWidget())
368                 return false;
369             Widget* widget = toRenderWidget(renderer)->widget();
370             if (!widget || !widget->isFrameView())
371                 return false;
372             if (!passWidgetMouseDownEventToWidget(toRenderWidget(renderer)))
373                 return false;
374             m_mouseDownWasInSubframe = true;
375             return true;
376         }
377         case WebEventMouseUp: {
378             if (!m_mouseDownWasInSubframe)
379                 return false;
380             ASSERT(!m_sendingEventToSubview);
381             m_sendingEventToSubview = true;
382             subframe->eventHandler().handleMouseReleaseEvent(currentPlatformMouseEvent());
383             m_sendingEventToSubview = false;
384             return true;
385         }
386         case WebEventKeyDown:
387         case WebEventKeyUp:
388         case WebEventScrollWheel:
389         case WebEventTouchBegin:
390         case WebEventTouchCancel:
391         case WebEventTouchChange:
392         case WebEventTouchEnd:
393             return false;
394     }
395     END_BLOCK_OBJC_EXCEPTIONS;
396
397     return false;
398 }
399
400 bool EventHandler::passWheelEventToWidget(const PlatformWheelEvent&, Widget* widget)
401 {
402     BEGIN_BLOCK_OBJC_EXCEPTIONS;
403
404     if (!widget)
405         return false;
406
407     NSView* nodeView = widget->platformWidget();
408     if (!nodeView) {
409         // WK2 code path. No wheel events on iOS anyway.
410         return false;
411     }
412
413     if (currentEvent().type != WebEventScrollWheel || m_sendingEventToSubview || !widget)
414         return false;
415
416     ASSERT(nodeView);
417     ASSERT([nodeView superview]);
418     NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:currentEvent().locationInWindow fromView:nil]];
419     if (!view) {
420         // We probably hit the border of a RenderWidget
421         return false;
422     }
423
424     ASSERT(!m_sendingEventToSubview);
425     m_sendingEventToSubview = true;
426     [view scrollWheel:currentEvent()];
427     m_sendingEventToSubview = false;
428     return true;
429
430     END_BLOCK_OBJC_EXCEPTIONS;
431     return false;
432 }
433
434 void EventHandler::mouseDown(WebEvent *event)
435 {
436     FrameView* v = m_frame.view();
437     if (!v || m_sendingEventToSubview)
438         return;
439
440     BEGIN_BLOCK_OBJC_EXCEPTIONS;
441
442     // FIXME: Why is this here? EventHandler::handleMousePressEvent() calls it.
443     m_frame.loader().resetMultipleFormSubmissionProtection();
444
445     m_mouseDownView = nil;
446
447     CurrentEventScope scope(event);
448
449     event.wasHandled = handleMousePressEvent(currentPlatformMouseEvent());
450
451     END_BLOCK_OBJC_EXCEPTIONS;
452 }
453
454 void EventHandler::mouseUp(WebEvent *event)
455 {
456     FrameView* v = m_frame.view();
457     if (!v || m_sendingEventToSubview)
458         return;
459
460     BEGIN_BLOCK_OBJC_EXCEPTIONS;
461
462     CurrentEventScope scope(event);
463
464     event.wasHandled = handleMouseReleaseEvent(currentPlatformMouseEvent());
465
466     m_mouseDownView = nil;
467
468     END_BLOCK_OBJC_EXCEPTIONS;
469 }
470
471 void EventHandler::mouseMoved(WebEvent *event)
472 {
473     // Reject a mouse moved if the button is down - screws up tracking during autoscroll
474     // These happen because WebKit sometimes has to fake up moved events.
475     if (!m_frame.view() || m_mousePressed || m_sendingEventToSubview)
476         return;
477
478     BEGIN_BLOCK_OBJC_EXCEPTIONS;
479
480     m_frame.document()->updateStyleIfNeeded();
481
482     WKBeginObservingContentChanges(true);
483     CurrentEventScope scope(event);
484     event.wasHandled = mouseMoved(currentPlatformMouseEvent());
485     
486     // FIXME: Why is this here?
487     m_frame.document()->updateStyleIfNeeded();
488     WKStopObservingContentChanges();
489
490     END_BLOCK_OBJC_EXCEPTIONS;
491 }
492
493 static bool frameHasPlatformWidget(const Frame& frame)
494 {
495     if (FrameView* frameView = frame.view()) {
496         if (frameView->platformWidget())
497             return true;
498     }
499
500     return false;
501 }
502
503 bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
504 {
505     // WebKit1 code path.
506     if (frameHasPlatformWidget(m_frame))
507         return passSubframeEventToSubframe(mev, subframe);
508
509     // WebKit2 code path.
510     subframe->eventHandler().handleMousePressEvent(mev.event());
511     return true;
512 }
513
514 bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode)
515 {
516     // WebKit1 code path.
517     if (frameHasPlatformWidget(m_frame))
518         return passSubframeEventToSubframe(mev, subframe, hoveredNode);
519
520     subframe->eventHandler().handleMouseMoveEvent(mev.event(), hoveredNode);
521     return true;
522 }
523
524 bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
525 {
526     // WebKit1 code path.
527     if (frameHasPlatformWidget(m_frame))
528         return passSubframeEventToSubframe(mev, subframe);
529
530     // WebKit2 code path.
531     subframe->eventHandler().handleMouseReleaseEvent(mev.event());
532     return true;
533 }
534
535 unsigned EventHandler::accessKeyModifiers()
536 {
537     // Control+Option key combinations are usually unused on Mac OS X, but not when VoiceOver is enabled.
538     // So, we use Control in this case, even though it conflicts with Emacs-style key bindings.
539     // See <https://bugs.webkit.org/show_bug.cgi?id=21107> for more detail.
540     if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
541         return PlatformKeyboardEvent::CtrlKey;
542
543     return PlatformKeyboardEvent::CtrlKey | PlatformKeyboardEvent::AltKey;
544 }
545
546 PlatformMouseEvent EventHandler::currentPlatformMouseEvent() const
547 {
548     return PlatformEventFactory::createPlatformMouseEvent(currentEvent());
549 }
550
551 }