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