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