1/*
2 * Copyright (C) 2006, 2007, 2008, 2009, 2014 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#include "config.h"
27#include "EventHandler.h"
28
29#include "AXObjectCache.h"
30#include "BlockExceptions.h"
31#include "Chrome.h"
32#include "ChromeClient.h"
33#include "DataTransfer.h"
34#include "DragController.h"
35#include "EventNames.h"
36#include "FocusController.h"
37#include "Frame.h"
38#include "FrameLoader.h"
39#include "FrameView.h"
40#include "KeyboardEvent.h"
41#include "MainFrame.h"
42#include "MouseEventWithHitTestResults.h"
43#include "Page.h"
44#include "Pasteboard.h"
45#include "PlatformEventFactoryMac.h"
46#include "RenderLayer.h"
47#include "RenderListBox.h"
48#include "RenderWidget.h"
49#include "RuntimeApplicationChecks.h"
50#include "ScrollableArea.h"
51#include "Scrollbar.h"
52#include "Settings.h"
53#include "ShadowRoot.h"
54#include "WebCoreSystemInterface.h"
55#include <wtf/MainThread.h>
56#include <wtf/NeverDestroyed.h>
57#include <wtf/ObjcRuntimeExtras.h>
58
59namespace WebCore {
60
61#if ENABLE(DRAG_SUPPORT)
62const double EventHandler::TextDragDelay = 0.15;
63#endif
64
65static RetainPtr<NSEvent>& currentNSEventSlot()
66{
67    static NeverDestroyed<RetainPtr<NSEvent>> event;
68    return event;
69}
70
71NSEvent *EventHandler::currentNSEvent()
72{
73    return currentNSEventSlot().get();
74}
75
76class CurrentEventScope {
77     WTF_MAKE_NONCOPYABLE(CurrentEventScope);
78public:
79    CurrentEventScope(NSEvent *);
80    ~CurrentEventScope();
81
82private:
83    RetainPtr<NSEvent> m_savedCurrentEvent;
84#ifndef NDEBUG
85    RetainPtr<NSEvent> m_event;
86#endif
87};
88
89inline CurrentEventScope::CurrentEventScope(NSEvent *event)
90    : m_savedCurrentEvent(currentNSEventSlot())
91#ifndef NDEBUG
92    , m_event(event)
93#endif
94{
95    currentNSEventSlot() = event;
96}
97
98inline CurrentEventScope::~CurrentEventScope()
99{
100    ASSERT(currentNSEventSlot() == m_event);
101    currentNSEventSlot() = m_savedCurrentEvent;
102}
103
104bool EventHandler::wheelEvent(NSEvent *event)
105{
106    Page* page = m_frame.page();
107    if (!page)
108        return false;
109
110    CurrentEventScope scope(event);
111    return handleWheelEvent(PlatformEventFactory::createPlatformWheelEvent(event, page->chrome().platformPageClient()));
112}
113
114bool EventHandler::keyEvent(NSEvent *event)
115{
116    BEGIN_BLOCK_OBJC_EXCEPTIONS;
117
118    ASSERT([event type] == NSKeyDown || [event type] == NSKeyUp);
119
120    CurrentEventScope scope(event);
121    return keyEvent(PlatformEventFactory::createPlatformKeyboardEvent(event));
122
123    END_BLOCK_OBJC_EXCEPTIONS;
124
125    return false;
126}
127
128void EventHandler::focusDocumentView()
129{
130    Page* page = m_frame.page();
131    if (!page)
132        return;
133
134    if (FrameView* frameView = m_frame.view()) {
135        if (NSView *documentView = frameView->documentView())
136            page->chrome().focusNSView(documentView);
137    }
138
139    page->focusController().setFocusedFrame(&m_frame);
140}
141
142bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event)
143{
144    // Figure out which view to send the event to.
145    auto target = event.targetNode() ? event.targetNode()->renderer() : nullptr;
146    if (!target || !target->isWidget())
147        return false;
148
149    // Double-click events don't exist in Cocoa. Since passWidgetMouseDownEventToWidget() will
150    // just pass currentEvent down to the widget, we don't want to call it for events that
151    // don't correspond to Cocoa events. The mousedown/ups will have already been passed on as
152    // part of the pressed/released handling.
153    return passMouseDownEventToWidget(toRenderWidget(target)->widget());
154}
155
156bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget)
157{
158    return passMouseDownEventToWidget(renderWidget->widget());
159}
160
161static bool lastEventIsMouseUp()
162{
163    // Many AppKit widgets run their own event loops and consume events while the mouse is down.
164    // When they finish, currentEvent is the mouseUp that they exited on. We need to update
165    // the WebCore state with this mouseUp, which we never saw. This method lets us detect
166    // that state. Handling this was critical when we used AppKit widgets for form elements.
167    // It's not clear in what cases this is helpful now -- it's possible it can be removed.
168
169    BEGIN_BLOCK_OBJC_EXCEPTIONS;
170    NSEvent *currentEventAfterHandlingMouseDown = [NSApp currentEvent];
171    return EventHandler::currentNSEvent() != currentEventAfterHandlingMouseDown
172        && [currentEventAfterHandlingMouseDown type] == NSLeftMouseUp
173        && [currentEventAfterHandlingMouseDown timestamp] >= [EventHandler::currentNSEvent() timestamp];
174    END_BLOCK_OBJC_EXCEPTIONS;
175
176    return false;
177}
178
179bool EventHandler::passMouseDownEventToWidget(Widget* pWidget)
180{
181    // FIXME: This function always returns true. It should be changed either to return
182    // false in some cases or the return value should be removed.
183
184    RefPtr<Widget> widget = pWidget;
185
186    if (!widget) {
187        LOG_ERROR("hit a RenderWidget without a corresponding Widget, means a frame is half-constructed");
188        return true;
189    }
190
191    // In WebKit2 we will never have a native widget. Just return early and let the regular event handler machinery take care of
192    // dispatching the event.
193    if (!widget->platformWidget())
194        return false;
195
196    BEGIN_BLOCK_OBJC_EXCEPTIONS;
197
198    NSView *nodeView = widget->platformWidget();
199    ASSERT([nodeView superview]);
200    NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[currentNSEvent() locationInWindow] fromView:nil]];
201    if (!view) {
202        // We probably hit the border of a RenderWidget
203        return true;
204    }
205
206    Page* page = m_frame.page();
207    if (!page)
208        return true;
209
210    if (page->chrome().client().firstResponder() != view) {
211        // Normally [NSWindow sendEvent:] handles setting the first responder.
212        // But in our case, the event was sent to the view representing the entire web page.
213        if ([currentNSEvent() clickCount] <= 1 && [view acceptsFirstResponder] && [view needsPanelToBecomeKey])
214            page->chrome().client().makeFirstResponder(view);
215    }
216
217    // We need to "defer loading" while tracking the mouse, because tearing down the
218    // page while an AppKit control is tracking the mouse can cause a crash.
219
220    // FIXME: In theory, WebCore now tolerates tear-down while tracking the
221    // mouse. We should confirm that, and then remove the deferrsLoading
222    // hack entirely.
223
224    bool wasDeferringLoading = page->defersLoading();
225    if (!wasDeferringLoading)
226        page->setDefersLoading(true);
227
228    ASSERT(!m_sendingEventToSubview);
229    m_sendingEventToSubview = true;
230
231    {
232        WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
233        [view mouseDown:currentNSEvent()];
234    }
235
236    m_sendingEventToSubview = false;
237
238    if (!wasDeferringLoading)
239        page->setDefersLoading(false);
240
241    // Remember which view we sent the event to, so we can direct the release event properly.
242    m_mouseDownView = view;
243    m_mouseDownWasInSubframe = false;
244
245    // Many AppKit widgets run their own event loops and consume events while the mouse is down.
246    // When they finish, currentEvent is the mouseUp that they exited on.  We need to update
247    // the EventHandler state with this mouseUp, which we never saw.
248    // If this event isn't a mouseUp, we assume that the mouseUp will be coming later.  There
249    // is a hole here if the widget consumes both the mouseUp and subsequent events.
250    if (lastEventIsMouseUp())
251        m_mousePressed = false;
252
253    END_BLOCK_OBJC_EXCEPTIONS;
254
255    return true;
256}
257
258// Note that this does the same kind of check as [target isDescendantOf:superview].
259// There are two differences: This is a lot slower because it has to walk the whole
260// tree, and this works in cases where the target has already been deallocated.
261static bool findViewInSubviews(NSView *superview, NSView *target)
262{
263    BEGIN_BLOCK_OBJC_EXCEPTIONS;
264    NSEnumerator *e = [[superview subviews] objectEnumerator];
265    NSView *subview;
266    while ((subview = [e nextObject])) {
267        if (subview == target || findViewInSubviews(subview, target)) {
268            return true;
269        }
270    }
271    END_BLOCK_OBJC_EXCEPTIONS;
272
273    return false;
274}
275
276NSView *EventHandler::mouseDownViewIfStillGood()
277{
278    // Since we have no way of tracking the lifetime of m_mouseDownView, we have to assume that
279    // it could be deallocated already. We search for it in our subview tree; if we don't find
280    // it, we set it to nil.
281    NSView *mouseDownView = m_mouseDownView;
282    if (!mouseDownView) {
283        return nil;
284    }
285    FrameView* topFrameView = m_frame.view();
286    NSView *topView = topFrameView ? topFrameView->platformWidget() : nil;
287    if (!topView || !findViewInSubviews(topView, mouseDownView)) {
288        m_mouseDownView = nil;
289        return nil;
290    }
291    return mouseDownView;
292}
293
294#if ENABLE(DRAG_SUPPORT)
295bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&)
296{
297    NSView *view = mouseDownViewIfStillGood();
298
299    if (!view)
300        return false;
301
302    if (!m_mouseDownWasInSubframe) {
303        ASSERT(!m_sendingEventToSubview);
304        m_sendingEventToSubview = true;
305        BEGIN_BLOCK_OBJC_EXCEPTIONS;
306        [view mouseDragged:currentNSEvent()];
307        END_BLOCK_OBJC_EXCEPTIONS;
308        m_sendingEventToSubview = false;
309    }
310
311    return true;
312}
313#endif // ENABLE(DRAG_SUPPORT)
314
315bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&)
316{
317    NSView *view = mouseDownViewIfStillGood();
318    if (!view)
319        return false;
320
321    if (!m_mouseDownWasInSubframe) {
322        ASSERT(!m_sendingEventToSubview);
323        m_sendingEventToSubview = true;
324        BEGIN_BLOCK_OBJC_EXCEPTIONS;
325        [view mouseUp:currentNSEvent()];
326        END_BLOCK_OBJC_EXCEPTIONS;
327        m_sendingEventToSubview = false;
328    }
329
330    return true;
331}
332
333bool EventHandler::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframe, HitTestResult* hoveredNode)
334{
335    BEGIN_BLOCK_OBJC_EXCEPTIONS;
336
337    switch ([currentNSEvent() type]) {
338        case NSLeftMouseDragged:
339        case NSOtherMouseDragged:
340        case NSRightMouseDragged:
341            // This check is bogus and results in <rdar://6813830>, but removing it breaks a number of
342            // layout tests.
343            if (!m_mouseDownWasInSubframe)
344                return false;
345#if ENABLE(DRAG_SUPPORT)
346            if (subframe->page()->dragController().didInitiateDrag())
347                return false;
348#endif
349        case NSMouseMoved:
350            // Since we're passing in currentNSEvent() here, we can call
351            // handleMouseMoveEvent() directly, since the save/restore of
352            // currentNSEvent() that mouseMoved() does would have no effect.
353            ASSERT(!m_sendingEventToSubview);
354            m_sendingEventToSubview = true;
355            subframe->eventHandler().handleMouseMoveEvent(currentPlatformMouseEvent(), hoveredNode);
356            m_sendingEventToSubview = false;
357            return true;
358
359        case NSLeftMouseDown: {
360            Node* node = event.targetNode();
361            if (!node)
362                return false;
363            auto renderer = node->renderer();
364            if (!renderer || !renderer->isWidget())
365                return false;
366            Widget* widget = toRenderWidget(renderer)->widget();
367            if (!widget || !widget->isFrameView())
368                return false;
369            if (!passWidgetMouseDownEventToWidget(toRenderWidget(renderer)))
370                return false;
371            m_mouseDownWasInSubframe = true;
372            return true;
373        }
374        case NSLeftMouseUp: {
375            if (!m_mouseDownWasInSubframe)
376                return false;
377            ASSERT(!m_sendingEventToSubview);
378            m_sendingEventToSubview = true;
379            subframe->eventHandler().handleMouseReleaseEvent(currentPlatformMouseEvent());
380            m_sendingEventToSubview = false;
381            return true;
382        }
383        default:
384            return false;
385    }
386    END_BLOCK_OBJC_EXCEPTIONS;
387
388    return false;
389}
390
391static IMP originalNSScrollViewScrollWheel;
392static bool _nsScrollViewScrollWheelShouldRetainSelf;
393static void selfRetainingNSScrollViewScrollWheel(NSScrollView *, SEL, NSEvent *);
394
395static bool nsScrollViewScrollWheelShouldRetainSelf()
396{
397    ASSERT(isMainThread());
398
399    return _nsScrollViewScrollWheelShouldRetainSelf;
400}
401
402static void setNSScrollViewScrollWheelShouldRetainSelf(bool shouldRetain)
403{
404    ASSERT(isMainThread());
405
406    if (!originalNSScrollViewScrollWheel) {
407        Method method = class_getInstanceMethod(objc_getRequiredClass("NSScrollView"), @selector(scrollWheel:));
408        originalNSScrollViewScrollWheel = method_setImplementation(method, reinterpret_cast<IMP>(selfRetainingNSScrollViewScrollWheel));
409    }
410
411    _nsScrollViewScrollWheelShouldRetainSelf = shouldRetain;
412}
413
414static void selfRetainingNSScrollViewScrollWheel(NSScrollView *self, SEL selector, NSEvent *event)
415{
416    bool shouldRetainSelf = isMainThread() && nsScrollViewScrollWheelShouldRetainSelf();
417
418    if (shouldRetainSelf)
419        [self retain];
420    wtfCallIMP<void>(originalNSScrollViewScrollWheel, self, selector, event);
421    if (shouldRetainSelf)
422        [self release];
423}
424
425bool EventHandler::passWheelEventToWidget(const PlatformWheelEvent& wheelEvent, Widget* widget)
426{
427    BEGIN_BLOCK_OBJC_EXCEPTIONS;
428
429    if (!widget)
430        return false;
431
432    NSView* nodeView = widget->platformWidget();
433    if (!nodeView) {
434        // WebKit2 code path.
435        if (!widget->isFrameView())
436            return false;
437
438        return toFrameView(widget)->frame().eventHandler().handleWheelEvent(wheelEvent);
439    }
440
441    if ([currentNSEvent() type] != NSScrollWheel || m_sendingEventToSubview)
442        return false;
443
444    ASSERT(nodeView);
445    ASSERT([nodeView superview]);
446    NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[currentNSEvent() locationInWindow] fromView:nil]];
447    if (!view) {
448        // We probably hit the border of a RenderWidget
449        return false;
450    }
451
452    ASSERT(!m_sendingEventToSubview);
453    m_sendingEventToSubview = true;
454    // Work around <rdar://problem/6806810> which can cause -[NSScrollView scrollWheel:] to
455    // crash if the NSScrollView is released during timer or network callback dispatch
456    // in the nested tracking runloop that -[NSScrollView scrollWheel:] runs.
457    setNSScrollViewScrollWheelShouldRetainSelf(true);
458    [view scrollWheel:currentNSEvent()];
459    setNSScrollViewScrollWheelShouldRetainSelf(false);
460    m_sendingEventToSubview = false;
461    return true;
462
463    END_BLOCK_OBJC_EXCEPTIONS;
464    return false;
465}
466
467void EventHandler::mouseDown(NSEvent *event)
468{
469    FrameView* v = m_frame.view();
470    if (!v || m_sendingEventToSubview)
471        return;
472
473    BEGIN_BLOCK_OBJC_EXCEPTIONS;
474
475    m_mouseDownView = nil;
476
477    CurrentEventScope scope(event);
478
479    handleMousePressEvent(currentPlatformMouseEvent());
480
481    END_BLOCK_OBJC_EXCEPTIONS;
482}
483
484void EventHandler::mouseDragged(NSEvent *event)
485{
486    FrameView* v = m_frame.view();
487    if (!v || m_sendingEventToSubview)
488        return;
489
490    BEGIN_BLOCK_OBJC_EXCEPTIONS;
491
492    CurrentEventScope scope(event);
493    handleMouseMoveEvent(currentPlatformMouseEvent());
494
495    END_BLOCK_OBJC_EXCEPTIONS;
496}
497
498void EventHandler::mouseUp(NSEvent *event)
499{
500    FrameView* v = m_frame.view();
501    if (!v || m_sendingEventToSubview)
502        return;
503
504    BEGIN_BLOCK_OBJC_EXCEPTIONS;
505
506    CurrentEventScope scope(event);
507
508    // Our behavior here is a little different that Qt. Qt always sends
509    // a mouse release event, even for a double click. To correct problems
510    // in khtml's DOM click event handling we do not send a release here
511    // for a double click. Instead we send that event from FrameView's
512    // handleMouseDoubleClickEvent. Note also that the third click of
513    // a triple click is treated as a single click, but the fourth is then
514    // treated as another double click. Hence the "% 2" below.
515    int clickCount = [event clickCount];
516    if (clickCount > 0 && clickCount % 2 == 0)
517        handleMouseDoubleClickEvent(currentPlatformMouseEvent());
518    else
519        handleMouseReleaseEvent(currentPlatformMouseEvent());
520
521    m_mouseDownView = nil;
522
523    END_BLOCK_OBJC_EXCEPTIONS;
524}
525
526/*
527 A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
528 eats all subsequent events after it is starts its modal tracking loop.  After the interaction
529 is done, this routine is used to fix things up.  When a mouse down started us tracking in
530 the widget, we post a fake mouse up to balance the mouse down we started with. When a
531 key down started us tracking in the widget, we post a fake key up to balance things out.
532 In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to
533 be over after the tracking is done.
534 */
535void EventHandler::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
536{
537    FrameView* view = m_frame.view();
538    if (!view)
539        return;
540
541    BEGIN_BLOCK_OBJC_EXCEPTIONS;
542
543    m_sendingEventToSubview = false;
544    int eventType = [initiatingEvent type];
545    if (eventType == NSLeftMouseDown || eventType == NSKeyDown) {
546        NSEvent *fakeEvent = nil;
547        if (eventType == NSLeftMouseDown) {
548            fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
549                                           location:[initiatingEvent locationInWindow]
550                                      modifierFlags:[initiatingEvent modifierFlags]
551                                          timestamp:[initiatingEvent timestamp]
552                                       windowNumber:[initiatingEvent windowNumber]
553                                            context:[initiatingEvent context]
554                                        eventNumber:[initiatingEvent eventNumber]
555                                         clickCount:[initiatingEvent clickCount]
556                                           pressure:[initiatingEvent pressure]];
557
558            [NSApp postEvent:fakeEvent atStart:YES];
559        } else { // eventType == NSKeyDown
560            fakeEvent = [NSEvent keyEventWithType:NSKeyUp
561                                         location:[initiatingEvent locationInWindow]
562                                    modifierFlags:[initiatingEvent modifierFlags]
563                                        timestamp:[initiatingEvent timestamp]
564                                     windowNumber:[initiatingEvent windowNumber]
565                                          context:[initiatingEvent context]
566                                       characters:[initiatingEvent characters]
567                      charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers]
568                                        isARepeat:[initiatingEvent isARepeat]
569                                          keyCode:[initiatingEvent keyCode]];
570            [NSApp postEvent:fakeEvent atStart:YES];
571        }
572        // FIXME: We should really get the current modifierFlags here, but there's no way to poll
573        // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
574        // no up-to-date cache of them anywhere.
575#pragma clang diagnostic push
576#pragma clang diagnostic ignored "-Wdeprecated-declarations"
577        fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
578                                       location:[[view->platformWidget() window] convertScreenToBase:[NSEvent mouseLocation]]
579                                  modifierFlags:[initiatingEvent modifierFlags]
580                                      timestamp:[initiatingEvent timestamp]
581                                   windowNumber:[initiatingEvent windowNumber]
582                                        context:[initiatingEvent context]
583                                    eventNumber:0
584                                     clickCount:0
585                                       pressure:0];
586#pragma clang diagnostic pop
587        [NSApp postEvent:fakeEvent atStart:YES];
588    }
589
590    END_BLOCK_OBJC_EXCEPTIONS;
591}
592
593void EventHandler::mouseMoved(NSEvent *event)
594{
595    // Reject a mouse moved if the button is down - screws up tracking during autoscroll
596    // These happen because WebKit sometimes has to fake up moved events.
597    if (!m_frame.view() || m_mousePressed || m_sendingEventToSubview)
598        return;
599
600    BEGIN_BLOCK_OBJC_EXCEPTIONS;
601    CurrentEventScope scope(event);
602    mouseMoved(currentPlatformMouseEvent());
603    END_BLOCK_OBJC_EXCEPTIONS;
604}
605
606void EventHandler::passMouseMovedEventToScrollbars(NSEvent *event)
607{
608    // Reject a mouse moved if the button is down - screws up tracking during autoscroll
609    // These happen because WebKit sometimes has to fake up moved events.
610    if (!m_frame.view() || m_mousePressed || m_sendingEventToSubview)
611        return;
612
613    BEGIN_BLOCK_OBJC_EXCEPTIONS;
614    CurrentEventScope scope(event);
615    passMouseMovedEventToScrollbars(currentPlatformMouseEvent());
616    END_BLOCK_OBJC_EXCEPTIONS;
617}
618
619static bool frameHasPlatformWidget(const Frame& frame)
620{
621    if (FrameView* frameView = frame.view()) {
622        if (frameView->platformWidget())
623            return true;
624    }
625
626    return false;
627}
628
629bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
630{
631    // WebKit1 code path.
632    if (frameHasPlatformWidget(m_frame))
633        return passSubframeEventToSubframe(mev, subframe);
634
635    // WebKit2 code path.
636    subframe->eventHandler().handleMousePressEvent(mev.event());
637    return true;
638}
639
640bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode)
641{
642    // WebKit1 code path.
643    if (frameHasPlatformWidget(m_frame))
644        return passSubframeEventToSubframe(mev, subframe, hoveredNode);
645
646#if ENABLE(DRAG_SUPPORT)
647    // WebKit2 code path.
648    if (m_mouseDownMayStartDrag && !m_mouseDownWasInSubframe)
649        return false;
650#endif
651
652    subframe->eventHandler().handleMouseMoveEvent(mev.event(), hoveredNode);
653    return true;
654}
655
656bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
657{
658    // WebKit1 code path.
659    if (frameHasPlatformWidget(m_frame))
660        return passSubframeEventToSubframe(mev, subframe);
661
662    // WebKit2 code path.
663    subframe->eventHandler().handleMouseReleaseEvent(mev.event());
664    return true;
665}
666
667PlatformMouseEvent EventHandler::currentPlatformMouseEvent() const
668{
669    NSView *windowView = nil;
670    if (Page* page = m_frame.page())
671        windowView = page->chrome().platformPageClient();
672    return PlatformEventFactory::createPlatformMouseEvent(currentNSEvent(), windowView);
673}
674
675bool EventHandler::eventActivatedView(const PlatformMouseEvent& event) const
676{
677    return m_activationEventNumber == event.eventNumber();
678}
679
680#if ENABLE(DRAG_SUPPORT)
681
682PassRefPtr<DataTransfer> EventHandler::createDraggingDataTransfer() const
683{
684    // Must be done before ondragstart adds types and data to the pboard,
685    // also done for security, as it erases data from the last drag.
686    OwnPtr<Pasteboard> pasteboard = Pasteboard::create(NSDragPboard);
687    pasteboard->clear();
688    return DataTransfer::createForDragAndDrop();
689}
690
691#endif
692
693bool EventHandler::tabsToAllFormControls(KeyboardEvent* event) const
694{
695    Page* page = m_frame.page();
696    if (!page)
697        return false;
698
699    KeyboardUIMode keyboardUIMode = page->chrome().client().keyboardUIMode();
700    bool handlingOptionTab = isKeyboardOptionTab(event);
701
702    // If tab-to-links is off, option-tab always highlights all controls
703    if ((keyboardUIMode & KeyboardAccessTabsToLinks) == 0 && handlingOptionTab)
704        return true;
705
706    // If system preferences say to include all controls, we always include all controls
707    if (keyboardUIMode & KeyboardAccessFull)
708        return true;
709
710    // Otherwise tab-to-links includes all controls, unless the sense is flipped via option-tab.
711    if (keyboardUIMode & KeyboardAccessTabsToLinks)
712        return !handlingOptionTab;
713
714    return handlingOptionTab;
715}
716
717bool EventHandler::needsKeyboardEventDisambiguationQuirks() const
718{
719#if ENABLE(DASHBOARD_SUPPORT)
720    if (m_frame.settings().usesDashboardBackwardCompatibilityMode())
721        return true;
722#endif
723
724    if (m_frame.settings().needsKeyboardEventDisambiguationQuirks())
725        return true;
726
727    return false;
728}
729
730unsigned EventHandler::accessKeyModifiers()
731{
732    // Control+Option key combinations are usually unused on Mac OS X, but not when VoiceOver is enabled.
733    // So, we use Control in this case, even though it conflicts with Emacs-style key bindings.
734    // See <https://bugs.webkit.org/show_bug.cgi?id=21107> for more detail.
735    if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
736        return PlatformEvent::CtrlKey;
737
738    return PlatformEvent::CtrlKey | PlatformEvent::AltKey;
739}
740
741static ContainerNode* findEnclosingScrollableContainer(ContainerNode& node)
742{
743    // Find the first node with a valid scrollable area starting with the current
744    // node and traversing its parents (or shadow hosts).
745    for (ContainerNode* candidate = &node; candidate; candidate = candidate->parentOrShadowHostNode()) {
746        RenderBox* box = candidate->renderBox();
747        if (box && box->canBeScrolledAndHasScrollableArea())
748            return candidate;
749    }
750
751    return nullptr;
752}
753
754static bool deltaIsPredominantlyVertical(float deltaX, float deltaY)
755{
756    return std::abs(deltaY) > std::abs(deltaX);
757}
758
759static bool scrolledToEdgeInDominantDirection(const ContainerNode& container, const ScrollableArea& area, float deltaX, float deltaY)
760{
761    if (!container.renderer())
762        return true;
763
764    const RenderStyle& style = container.renderer()->style();
765
766    if (!deltaIsPredominantlyVertical(deltaX, deltaY) && deltaX) {
767        if (style.overflowX() == OHIDDEN)
768            return true;
769
770        if (deltaX < 0)
771            return area.scrolledToRight();
772
773        return area.scrolledToLeft();
774    }
775
776    if (style.overflowY() == OHIDDEN)
777        return true;
778
779    if (deltaY < 0)
780        return area.scrolledToBottom();
781
782    return area.scrolledToTop();
783}
784
785static Widget* widgetForEventTarget(Element* eventTarget)
786{
787    if (!eventTarget)
788        return nullptr;
789
790    RenderElement* target = eventTarget->renderer();
791    if (!target || !target->isWidget())
792        return nullptr;
793
794    return toRenderWidget(target)->widget();
795}
796
797static ScrollView* scrollViewForEventTarget(Element* eventTarget)
798{
799    Widget* widget = widgetForEventTarget(eventTarget);
800    if (!widget)
801        return nullptr;
802
803    if (!widget->isScrollView())
804        return nullptr;
805
806    return reinterpret_cast<ScrollView*>(widget);
807}
808
809static bool eventTargetIsPlatformWidget(Element* eventTarget)
810{
811    Widget* widget = widgetForEventTarget(eventTarget);
812    if (!widget)
813        return false;
814
815    return widget->platformWidget();
816}
817
818void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent& wheelEvent, const HitTestResult& result, RefPtr<Element>& wheelEventTarget, RefPtr<ContainerNode>& scrollableContainer, ScrollableArea*& scrollableArea, bool& isOverWidget)
819{
820    FrameView* view = m_frame.view();
821
822    scrollableContainer = nullptr;
823    scrollableArea = nullptr;
824    if (!view || !view->frame().isMainFrame()) {
825        scrollableContainer = wheelEventTarget;
826        scrollableArea = view;
827    } else {
828        if (eventTargetIsPlatformWidget(wheelEventTarget.get())) {
829            scrollableContainer = wheelEventTarget;
830            scrollableArea = scrollViewForEventTarget(wheelEventTarget.get());
831        } else {
832            scrollableContainer = findEnclosingScrollableContainer(*wheelEventTarget);
833            if (scrollableContainer) {
834                if (RenderBox* box = scrollableContainer->renderBox()) {
835                    if (box->isListBox())
836                        scrollableArea = toRenderListBox(box);
837                    else
838                        scrollableArea = box->layer();
839                }
840            }
841        }
842    }
843
844    if (wheelEvent.shouldConsiderLatching()) {
845        if (scrollableArea && scrollableContainer)
846            m_startedGestureAtScrollLimit = scrolledToEdgeInDominantDirection(*scrollableContainer, *scrollableArea, wheelEvent.deltaX(), wheelEvent.deltaY());
847        else
848            m_startedGestureAtScrollLimit = false;
849        m_latchedWheelEventElement = wheelEventTarget;
850        // FIXME: What prevents us from deleting this scrollable container while still holding a pointer to it?
851        m_latchedScrollableContainer = scrollableContainer;
852        m_widgetIsLatched = result.isOverWidget();
853        isOverWidget = m_widgetIsLatched;
854        m_recentWheelEventDeltaTracker->beginTrackingDeltas();
855    } else if (wheelEvent.shouldResetLatching()) {
856        clearLatchedState();
857        m_recentWheelEventDeltaTracker->endTrackingDeltas();
858    }
859
860    if (!wheelEvent.shouldResetLatching() && m_latchedWheelEventElement) {
861        wheelEventTarget = m_latchedWheelEventElement.get();
862        isOverWidget = m_widgetIsLatched;
863    }
864}
865
866void EventHandler::platformRecordWheelEvent(const PlatformWheelEvent& wheelEvent)
867{
868    switch (wheelEvent.phase()) {
869        case PlatformWheelEventPhaseBegan:
870            m_recentWheelEventDeltaTracker->beginTrackingDeltas();
871            break;
872        case PlatformWheelEventPhaseEnded:
873            m_recentWheelEventDeltaTracker->endTrackingDeltas();
874            break;
875        default:
876            break;
877    }
878
879    m_recentWheelEventDeltaTracker->recordWheelEventDelta(wheelEvent);
880}
881
882bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& wheelEvent, Element* wheelEventTarget, ContainerNode* scrollableContainer, ScrollableArea* scrollableArea)
883{
884    // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed.
885    FrameView* view = m_frame.view();
886
887    if (wheelEvent.useLatchedEventElement() && m_latchedScrollableContainer) {
888        if (!view || !view->frame().isMainFrame()) {
889            bool didHandleWheelEvent = view && view->wheelEvent(wheelEvent);
890            if (!didHandleWheelEvent && scrollableContainer == m_latchedScrollableContainer) {
891                // If we are just starting a scroll event, and have nowhere left to scroll, allow
892                // the enclosing frame to handle the scroll.
893                didHandleWheelEvent = !m_startedGestureAtScrollLimit;
894            }
895
896            // If the platform widget is handling the event, we always want to return false
897            if (view && scrollableArea == view && view->platformWidget())
898                didHandleWheelEvent = false;
899
900            m_isHandlingWheelEvent = false;
901            return didHandleWheelEvent;
902        }
903
904        if (scrollableArea && !m_startedGestureAtScrollLimit && scrollableContainer == m_latchedScrollableContainer) {
905            m_isHandlingWheelEvent = false;
906
907            if (eventTargetIsPlatformWidget(wheelEventTarget))
908                return !m_startedGestureAtScrollLimit;
909
910            return true;
911        }
912    }
913
914    bool didHandleEvent = view ? view->wheelEvent(wheelEvent) : false;
915    m_isHandlingWheelEvent = false;
916    return didHandleEvent;
917}
918
919bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent& wheelEvent, const Widget& widget, ContainerNode* scrollableContainer)
920{
921    // WebKit1: Prevent multiple copies of the scrollWheel event from being sent to the NSScrollView widget.
922    if (frameHasPlatformWidget(m_frame) && widget.isFrameView())
923        return true;
924
925    if (wheelEvent.useLatchedEventElement() && m_latchedScrollableContainer && scrollableContainer == m_latchedScrollableContainer)
926        return !m_startedGestureAtScrollLimit;
927
928    return false;
929}
930
931}
932