• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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#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 "ClipboardMac.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 "MouseEventWithHitTestResults.h"
42#include "NotImplemented.h"
43#include "Page.h"
44#include "PlatformKeyboardEvent.h"
45#include "PlatformWheelEvent.h"
46#include "RenderWidget.h"
47#include "RuntimeApplicationChecks.h"
48#include "Scrollbar.h"
49#include "Settings.h"
50#include "WebCoreSystemInterface.h"
51#include <objc/objc-runtime.h>
52#include <wtf/StdLibExtras.h>
53
54#if !(defined(OBJC_API_VERSION) && OBJC_API_VERSION > 0)
55static inline IMP method_setImplementation(Method m, IMP i)
56{
57    IMP oi = m->method_imp;
58    m->method_imp = i;
59    return oi;
60}
61#endif
62
63namespace WebCore {
64
65#if ENABLE(DRAG_SUPPORT)
66const double EventHandler::TextDragDelay = 0.15;
67#endif
68
69static RetainPtr<NSEvent>& currentNSEventSlot()
70{
71    DEFINE_STATIC_LOCAL(RetainPtr<NSEvent>, event, ());
72    return event;
73}
74
75NSEvent *EventHandler::currentNSEvent()
76{
77    return currentNSEventSlot().get();
78}
79
80class CurrentEventScope {
81     WTF_MAKE_NONCOPYABLE(CurrentEventScope);
82public:
83    CurrentEventScope(NSEvent *);
84    ~CurrentEventScope();
85
86private:
87    RetainPtr<NSEvent> m_savedCurrentEvent;
88#ifndef NDEBUG
89    RetainPtr<NSEvent> m_event;
90#endif
91};
92
93inline CurrentEventScope::CurrentEventScope(NSEvent *event)
94    : m_savedCurrentEvent(currentNSEventSlot())
95#ifndef NDEBUG
96    , m_event(event)
97#endif
98{
99    currentNSEventSlot() = event;
100}
101
102inline CurrentEventScope::~CurrentEventScope()
103{
104    ASSERT(currentNSEventSlot() == m_event);
105    currentNSEventSlot() = m_savedCurrentEvent;
106}
107
108bool EventHandler::wheelEvent(NSEvent *event)
109{
110    Page* page = m_frame->page();
111    if (!page)
112        return false;
113
114    CurrentEventScope scope(event);
115
116    PlatformWheelEvent wheelEvent(event, page->chrome()->platformPageClient());
117    handleWheelEvent(wheelEvent);
118
119    return wheelEvent.isAccepted();
120}
121
122PassRefPtr<KeyboardEvent> EventHandler::currentKeyboardEvent() const
123{
124    NSEvent *event = [NSApp currentEvent];
125    if (!event)
126        return 0;
127    switch ([event type]) {
128        case NSKeyDown: {
129            PlatformKeyboardEvent platformEvent(event);
130            platformEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown);
131            return KeyboardEvent::create(platformEvent, m_frame->document()->defaultView());
132        }
133        case NSKeyUp:
134            return KeyboardEvent::create(event, m_frame->document()->defaultView());
135        default:
136            return 0;
137    }
138}
139
140bool EventHandler::keyEvent(NSEvent *event)
141{
142    BEGIN_BLOCK_OBJC_EXCEPTIONS;
143
144    ASSERT([event type] == NSKeyDown || [event type] == NSKeyUp);
145
146    CurrentEventScope scope(event);
147    return keyEvent(PlatformKeyboardEvent(event));
148
149    END_BLOCK_OBJC_EXCEPTIONS;
150
151    return false;
152}
153
154void EventHandler::focusDocumentView()
155{
156    Page* page = m_frame->page();
157    if (!page)
158        return;
159
160    if (FrameView* frameView = m_frame->view()) {
161        if (NSView *documentView = frameView->documentView())
162            page->chrome()->focusNSView(documentView);
163    }
164
165    page->focusController()->setFocusedFrame(m_frame);
166}
167
168bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event)
169{
170    // Figure out which view to send the event to.
171    RenderObject* target = targetNode(event) ? targetNode(event)->renderer() : 0;
172    if (!target || !target->isWidget())
173        return false;
174
175    // Double-click events don't exist in Cocoa. Since passWidgetMouseDownEventToWidget will
176    // just pass currentEvent down to the widget, we don't want to call it for events that
177    // don't correspond to Cocoa events.  The mousedown/ups will have already been passed on as
178    // part of the pressed/released handling.
179    return passMouseDownEventToWidget(toRenderWidget(target)->widget());
180}
181
182bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget)
183{
184    return passMouseDownEventToWidget(renderWidget->widget());
185}
186
187static bool lastEventIsMouseUp()
188{
189    // Many AppKit widgets run their own event loops and consume events while the mouse is down.
190    // When they finish, currentEvent is the mouseUp that they exited on. We need to update
191    // the WebCore state with this mouseUp, which we never saw. This method lets us detect
192    // that state. Handling this was critical when we used AppKit widgets for form elements.
193    // It's not clear in what cases this is helpful now -- it's possible it can be removed.
194
195    BEGIN_BLOCK_OBJC_EXCEPTIONS;
196    NSEvent *currentEventAfterHandlingMouseDown = [NSApp currentEvent];
197    return EventHandler::currentNSEvent() != currentEventAfterHandlingMouseDown
198        && [currentEventAfterHandlingMouseDown type] == NSLeftMouseUp
199        && [currentEventAfterHandlingMouseDown timestamp] >= [EventHandler::currentNSEvent() timestamp];
200    END_BLOCK_OBJC_EXCEPTIONS;
201
202    return false;
203}
204
205bool EventHandler::passMouseDownEventToWidget(Widget* pWidget)
206{
207    // FIXME: This function always returns true. It should be changed either to return
208    // false in some cases or the return value should be removed.
209
210    RefPtr<Widget> widget = pWidget;
211
212    if (!widget) {
213        LOG_ERROR("hit a RenderWidget without a corresponding Widget, means a frame is half-constructed");
214        return true;
215    }
216
217    // In WebKit2 we will never have an NSView. Just return early and let the regular event handler machinery take care of
218    // dispatching the event.
219    if (!widget->platformWidget())
220        return false;
221
222    BEGIN_BLOCK_OBJC_EXCEPTIONS;
223
224    NSView *nodeView = widget->platformWidget();
225    ASSERT([nodeView superview]);
226    NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[currentNSEvent() locationInWindow] fromView:nil]];
227    if (!view) {
228        // We probably hit the border of a RenderWidget
229        return true;
230    }
231
232    Page* page = m_frame->page();
233    if (!page)
234        return true;
235
236    if (page->chrome()->client()->firstResponder() != view) {
237        // Normally [NSWindow sendEvent:] handles setting the first responder.
238        // But in our case, the event was sent to the view representing the entire web page.
239        if ([currentNSEvent() clickCount] <= 1 && [view acceptsFirstResponder] && [view needsPanelToBecomeKey])
240            page->chrome()->client()->makeFirstResponder(view);
241    }
242
243    // We need to "defer loading" while tracking the mouse, because tearing down the
244    // page while an AppKit control is tracking the mouse can cause a crash.
245
246    // FIXME: In theory, WebCore now tolerates tear-down while tracking the
247    // mouse. We should confirm that, and then remove the deferrsLoading
248    // hack entirely.
249
250    bool wasDeferringLoading = page->defersLoading();
251    if (!wasDeferringLoading)
252        page->setDefersLoading(true);
253
254    ASSERT(!m_sendingEventToSubview);
255    m_sendingEventToSubview = true;
256    NSView *outerView = widget->getOuterView();
257    widget->beforeMouseDown(outerView, widget.get());
258    [view mouseDown:currentNSEvent()];
259    widget->afterMouseDown(outerView, widget.get());
260    m_sendingEventToSubview = false;
261
262    if (!wasDeferringLoading)
263        page->setDefersLoading(false);
264
265    // Remember which view we sent the event to, so we can direct the release event properly.
266    m_mouseDownView = view;
267    m_mouseDownWasInSubframe = false;
268
269    // Many AppKit widgets run their own event loops and consume events while the mouse is down.
270    // When they finish, currentEvent is the mouseUp that they exited on.  We need to update
271    // the EventHandler state with this mouseUp, which we never saw.
272    // If this event isn't a mouseUp, we assume that the mouseUp will be coming later.  There
273    // is a hole here if the widget consumes both the mouseUp and subsequent events.
274    if (lastEventIsMouseUp())
275        m_mousePressed = false;
276
277    END_BLOCK_OBJC_EXCEPTIONS;
278
279    return true;
280}
281
282// Note that this does the same kind of check as [target isDescendantOf:superview].
283// There are two differences: This is a lot slower because it has to walk the whole
284// tree, and this works in cases where the target has already been deallocated.
285static bool findViewInSubviews(NSView *superview, NSView *target)
286{
287    BEGIN_BLOCK_OBJC_EXCEPTIONS;
288    NSEnumerator *e = [[superview subviews] objectEnumerator];
289    NSView *subview;
290    while ((subview = [e nextObject])) {
291        if (subview == target || findViewInSubviews(subview, target)) {
292            return true;
293        }
294    }
295    END_BLOCK_OBJC_EXCEPTIONS;
296
297    return false;
298}
299
300NSView *EventHandler::mouseDownViewIfStillGood()
301{
302    // Since we have no way of tracking the lifetime of m_mouseDownView, we have to assume that
303    // it could be deallocated already. We search for it in our subview tree; if we don't find
304    // it, we set it to nil.
305    NSView *mouseDownView = m_mouseDownView;
306    if (!mouseDownView) {
307        return nil;
308    }
309    FrameView* topFrameView = m_frame->view();
310    NSView *topView = topFrameView ? topFrameView->platformWidget() : nil;
311    if (!topView || !findViewInSubviews(topView, mouseDownView)) {
312        m_mouseDownView = nil;
313        return nil;
314    }
315    return mouseDownView;
316}
317
318#if ENABLE(DRAG_SUPPORT)
319bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&)
320{
321    NSView *view = mouseDownViewIfStillGood();
322
323    if (!view)
324        return false;
325
326    if (!m_mouseDownWasInSubframe) {
327        ASSERT(!m_sendingEventToSubview);
328        m_sendingEventToSubview = true;
329        BEGIN_BLOCK_OBJC_EXCEPTIONS;
330        [view mouseDragged:currentNSEvent()];
331        END_BLOCK_OBJC_EXCEPTIONS;
332        m_sendingEventToSubview = false;
333    }
334
335    return true;
336}
337#endif // ENABLE(DRAG_SUPPORT)
338
339bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&)
340{
341    NSView *view = mouseDownViewIfStillGood();
342    if (!view)
343        return false;
344
345    if (!m_mouseDownWasInSubframe) {
346        ASSERT(!m_sendingEventToSubview);
347        m_sendingEventToSubview = true;
348        BEGIN_BLOCK_OBJC_EXCEPTIONS;
349        [view mouseUp:currentNSEvent()];
350        END_BLOCK_OBJC_EXCEPTIONS;
351        m_sendingEventToSubview = false;
352    }
353
354    return true;
355}
356
357bool EventHandler::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframe, HitTestResult* hoveredNode)
358{
359    BEGIN_BLOCK_OBJC_EXCEPTIONS;
360
361    switch ([currentNSEvent() type]) {
362        case NSLeftMouseDragged:
363        case NSOtherMouseDragged:
364        case NSRightMouseDragged:
365            // This check is bogus and results in <rdar://6813830>, but removing it breaks a number of
366            // layout tests.
367            if (!m_mouseDownWasInSubframe)
368                return false;
369#if ENABLE(DRAG_SUPPORT)
370            if (subframe->page()->dragController()->didInitiateDrag())
371                return false;
372#endif
373        case NSMouseMoved:
374            // Since we're passing in currentNSEvent() here, we can call
375            // handleMouseMoveEvent() directly, since the save/restore of
376            // currentNSEvent() that mouseMoved() does would have no effect.
377            ASSERT(!m_sendingEventToSubview);
378            m_sendingEventToSubview = true;
379            subframe->eventHandler()->handleMouseMoveEvent(currentPlatformMouseEvent(), hoveredNode);
380            m_sendingEventToSubview = false;
381            return true;
382
383        case NSLeftMouseDown: {
384            Node* node = targetNode(event);
385            if (!node)
386                return false;
387            RenderObject* renderer = node->renderer();
388            if (!renderer || !renderer->isWidget())
389                return false;
390            Widget* widget = toRenderWidget(renderer)->widget();
391            if (!widget || !widget->isFrameView())
392                return false;
393            if (!passWidgetMouseDownEventToWidget(toRenderWidget(renderer)))
394                return false;
395            m_mouseDownWasInSubframe = true;
396            return true;
397        }
398        case NSLeftMouseUp: {
399            if (!m_mouseDownWasInSubframe)
400                return false;
401            ASSERT(!m_sendingEventToSubview);
402            m_sendingEventToSubview = true;
403            subframe->eventHandler()->handleMouseReleaseEvent(currentPlatformMouseEvent());
404            m_sendingEventToSubview = false;
405            return true;
406        }
407        default:
408            return false;
409    }
410    END_BLOCK_OBJC_EXCEPTIONS;
411
412    return false;
413}
414
415static IMP originalNSScrollViewScrollWheel;
416static bool _nsScrollViewScrollWheelShouldRetainSelf;
417static void selfRetainingNSScrollViewScrollWheel(NSScrollView *, SEL, NSEvent *);
418
419static bool nsScrollViewScrollWheelShouldRetainSelf()
420{
421    ASSERT(isMainThread());
422
423    return _nsScrollViewScrollWheelShouldRetainSelf;
424}
425
426static void setNSScrollViewScrollWheelShouldRetainSelf(bool shouldRetain)
427{
428    ASSERT(isMainThread());
429
430    if (!originalNSScrollViewScrollWheel) {
431        Method method = class_getInstanceMethod(objc_getRequiredClass("NSScrollView"), @selector(scrollWheel:));
432        originalNSScrollViewScrollWheel = method_setImplementation(method, reinterpret_cast<IMP>(selfRetainingNSScrollViewScrollWheel));
433    }
434
435    _nsScrollViewScrollWheelShouldRetainSelf = shouldRetain;
436}
437
438static void selfRetainingNSScrollViewScrollWheel(NSScrollView *self, SEL selector, NSEvent *event)
439{
440    bool shouldRetainSelf = isMainThread() && nsScrollViewScrollWheelShouldRetainSelf();
441
442    if (shouldRetainSelf)
443        [self retain];
444    originalNSScrollViewScrollWheel(self, selector, event);
445    if (shouldRetainSelf)
446        [self release];
447}
448
449bool EventHandler::passWheelEventToWidget(PlatformWheelEvent& wheelEvent, Widget* widget)
450{
451    BEGIN_BLOCK_OBJC_EXCEPTIONS;
452
453    if (!widget)
454        return false;
455
456    NSView* nodeView = widget->platformWidget();
457    if (!nodeView) {
458        // WebKit2 code path.
459        if (!widget->isFrameView())
460            return false;
461
462        return static_cast<FrameView*>(widget)->frame()->eventHandler()->handleWheelEvent(wheelEvent);
463    }
464
465    if ([currentNSEvent() type] != NSScrollWheel || m_sendingEventToSubview)
466        return false;
467
468    ASSERT(nodeView);
469    ASSERT([nodeView superview]);
470    NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[currentNSEvent() locationInWindow] fromView:nil]];
471    if (!view)
472        // We probably hit the border of a RenderWidget
473        return false;
474
475    ASSERT(!m_sendingEventToSubview);
476    m_sendingEventToSubview = true;
477    // Work around <rdar://problem/6806810> which can cause -[NSScrollView scrollWheel:] to
478    // crash if the NSScrollView is released during timer or network callback dispatch
479    // in the nested tracking runloop that -[NSScrollView scrollWheel:] runs.
480    setNSScrollViewScrollWheelShouldRetainSelf(true);
481    [view scrollWheel:currentNSEvent()];
482    setNSScrollViewScrollWheelShouldRetainSelf(false);
483    m_sendingEventToSubview = false;
484    return true;
485
486    END_BLOCK_OBJC_EXCEPTIONS;
487    return false;
488}
489
490void EventHandler::mouseDown(NSEvent *event)
491{
492    FrameView* v = m_frame->view();
493    if (!v || m_sendingEventToSubview)
494        return;
495
496    BEGIN_BLOCK_OBJC_EXCEPTIONS;
497
498    m_frame->loader()->resetMultipleFormSubmissionProtection();
499
500    m_mouseDownView = nil;
501
502    CurrentEventScope scope(event);
503
504    handleMousePressEvent(currentPlatformMouseEvent());
505
506    END_BLOCK_OBJC_EXCEPTIONS;
507}
508
509void EventHandler::mouseDragged(NSEvent *event)
510{
511    FrameView* v = m_frame->view();
512    if (!v || m_sendingEventToSubview)
513        return;
514
515    BEGIN_BLOCK_OBJC_EXCEPTIONS;
516
517    CurrentEventScope scope(event);
518    handleMouseMoveEvent(currentPlatformMouseEvent());
519
520    END_BLOCK_OBJC_EXCEPTIONS;
521}
522
523void EventHandler::mouseUp(NSEvent *event)
524{
525    FrameView* v = m_frame->view();
526    if (!v || m_sendingEventToSubview)
527        return;
528
529    BEGIN_BLOCK_OBJC_EXCEPTIONS;
530
531    CurrentEventScope scope(event);
532
533    // Our behavior here is a little different that Qt. Qt always sends
534    // a mouse release event, even for a double click. To correct problems
535    // in khtml's DOM click event handling we do not send a release here
536    // for a double click. Instead we send that event from FrameView's
537    // handleMouseDoubleClickEvent. Note also that the third click of
538    // a triple click is treated as a single click, but the fourth is then
539    // treated as another double click. Hence the "% 2" below.
540    int clickCount = [event clickCount];
541    if (clickCount > 0 && clickCount % 2 == 0)
542        handleMouseDoubleClickEvent(currentPlatformMouseEvent());
543    else
544        handleMouseReleaseEvent(currentPlatformMouseEvent());
545
546    m_mouseDownView = nil;
547
548    END_BLOCK_OBJC_EXCEPTIONS;
549}
550
551/*
552 A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
553 eats all subsequent events after it is starts its modal tracking loop.  After the interaction
554 is done, this routine is used to fix things up.  When a mouse down started us tracking in
555 the widget, we post a fake mouse up to balance the mouse down we started with. When a
556 key down started us tracking in the widget, we post a fake key up to balance things out.
557 In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to
558 be over after the tracking is done.
559 */
560void EventHandler::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
561{
562    FrameView* view = m_frame->view();
563    if (!view)
564        return;
565
566    BEGIN_BLOCK_OBJC_EXCEPTIONS;
567
568    m_sendingEventToSubview = false;
569    int eventType = [initiatingEvent type];
570    if (eventType == NSLeftMouseDown || eventType == NSKeyDown) {
571        NSEvent *fakeEvent = nil;
572        if (eventType == NSLeftMouseDown) {
573            fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
574                                           location:[initiatingEvent locationInWindow]
575                                      modifierFlags:[initiatingEvent modifierFlags]
576                                          timestamp:[initiatingEvent timestamp]
577                                       windowNumber:[initiatingEvent windowNumber]
578                                            context:[initiatingEvent context]
579                                        eventNumber:[initiatingEvent eventNumber]
580                                         clickCount:[initiatingEvent clickCount]
581                                           pressure:[initiatingEvent pressure]];
582
583            [NSApp postEvent:fakeEvent atStart:YES];
584        } else { // eventType == NSKeyDown
585            fakeEvent = [NSEvent keyEventWithType:NSKeyUp
586                                         location:[initiatingEvent locationInWindow]
587                                    modifierFlags:[initiatingEvent modifierFlags]
588                                        timestamp:[initiatingEvent timestamp]
589                                     windowNumber:[initiatingEvent windowNumber]
590                                          context:[initiatingEvent context]
591                                       characters:[initiatingEvent characters]
592                      charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers]
593                                        isARepeat:[initiatingEvent isARepeat]
594                                          keyCode:[initiatingEvent keyCode]];
595            [NSApp postEvent:fakeEvent atStart:YES];
596        }
597        // FIXME: We should really get the current modifierFlags here, but there's no way to poll
598        // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
599        // no up-to-date cache of them anywhere.
600        fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
601                                       location:[[view->platformWidget() window] convertScreenToBase:[NSEvent mouseLocation]]
602                                  modifierFlags:[initiatingEvent modifierFlags]
603                                      timestamp:[initiatingEvent timestamp]
604                                   windowNumber:[initiatingEvent windowNumber]
605                                        context:[initiatingEvent context]
606                                    eventNumber:0
607                                     clickCount:0
608                                       pressure:0];
609        [NSApp postEvent:fakeEvent atStart:YES];
610    }
611
612    END_BLOCK_OBJC_EXCEPTIONS;
613}
614
615void EventHandler::mouseMoved(NSEvent *event)
616{
617    // Reject a mouse moved if the button is down - screws up tracking during autoscroll
618    // These happen because WebKit sometimes has to fake up moved events.
619    if (!m_frame->view() || m_mousePressed || m_sendingEventToSubview)
620        return;
621
622    BEGIN_BLOCK_OBJC_EXCEPTIONS;
623    CurrentEventScope scope(event);
624    mouseMoved(currentPlatformMouseEvent());
625    END_BLOCK_OBJC_EXCEPTIONS;
626}
627
628static bool frameHasPlatformWidget(Frame* frame)
629{
630    if (FrameView* frameView = frame->view()) {
631        if (frameView->platformWidget())
632            return true;
633    }
634
635    return false;
636}
637
638bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
639{
640    // WebKit1 code path.
641    if (frameHasPlatformWidget(m_frame))
642        return passSubframeEventToSubframe(mev, subframe);
643
644    // WebKit2 code path.
645    subframe->eventHandler()->handleMousePressEvent(mev.event());
646    return true;
647}
648
649bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode)
650{
651    // WebKit1 code path.
652    if (frameHasPlatformWidget(m_frame))
653        return passSubframeEventToSubframe(mev, subframe, hoveredNode);
654
655    // WebKit2 code path.
656    if (m_mouseDownMayStartDrag && !m_mouseDownWasInSubframe)
657        return false;
658    subframe->eventHandler()->handleMouseMoveEvent(mev.event(), hoveredNode);
659    return true;
660}
661
662bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
663{
664    // WebKit1 code path.
665    if (frameHasPlatformWidget(m_frame))
666        return passSubframeEventToSubframe(mev, subframe);
667
668    // WebKit2 code path.
669    subframe->eventHandler()->handleMouseReleaseEvent(mev.event());
670    return true;
671}
672
673PlatformMouseEvent EventHandler::currentPlatformMouseEvent() const
674{
675    NSView *windowView = nil;
676    if (Page* page = m_frame->page())
677        windowView = page->chrome()->platformPageClient();
678    return PlatformMouseEvent(currentNSEvent(), windowView);
679}
680
681#if ENABLE(CONTEXT_MENUS)
682bool EventHandler::sendContextMenuEvent(NSEvent *event)
683{
684    Page* page = m_frame->page();
685    if (!page)
686        return false;
687    return sendContextMenuEvent(PlatformMouseEvent(event, page->chrome()->platformPageClient()));
688}
689#endif // ENABLE(CONTEXT_MENUS)
690
691#if ENABLE(DRAG_SUPPORT)
692bool EventHandler::eventMayStartDrag(NSEvent *event)
693{
694    Page* page = m_frame->page();
695    if (!page)
696        return false;
697    return eventMayStartDrag(PlatformMouseEvent(event, page->chrome()->platformPageClient()));
698}
699#endif // ENABLE(DRAG_SUPPORT)
700
701bool EventHandler::eventActivatedView(const PlatformMouseEvent& event) const
702{
703    return m_activationEventNumber == event.eventNumber();
704}
705
706#if ENABLE(DRAG_SUPPORT)
707
708PassRefPtr<Clipboard> EventHandler::createDraggingClipboard() const
709{
710    NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
711    // Must be done before ondragstart adds types and data to the pboard,
712    // also done for security, as it erases data from the last drag
713    [pasteboard declareTypes:[NSArray array] owner:nil];
714    return ClipboardMac::create(Clipboard::DragAndDrop, pasteboard, ClipboardWritable, m_frame);
715}
716
717#endif
718
719bool EventHandler::tabsToAllFormControls(KeyboardEvent* event) const
720{
721    Page* page = m_frame->page();
722    if (!page)
723        return false;
724
725    KeyboardUIMode keyboardUIMode = page->chrome()->client()->keyboardUIMode();
726    bool handlingOptionTab = isKeyboardOptionTab(event);
727
728    // If tab-to-links is off, option-tab always highlights all controls
729    if ((keyboardUIMode & KeyboardAccessTabsToLinks) == 0 && handlingOptionTab)
730        return true;
731
732    // If system preferences say to include all controls, we always include all controls
733    if (keyboardUIMode & KeyboardAccessFull)
734        return true;
735
736    // Otherwise tab-to-links includes all controls, unless the sense is flipped via option-tab.
737    if (keyboardUIMode & KeyboardAccessTabsToLinks)
738        return !handlingOptionTab;
739
740    return handlingOptionTab;
741}
742
743bool EventHandler::needsKeyboardEventDisambiguationQuirks() const
744{
745    Document* document = m_frame->document();
746
747    // RSS view needs arrow key keypress events.
748    if (applicationIsSafari() && (document->url().protocolIs("feed") || document->url().protocolIs("feeds")))
749        return true;
750    Settings* settings = m_frame->settings();
751    if (!settings)
752        return false;
753
754#if ENABLE(DASHBOARD_SUPPORT)
755    if (settings->usesDashboardBackwardCompatibilityMode())
756        return true;
757#endif
758
759    if (settings->needsKeyboardEventDisambiguationQuirks())
760        return true;
761
762    return false;
763}
764
765unsigned EventHandler::accessKeyModifiers()
766{
767    // Control+Option key combinations are usually unused on Mac OS X, but not when VoiceOver is enabled.
768    // So, we use Control in this case, even though it conflicts with Emacs-style key bindings.
769    // See <https://bugs.webkit.org/show_bug.cgi?id=21107> for more detail.
770    if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled())
771        return PlatformKeyboardEvent::CtrlKey;
772
773    return PlatformKeyboardEvent::CtrlKey | PlatformKeyboardEvent::AltKey;
774}
775
776}
777