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