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