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