1/* 2 * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#import "config.h" 27#import "NetscapePlugin.h" 28 29#if ENABLE(NETSCAPE_PLUGIN_API) 30 31#import "NetscapeBrowserFuncs.h" 32#import "PluginController.h" 33#import "WKNPAPIPlugInContainerInternal.h" 34#import "WebEvent.h" 35#import <Carbon/Carbon.h> 36#import <WebCore/GraphicsContext.h> 37#import <WebCore/NotImplemented.h> 38#import <WebKitSystemInterface.h> 39#import <wtf/NeverDestroyed.h> 40#import <wtf/text/StringView.h> 41 42using namespace WebCore; 43 44namespace WebKit { 45 46#ifndef NP_NO_CARBON 47static const double nullEventIntervalActive = 0.02; 48 49static unsigned buttonStateFromLastMouseEvent; 50 51#endif 52 53NPError NetscapePlugin::setDrawingModel(NPDrawingModel drawingModel) 54{ 55 // The drawing model can only be set from NPP_New. 56 if (!m_inNPPNew) 57 return NPERR_GENERIC_ERROR; 58 59 switch (drawingModel) { 60#ifndef NP_NO_QUICKDRAW 61 case NPDrawingModelQuickDraw: 62#endif 63 case NPDrawingModelCoreGraphics: 64 case NPDrawingModelCoreAnimation: 65 m_drawingModel = drawingModel; 66 break; 67 68 default: 69 return NPERR_GENERIC_ERROR; 70 } 71 72 return NPERR_NO_ERROR; 73} 74 75NPError NetscapePlugin::setEventModel(NPEventModel eventModel) 76{ 77 // The event model can only be set from NPP_New. 78 if (!m_inNPPNew) 79 return NPERR_GENERIC_ERROR; 80 81 switch (eventModel) { 82#ifndef NP_NO_CARBON 83 case NPEventModelCarbon: 84#endif 85 case NPEventModelCocoa: 86 m_eventModel = eventModel; 87 break; 88 89 default: 90 return NPERR_GENERIC_ERROR; 91 } 92 93 return NPERR_NO_ERROR; 94} 95 96bool NetscapePlugin::getScreenTransform(NPCoordinateSpace sourceSpace, AffineTransform& transform) 97{ 98 ASSERT(transform.isIdentity()); 99 100 switch (sourceSpace) { 101 case NPCoordinateSpacePlugin: { 102 transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.y()); 103 transform.translate(m_viewFrameInWindowCoordinates.x(), m_viewFrameInWindowCoordinates.height() + m_viewFrameInWindowCoordinates.y()); 104 transform.flipY(); 105 transform *= m_pluginToRootViewTransform; 106 return true; 107 } 108 109 case NPCoordinateSpaceWindow: { 110 transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.y()); 111 return true; 112 } 113 114 case NPCoordinateSpaceFlippedWindow: { 115 transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.height() + m_windowFrameInScreenCoordinates.y()); 116 transform.flipY(); 117 return true; 118 } 119 120 case NPCoordinateSpaceScreen: { 121 // Nothing to do. 122 return true; 123 } 124 125 case NPCoordinateSpaceFlippedScreen: { 126 double screenHeight = [(NSScreen *)[[NSScreen screens] objectAtIndex:0] frame].size.height; 127 transform.translate(0, screenHeight); 128 transform.flipY(); 129 return true; 130 } 131 132 default: 133 return false; 134 } 135} 136 137NPBool NetscapePlugin::convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double& destX, double& destY, NPCoordinateSpace destSpace) 138{ 139 AffineTransform sourceTransform; 140 if (!getScreenTransform(sourceSpace, sourceTransform)) 141 return false; 142 143 AffineTransform destTransform; 144 if (!getScreenTransform(destSpace, destTransform)) 145 return false; 146 147 if (!destTransform.isInvertible()) 148 return false; 149 150 AffineTransform transform = destTransform.inverse() * sourceTransform; 151 152 FloatPoint destinationPoint = transform.mapPoint(FloatPoint(sourceX, sourceY)); 153 154 destX = destinationPoint.x(); 155 destY = destinationPoint.y(); 156 return true; 157} 158 159 160NPError NetscapePlugin::popUpContextMenu(NPMenu* npMenu) 161{ 162 if (!m_currentMouseEvent) 163 return NPERR_GENERIC_ERROR; 164 165 double screenX, screenY; 166 if (!convertPoint(m_currentMouseEvent->data.mouse.pluginX, m_currentMouseEvent->data.mouse.pluginY, NPCoordinateSpacePlugin, screenX, screenY, NPCoordinateSpaceScreen)) 167 ASSERT_NOT_REACHED(); 168 169 WKPopupContextMenu(reinterpret_cast<NSMenu *>(npMenu), NSMakePoint(screenX, screenY)); 170 return NPERR_NO_ERROR; 171} 172 173mach_port_t NetscapePlugin::compositingRenderServerPort() 174{ 175#if HAVE(OUT_OF_PROCESS_LAYER_HOSTING) 176 if (m_layerHostingMode == LayerHostingMode::OutOfProcess) 177 return MACH_PORT_NULL; 178#endif 179 180 return controller()->compositingRenderServerPort(); 181} 182 183void NetscapePlugin::openPluginPreferencePane() 184{ 185 controller()->openPluginPreferencePane(); 186} 187 188WKNPAPIPlugInContainer* NetscapePlugin::plugInContainer() 189{ 190 if (!m_plugInContainer) 191 m_plugInContainer = adoptNS([[WKNPAPIPlugInContainer alloc] _initWithNetscapePlugin:this]); 192 193 return m_plugInContainer.get(); 194} 195 196#ifndef NP_NO_CARBON 197typedef HashMap<WindowRef, NetscapePlugin*> WindowMap; 198 199static WindowMap& windowMap() 200{ 201 static NeverDestroyed<WindowMap> windowMap; 202 return windowMap; 203} 204#endif 205 206static void NSException_release(id, SEL) 207{ 208 // Do nothing. 209} 210 211void NetscapePlugin::platformPreInitialize() 212{ 213 if (m_pluginModule->pluginQuirks().contains(PluginQuirks::LeakAllThrownNSExceptions)) { 214 // Patch -[NSException release] to not release the object. 215 static dispatch_once_t once; 216 dispatch_once(&once, ^{ 217 Class exceptionClass = [NSException class]; 218 Method exceptionReleaseMethod = class_getInstanceMethod(exceptionClass, @selector(release)); 219 class_replaceMethod(exceptionClass, @selector(release), reinterpret_cast<IMP>(NSException_release), method_getTypeEncoding(exceptionReleaseMethod)); 220 }); 221 } 222} 223 224bool NetscapePlugin::platformPostInitialize() 225{ 226 if (m_drawingModel == static_cast<NPDrawingModel>(-1)) { 227#ifndef NP_NO_QUICKDRAW 228 // Default to QuickDraw if the plugin did not specify a drawing model. 229 m_drawingModel = NPDrawingModelQuickDraw; 230#else 231 // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics. 232 m_drawingModel = NPDrawingModelCoreGraphics; 233#endif 234 } 235 236 if (m_eventModel == static_cast<NPEventModel>(-1)) { 237 // If the plug-in did not specify a drawing model we default to Carbon when it is available. 238#ifndef NP_NO_CARBON 239 m_eventModel = NPEventModelCarbon; 240#else 241 m_eventModel = NPEventModelCocoa; 242#endif // NP_NO_CARBON 243 } 244 245#if !defined(NP_NO_CARBON) && !defined(NP_NO_QUICKDRAW) 246 // The CA drawing model does not work with the Carbon event model. 247 if (m_drawingModel == NPDrawingModelCoreAnimation && m_eventModel == NPEventModelCarbon) 248 return false; 249 250 // The Cocoa event model does not work with the QuickDraw drawing model. 251 if (m_eventModel == NPEventModelCocoa && m_drawingModel == NPDrawingModelQuickDraw) 252 return false; 253#endif 254 255#ifndef NP_NO_QUICKDRAW 256 // Right now we don't support the QuickDraw drawing model at all 257 if (m_drawingModel == NPDrawingModelQuickDraw && 258 !m_pluginModule->pluginQuirks().contains(PluginQuirks::AllowHalfBakedQuickDrawSupport)) 259 return false; 260#endif 261 262 updatePluginLayer(); 263 264#ifndef NP_NO_CARBON 265 if (m_eventModel == NPEventModelCarbon) { 266 // Initialize the fake Carbon window. 267 ::Rect bounds = { 0, 0, 0, 0 }; 268 CreateNewWindow(kDocumentWindowClass, kWindowNoTitleBarAttribute, &bounds, reinterpret_cast<WindowRef*>(&m_npCGContext.window)); 269 ASSERT(m_npCGContext.window); 270 271 // FIXME: Disable the backing store. 272 273 m_npWindow.window = &m_npCGContext; 274 275 ASSERT(!windowMap().contains(windowRef())); 276 windowMap().set(windowRef(), this); 277 278 // Start the null event timer. 279 // FIXME: Throttle null events when the plug-in isn't visible on screen. 280 m_nullEventTimer.startRepeating(nullEventIntervalActive); 281 } 282#endif 283 284 return true; 285} 286 287void NetscapePlugin::platformDestroy() 288{ 289 [m_plugInContainer _invalidate]; 290 291#ifndef NP_NO_CARBON 292 if (m_eventModel == NPEventModelCarbon) { 293 if (WindowRef window = windowRef()) { 294 // Destroy the fake Carbon window. 295 DisposeWindow(window); 296 297 ASSERT(windowMap().contains(window)); 298 windowMap().remove(window); 299 } 300 301 // Stop the null event timer. 302 m_nullEventTimer.stop(); 303 } 304#endif 305} 306 307bool NetscapePlugin::platformInvalidate(const IntRect&) 308{ 309 // NPN_InvalidateRect is just a no-op in the Core Animation drawing model. 310 if (m_drawingModel == NPDrawingModelCoreAnimation) 311 return true; 312 313 return false; 314} 315 316void NetscapePlugin::platformGeometryDidChange() 317{ 318 switch (m_eventModel) { 319 case NPEventModelCocoa: 320 // Nothing to do 321 break; 322#ifndef NP_NO_CARBON 323 case NPEventModelCarbon: 324 updateFakeWindowBounds(); 325 break; 326#endif 327 default: 328 ASSERT_NOT_REACHED(); 329 } 330} 331 332void NetscapePlugin::platformVisibilityDidChange() 333{ 334 // FIXME: Implement this. <http://webkit.org/b/44368>. 335 notImplemented(); 336} 337 338static inline NPCocoaEvent initializeEvent(NPCocoaEventType type) 339{ 340 NPCocoaEvent event; 341 342 event.type = type; 343 event.version = 0; 344 345 return event; 346} 347 348#ifndef NP_NO_CARBON 349NetscapePlugin* NetscapePlugin::netscapePluginFromWindow(WindowRef windowRef) 350{ 351 return windowMap().get(windowRef); 352} 353 354WindowRef NetscapePlugin::windowRef() const 355{ 356 ASSERT(m_eventModel == NPEventModelCarbon); 357 358 return reinterpret_cast<WindowRef>(m_npCGContext.window); 359} 360 361void NetscapePlugin::updateFakeWindowBounds() 362{ 363 double screenX, screenY; 364 bool didConvert = convertPoint(0, 0, NPCoordinateSpacePlugin, screenX, screenY, NPCoordinateSpaceFlippedScreen); 365 ASSERT_UNUSED(didConvert, didConvert); 366 367 Rect bounds; 368 bounds.top = screenY; 369 bounds.left = screenX; 370 bounds.bottom = screenY + m_pluginSize.height(); 371 bounds.right = screenX + m_pluginSize.width(); 372 373 ::SetWindowBounds(windowRef(), kWindowStructureRgn, &bounds); 374} 375 376unsigned NetscapePlugin::buttonState() 377{ 378 return buttonStateFromLastMouseEvent; 379} 380 381static inline EventRecord initializeEventRecord(EventKind eventKind) 382{ 383 EventRecord eventRecord; 384 385 eventRecord.what = eventKind; 386 eventRecord.message = 0; 387 eventRecord.when = TickCount(); 388 eventRecord.where = Point(); 389 eventRecord.modifiers = 0; 390 391 return eventRecord; 392} 393 394static bool anyMouseButtonIsDown(const WebEvent& event) 395{ 396 if (event.type() == WebEvent::MouseDown) 397 return true; 398 399 if (event.type() == WebEvent::MouseMove && static_cast<const WebMouseEvent&>(event).button() != WebMouseEvent::NoButton) 400 return true; 401 402 return false; 403} 404 405static bool rightMouseButtonIsDown(const WebEvent& event) 406{ 407 if (event.type() == WebEvent::MouseDown && static_cast<const WebMouseEvent&>(event).button() == WebMouseEvent::RightButton) 408 return true; 409 410 if (event.type() == WebEvent::MouseMove && static_cast<const WebMouseEvent&>(event).button() == WebMouseEvent::RightButton) 411 return true; 412 413 return false; 414} 415 416static EventModifiers modifiersForEvent(const WebEvent& event) 417{ 418 EventModifiers modifiers = 0; 419 420 // We only want to set the btnState if a mouse button is _not_ down. 421 if (!anyMouseButtonIsDown(event)) 422 modifiers |= btnState; 423 424 if (event.metaKey()) 425 modifiers |= cmdKey; 426 427 if (event.shiftKey()) 428 modifiers |= shiftKey; 429 430 if (event.altKey()) 431 modifiers |= optionKey; 432 433 // Set controlKey if the control key is down or the right mouse button is down. 434 if (event.controlKey() || rightMouseButtonIsDown(event)) 435 modifiers |= controlKey; 436 437 return modifiers; 438} 439 440#endif 441 442void NetscapePlugin::platformPaint(GraphicsContext* context, const IntRect& dirtyRect, bool isSnapshot) 443{ 444 CGContextRef platformContext = context->platformContext(); 445 446 switch (m_eventModel) { 447 case NPEventModelCocoa: { 448 // Don't send draw events when we're using the Core Animation drawing model. 449 if (!isSnapshot && m_drawingModel == NPDrawingModelCoreAnimation) 450 return; 451 452 NPCocoaEvent event = initializeEvent(NPCocoaEventDrawRect); 453 454 event.data.draw.context = platformContext; 455 event.data.draw.x = dirtyRect.x(); 456 event.data.draw.y = dirtyRect.y(); 457 event.data.draw.width = dirtyRect.width(); 458 event.data.draw.height = dirtyRect.height(); 459 460 NPP_HandleEvent(&event); 461 break; 462 } 463 464#ifndef NP_NO_CARBON 465 case NPEventModelCarbon: { 466 if (platformContext != m_npCGContext.context) { 467 m_npCGContext.context = platformContext; 468 callSetWindow(); 469 } 470 471 EventRecord event = initializeEventRecord(updateEvt); 472 event.message = reinterpret_cast<unsigned long>(windowRef()); 473 474 NPP_HandleEvent(&event); 475 break; 476 } 477#endif 478 479 default: 480 ASSERT_NOT_REACHED(); 481 } 482} 483 484static uint32_t modifierFlags(const WebEvent& event) 485{ 486 uint32_t modifiers = 0; 487 488 if (event.shiftKey()) 489 modifiers |= NSShiftKeyMask; 490 if (event.controlKey()) 491 modifiers |= NSControlKeyMask; 492 if (event.altKey()) 493 modifiers |= NSAlternateKeyMask; 494 if (event.metaKey()) 495 modifiers |= NSCommandKeyMask; 496 497 return modifiers; 498} 499 500static int32_t buttonNumber(WebMouseEvent::Button button) 501{ 502 switch (button) { 503 case WebMouseEvent::NoButton: 504 case WebMouseEvent::LeftButton: 505 return 0; 506 case WebMouseEvent::RightButton: 507 return 1; 508 case WebMouseEvent::MiddleButton: 509 return 2; 510 } 511 512 ASSERT_NOT_REACHED(); 513 return -1; 514} 515 516static void fillInCocoaEventFromMouseEvent(NPCocoaEvent& event, const WebMouseEvent& mouseEvent, const WebCore::IntPoint& eventPositionInPluginCoordinates) 517{ 518 event.data.mouse.modifierFlags = modifierFlags(mouseEvent); 519 event.data.mouse.pluginX = eventPositionInPluginCoordinates.x(); 520 event.data.mouse.pluginY = eventPositionInPluginCoordinates.y(); 521 event.data.mouse.buttonNumber = buttonNumber(mouseEvent.button()); 522 event.data.mouse.clickCount = mouseEvent.clickCount(); 523 event.data.mouse.deltaX = mouseEvent.deltaX(); 524 event.data.mouse.deltaY = mouseEvent.deltaY(); 525 event.data.mouse.deltaZ = mouseEvent.deltaZ(); 526} 527 528static NPCocoaEvent initializeMouseEvent(const WebMouseEvent& mouseEvent, const WebCore::IntPoint& eventPositionInPluginCoordinates) 529{ 530 NPCocoaEventType eventType; 531 532 switch (mouseEvent.type()) { 533 case WebEvent::MouseDown: 534 eventType = NPCocoaEventMouseDown; 535 break; 536 case WebEvent::MouseUp: 537 eventType = NPCocoaEventMouseUp; 538 break; 539 case WebEvent::MouseMove: 540 if (mouseEvent.button() == WebMouseEvent::NoButton) 541 eventType = NPCocoaEventMouseMoved; 542 else 543 eventType = NPCocoaEventMouseDragged; 544 break; 545 default: 546 ASSERT_NOT_REACHED(); 547 return NPCocoaEvent(); 548 } 549 550 NPCocoaEvent event = initializeEvent(eventType); 551 fillInCocoaEventFromMouseEvent(event, mouseEvent, eventPositionInPluginCoordinates); 552 return event; 553} 554 555bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& mouseEvent) 556{ 557 IntPoint eventPositionInPluginCoordinates; 558 if (!convertFromRootView(mouseEvent.position(), eventPositionInPluginCoordinates)) 559 return true; 560 561 switch (m_eventModel) { 562 case NPEventModelCocoa: { 563 NPCocoaEvent event = initializeMouseEvent(mouseEvent, eventPositionInPluginCoordinates); 564 565 NPCocoaEvent* previousMouseEvent = m_currentMouseEvent; 566 m_currentMouseEvent = &event; 567 568 // Protect against NPP_HandleEvent causing the plug-in to be destroyed, since we 569 // access m_currentMouseEvent afterwards. 570 Ref<NetscapePlugin> protect(*this); 571 572 NPP_HandleEvent(&event); 573 574 m_currentMouseEvent = previousMouseEvent; 575 576 // Some plug-ins return false even if the mouse event has been handled. 577 // This leads to bugs such as <rdar://problem/9167611>. Work around this 578 // by always returning true. 579 return true; 580 } 581 582#ifndef NP_NO_CARBON 583 case NPEventModelCarbon: { 584 EventKind eventKind = nullEvent; 585 586 switch (mouseEvent.type()) { 587 case WebEvent::MouseDown: 588 eventKind = mouseDown; 589 buttonStateFromLastMouseEvent |= (1 << buttonNumber(mouseEvent.button())); 590 break; 591 case WebEvent::MouseUp: 592 eventKind = mouseUp; 593 buttonStateFromLastMouseEvent &= ~(1 << buttonNumber(mouseEvent.button())); 594 break; 595 case WebEvent::MouseMove: 596 eventKind = nullEvent; 597 break; 598 default: 599 ASSERT_NOT_REACHED(); 600 } 601 602 EventRecord event = initializeEventRecord(eventKind); 603 event.modifiers = modifiersForEvent(mouseEvent); 604 605 double globalX; 606 double globalY; 607 if (!convertPoint(eventPositionInPluginCoordinates.x(), eventPositionInPluginCoordinates.y(), NPCoordinateSpacePlugin, globalX, globalY, NPCoordinateSpaceFlippedScreen)) 608 ASSERT_NOT_REACHED(); 609 610 event.where.h = globalX; 611 event.where.v = globalY; 612 613 NPP_HandleEvent(&event); 614 615 // Some plug-ins return false even if the mouse event has been handled. 616 // This leads to bugs such as <rdar://problem/9167611>. Work around this 617 // by always returning true. 618 return true; 619 } 620#endif 621 622 default: 623 ASSERT_NOT_REACHED(); 624 } 625 626 return false; 627} 628 629bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent& wheelEvent) 630{ 631 switch (m_eventModel) { 632 case NPEventModelCocoa: { 633 IntPoint eventPositionInPluginCoordinates; 634 if (!convertFromRootView(wheelEvent.position(), eventPositionInPluginCoordinates)) 635 return true; 636 637 NPCocoaEvent event = initializeEvent(NPCocoaEventScrollWheel); 638 639 event.data.mouse.modifierFlags = modifierFlags(wheelEvent); 640 event.data.mouse.pluginX = eventPositionInPluginCoordinates.x(); 641 event.data.mouse.pluginY = eventPositionInPluginCoordinates.y(); 642 event.data.mouse.buttonNumber = 0; 643 event.data.mouse.clickCount = 0; 644 event.data.mouse.deltaX = wheelEvent.delta().width(); 645 event.data.mouse.deltaY = wheelEvent.delta().height(); 646 event.data.mouse.deltaZ = 0; 647 return NPP_HandleEvent(&event); 648 } 649 650#ifndef NP_NO_CARBON 651 case NPEventModelCarbon: 652 // Carbon doesn't have wheel events. 653 break; 654#endif 655 656 default: 657 ASSERT_NOT_REACHED(); 658 } 659 660 return false; 661} 662 663bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& mouseEvent) 664{ 665 switch (m_eventModel) { 666 case NPEventModelCocoa: { 667 NPCocoaEvent event = initializeEvent(NPCocoaEventMouseEntered); 668 669 fillInCocoaEventFromMouseEvent(event, mouseEvent, IntPoint()); 670 return NPP_HandleEvent(&event); 671 } 672 673#ifndef NP_NO_CARBON 674 case NPEventModelCarbon: { 675 EventRecord eventRecord = initializeEventRecord(NPEventType_AdjustCursorEvent); 676 eventRecord.modifiers = modifiersForEvent(mouseEvent); 677 678 return NPP_HandleEvent(&eventRecord); 679 } 680#endif 681 682 default: 683 ASSERT_NOT_REACHED(); 684 } 685 686 return false; 687} 688 689bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& mouseEvent) 690{ 691 switch (m_eventModel) { 692 case NPEventModelCocoa: { 693 NPCocoaEvent event = initializeEvent(NPCocoaEventMouseExited); 694 695 fillInCocoaEventFromMouseEvent(event, mouseEvent, IntPoint()); 696 return NPP_HandleEvent(&event); 697 } 698 699#ifndef NP_NO_CARBON 700 case NPEventModelCarbon: { 701 EventRecord eventRecord = initializeEventRecord(NPEventType_AdjustCursorEvent); 702 eventRecord.modifiers = modifiersForEvent(mouseEvent); 703 704 return NPP_HandleEvent(&eventRecord); 705 } 706#endif 707 708 default: 709 ASSERT_NOT_REACHED(); 710 } 711 712 return false; 713} 714 715static unsigned modifierFlags(const WebKeyboardEvent& keyboardEvent) 716{ 717 unsigned modifierFlags = 0; 718 719 if (keyboardEvent.capsLockKey()) 720 modifierFlags |= NSAlphaShiftKeyMask; 721 if (keyboardEvent.shiftKey()) 722 modifierFlags |= NSShiftKeyMask; 723 if (keyboardEvent.controlKey()) 724 modifierFlags |= NSControlKeyMask; 725 if (keyboardEvent.altKey()) 726 modifierFlags |= NSAlternateKeyMask; 727 if (keyboardEvent.metaKey()) 728 modifierFlags |= NSCommandKeyMask; 729 730 return modifierFlags; 731} 732 733static bool isFlagsChangedEvent(const WebKeyboardEvent& keyboardEvent) 734{ 735 switch (keyboardEvent.nativeVirtualKeyCode()) { 736 case 54: // Right Command 737 case 55: // Left Command 738 739 case 57: // Capslock 740 741 case 56: // Left Shift 742 case 60: // Right Shift 743 744 case 58: // Left Alt 745 case 61: // Right Alt 746 747 case 59: // Left Ctrl 748 case 62: // Right Ctrl 749 return true; 750 } 751 752 return false; 753} 754 755static NPCocoaEvent initializeKeyboardEvent(const WebKeyboardEvent& keyboardEvent) 756{ 757 NPCocoaEventType eventType; 758 759 if (isFlagsChangedEvent(keyboardEvent)) 760 eventType = NPCocoaEventFlagsChanged; 761 else { 762 switch (keyboardEvent.type()) { 763 case WebEvent::KeyDown: 764 eventType = NPCocoaEventKeyDown; 765 break; 766 case WebEvent::KeyUp: 767 eventType = NPCocoaEventKeyUp; 768 break; 769 default: 770 ASSERT_NOT_REACHED(); 771 return NPCocoaEvent(); 772 } 773 } 774 775 NPCocoaEvent event = initializeEvent(eventType); 776 event.data.key.modifierFlags = modifierFlags(keyboardEvent); 777 event.data.key.characters = reinterpret_cast<NPNSString*>(static_cast<NSString*>(keyboardEvent.text())); 778 event.data.key.charactersIgnoringModifiers = reinterpret_cast<NPNSString*>(static_cast<NSString*>(keyboardEvent.unmodifiedText())); 779 event.data.key.isARepeat = keyboardEvent.isAutoRepeat(); 780 event.data.key.keyCode = keyboardEvent.nativeVirtualKeyCode(); 781 782 return event; 783} 784 785bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent& keyboardEvent) 786{ 787 bool handled = false; 788 789 switch (m_eventModel) { 790 case NPEventModelCocoa: { 791 if (keyboardEvent.type() == WebEvent::KeyDown) { 792 m_hasHandledAKeyDownEvent = true; 793 794 if (!m_pluginWantsLegacyCocoaTextInput && m_isComplexTextInputEnabled && !keyboardEvent.isAutoRepeat()) { 795 // When complex text is enabled in the new model, the plug-in should never 796 // receive any key down or key up events until the composition is complete. 797 m_ignoreNextKeyUpEventCounter++; 798 return true; 799 } 800 } else if (keyboardEvent.type() == WebEvent::KeyUp && m_ignoreNextKeyUpEventCounter) { 801 m_ignoreNextKeyUpEventCounter--; 802 return true; 803 } 804 805 NPCocoaEvent event = initializeKeyboardEvent(keyboardEvent); 806 int16_t returnValue = NPP_HandleEvent(&event); 807 handled = returnValue; 808 809 if (!m_pluginWantsLegacyCocoaTextInput) { 810 if (event.type == NPCocoaEventKeyDown && returnValue == kNPEventStartIME) { 811 if (!keyboardEvent.isAutoRepeat()) 812 m_ignoreNextKeyUpEventCounter++; 813 setComplexTextInputEnabled(true); 814 } 815 } 816 817 break; 818 } 819 820#ifndef NP_NO_CARBON 821 case NPEventModelCarbon: { 822 EventKind eventKind = nullEvent; 823 824 switch (keyboardEvent.type()) { 825 case WebEvent::KeyDown: 826 eventKind = keyboardEvent.isAutoRepeat() ? autoKey : keyDown; 827 break; 828 case WebEvent::KeyUp: 829 eventKind = keyUp; 830 break; 831 default: 832 ASSERT_NOT_REACHED(); 833 } 834 835 EventRecord event = initializeEventRecord(eventKind); 836 event.modifiers = modifiersForEvent(keyboardEvent); 837 event.message = keyboardEvent.nativeVirtualKeyCode() << 8 | keyboardEvent.macCharCode(); 838 handled = NPP_HandleEvent(&event); 839 break; 840 } 841#endif 842 843 default: 844 ASSERT_NOT_REACHED(); 845 } 846 847 // Most plug-ins simply return true for all keyboard events, even those that aren't handled. 848 // This leads to bugs such as <rdar://problem/8740926>. We work around this by returning false 849 // if the keyboard event has the command modifier pressed. 850 // However, for command-A (the shortcurt for 'Select All' we will always return true, since we don't 851 // want the entire page to be selected if the focus is in a plug-in text field (see <rdar://problem/9309903>). 852 if (keyboardEvent.metaKey()) { 853 if (keyboardEvent.text() == "a") 854 return true; 855 856 return false; 857 } 858 859 return handled; 860} 861 862void NetscapePlugin::platformSetFocus(bool hasFocus) 863{ 864 m_pluginHasFocus = hasFocus; 865 pluginFocusOrWindowFocusChanged(); 866 867 switch (m_eventModel) { 868 case NPEventModelCocoa: { 869 NPCocoaEvent event = initializeEvent(NPCocoaEventFocusChanged); 870 871 event.data.focus.hasFocus = hasFocus; 872 NPP_HandleEvent(&event); 873 break; 874 } 875 876#ifndef NP_NO_CARBON 877 case NPEventModelCarbon: { 878 EventRecord event = initializeEventRecord(hasFocus ? NPEventType_GetFocusEvent : NPEventType_LoseFocusEvent); 879 880 NPP_HandleEvent(&event); 881 break; 882 } 883#endif 884 885 default: 886 ASSERT_NOT_REACHED(); 887 } 888} 889 890bool NetscapePlugin::wantsPluginRelativeNPWindowCoordinates() 891{ 892 return true; 893} 894 895void NetscapePlugin::windowFocusChanged(bool hasFocus) 896{ 897 m_windowHasFocus = hasFocus; 898 pluginFocusOrWindowFocusChanged(); 899 900 switch (m_eventModel) { 901 case NPEventModelCocoa: { 902 NPCocoaEvent event = initializeEvent(NPCocoaEventWindowFocusChanged); 903 904 event.data.focus.hasFocus = hasFocus; 905 NPP_HandleEvent(&event); 906 break; 907 } 908 909#ifndef NP_NO_CARBON 910 case NPEventModelCarbon: { 911 HiliteWindow(windowRef(), hasFocus); 912 if (hasFocus) 913 SetUserFocusWindow(windowRef()); 914 915 EventRecord event = initializeEventRecord(activateEvt); 916 event.message = reinterpret_cast<unsigned long>(windowRef()); 917 if (hasFocus) 918 event.modifiers |= activeFlag; 919 920 NPP_HandleEvent(&event); 921 break; 922 } 923#endif 924 925 default: 926 ASSERT_NOT_REACHED(); 927 } 928} 929 930void NetscapePlugin::windowAndViewFramesChanged(const IntRect& windowFrameInScreenCoordinates, const IntRect& viewFrameInWindowCoordinates) 931{ 932 m_windowFrameInScreenCoordinates = windowFrameInScreenCoordinates; 933 m_viewFrameInWindowCoordinates = viewFrameInWindowCoordinates; 934 935 switch (m_eventModel) { 936 case NPEventModelCocoa: 937 // Nothing to do. 938 break; 939 940#ifndef NP_NO_CARBON 941 case NPEventModelCarbon: 942 updateFakeWindowBounds(); 943 break; 944#endif 945 946 default: 947 ASSERT_NOT_REACHED(); 948 } 949} 950 951void NetscapePlugin::windowVisibilityChanged(bool visible) 952{ 953 if (visible) 954 callSetWindow(); 955 else 956 callSetWindowInvisible(); 957} 958 959uint64_t NetscapePlugin::pluginComplexTextInputIdentifier() const 960{ 961 // Just return a dummy value; this is only called for in-process plug-ins, which we don't support on Mac. 962 return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this)); 963} 964 965 966#ifndef NP_NO_CARBON 967static bool convertStringToKeyCodes(StringView string, ScriptCode scriptCode, Vector<UInt8>& keyCodes) 968{ 969 // Create the mapping. 970 UnicodeMapping mapping; 971 972 if (GetTextEncodingFromScriptInfo(scriptCode, kTextLanguageDontCare, kTextRegionDontCare, &mapping.otherEncoding) != noErr) 973 return false; 974 975 mapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, kTextEncodingDefaultVariant, kTextEncodingDefaultFormat); 976 mapping.mappingVersion = kUnicodeUseLatestMapping; 977 978 // Create the converter 979 UnicodeToTextInfo textInfo; 980 981 if (CreateUnicodeToTextInfo(&mapping, &textInfo) != noErr) 982 return false; 983 984 ByteCount inputLength = string.length() * sizeof(UniChar); 985 ByteCount inputRead; 986 ByteCount outputLength; 987 ByteCount maxOutputLength = string.length() * sizeof(UniChar); 988 989 Vector<UInt8> outputData(maxOutputLength); 990 OSStatus status = ConvertFromUnicodeToText(textInfo, inputLength, string.upconvertedCharacters(), kNilOptions, 0, 0, 0, 0, maxOutputLength, &inputRead, &outputLength, outputData.data()); 991 992 DisposeUnicodeToTextInfo(&textInfo); 993 994 if (status != noErr) 995 return false; 996 997 keyCodes = WTF::move(outputData); 998 return true; 999} 1000#endif 1001 1002void NetscapePlugin::sendComplexTextInput(const String& textInput) 1003{ 1004 if (!m_pluginWantsLegacyCocoaTextInput) { 1005 // In the updated Cocoa text input spec, text input is disabled when the text input string has been sent 1006 // by the UI process. Since the UI process has also updated its state, we can just reset the variable here 1007 // instead of calling setComplexTextInputEnabled. 1008 m_isComplexTextInputEnabled = false; 1009 1010 // The UI process can also disable text input by sending an empty input string. In this case, we don't want 1011 // to send it to the plug-in. 1012 if (textInput.isNull()) 1013 return; 1014 } 1015 1016 switch (m_eventModel) { 1017 case NPEventModelCocoa: { 1018 NPCocoaEvent event = initializeEvent(NPCocoaEventTextInput); 1019 event.data.text.text = reinterpret_cast<NPNSString*>(static_cast<NSString*>(textInput)); 1020 NPP_HandleEvent(&event); 1021 break; 1022 } 1023#ifndef NP_NO_CARBON 1024 case NPEventModelCarbon: { 1025 ScriptCode scriptCode = WKGetScriptCodeFromCurrentKeyboardInputSource(); 1026 Vector<UInt8> keyCodes; 1027 1028 if (!convertStringToKeyCodes(textInput, scriptCode, keyCodes)) 1029 return; 1030 1031 // Set the script code as the keyboard script. Normally Carbon does this whenever the input source changes. 1032 // However, this is only done for the process that has the keyboard focus. We cheat and do it here instead. 1033 SetScriptManagerVariable(smKeyScript, scriptCode); 1034 1035 EventRecord event = initializeEventRecord(keyDown); 1036 event.modifiers = 0; 1037 1038 for (auto& keyCode : keyCodes) { 1039 event.message = keyCode; 1040 NPP_HandleEvent(&event); 1041 } 1042 break; 1043 } 1044#endif 1045 default: 1046 ASSERT_NOT_REACHED(); 1047 } 1048} 1049 1050void NetscapePlugin::setLayerHostingMode(LayerHostingMode layerHostingMode) 1051{ 1052 m_layerHostingMode = layerHostingMode; 1053 1054 // Tell the plug-in about the new compositing render server port. If it returns OK we'll ask it again for a new layer. 1055 mach_port_t port = NetscapePlugin::compositingRenderServerPort(); 1056 if (NPP_SetValue(static_cast<NPNVariable>(WKNVCALayerRenderServerPort), &port) != NPERR_NO_ERROR) 1057 return; 1058 1059 m_pluginLayer = nullptr; 1060 updatePluginLayer(); 1061} 1062 1063void NetscapePlugin::pluginFocusOrWindowFocusChanged() 1064{ 1065 bool pluginHasFocusAndWindowHasFocus = m_pluginHasFocus && m_windowHasFocus; 1066 1067 controller()->pluginFocusOrWindowFocusChanged(pluginHasFocusAndWindowHasFocus); 1068 1069 // In the updated Cocoa text input spec, the plug-in will enable complex text input 1070 // by returning kNPEventStartIME from it's NPCocoaEventKeyDown handler. 1071 if (!m_pluginWantsLegacyCocoaTextInput) 1072 return; 1073 1074 // In the old model, if the plug-in is focused, enable complex text input. 1075 setComplexTextInputEnabled(pluginHasFocusAndWindowHasFocus); 1076} 1077 1078void NetscapePlugin::setComplexTextInputEnabled(bool complexTextInputEnabled) 1079{ 1080 if (m_isComplexTextInputEnabled == complexTextInputEnabled) 1081 return; 1082 1083 m_isComplexTextInputEnabled = complexTextInputEnabled; 1084 1085 PluginComplexTextInputState complexTextInputState = PluginComplexTextInputDisabled; 1086 if (m_isComplexTextInputEnabled) 1087 complexTextInputState = m_pluginWantsLegacyCocoaTextInput ? PluginComplexTextInputEnabledLegacy : PluginComplexTextInputEnabled; 1088 1089 controller()->setComplexTextInputState(complexTextInputState); 1090} 1091 1092PlatformLayer* NetscapePlugin::pluginLayer() 1093{ 1094 return static_cast<PlatformLayer*>(m_pluginLayer.get()); 1095} 1096 1097static void makeCGLPresentLayerOpaque(CALayer *pluginRootLayer) 1098{ 1099 // We look for a layer that's the only sublayer of the root layer that is an instance 1100 // of the CGLPresentLayer class which in turn is a subclass of CAOpenGLLayer and make 1101 // it opaque if all these conditions hold. 1102 1103 NSArray *sublayers = [pluginRootLayer sublayers]; 1104 if ([sublayers count] != 1) 1105 return; 1106 1107 Class cglPresentLayerClass = NSClassFromString(@"CGLPresentLayer"); 1108 if (![cglPresentLayerClass isSubclassOfClass:[CAOpenGLLayer class]]) 1109 return; 1110 1111 CALayer *layer = [sublayers objectAtIndex:0]; 1112 if (![layer isKindOfClass:cglPresentLayerClass]) 1113 return; 1114 1115 [layer setOpaque:YES]; 1116} 1117 1118void NetscapePlugin::updatePluginLayer() 1119{ 1120 if (m_drawingModel != NPDrawingModelCoreAnimation) 1121 return; 1122 1123 void* value = 0; 1124 1125 // Get the Core Animation layer. 1126 if (NPP_GetValue(NPPVpluginCoreAnimationLayer, &value) != NPERR_NO_ERROR) 1127 return; 1128 1129 if (!value) 1130 return; 1131 1132 ASSERT(!m_pluginLayer); 1133 1134 // The original Core Animation drawing model required that plug-ins pass a retained layer 1135 // to the browser, which the browser would then adopt. However, the final spec changed this 1136 // (See https://wiki.mozilla.org/NPAPI:CoreAnimationDrawingModel for more information) 1137 // after a version of WebKit1 with the original implementation had shipped, but that now means 1138 // that any plug-ins that expect the WebKit1 behavior would leak the CALayer. 1139 // For plug-ins that we know return retained layers, we have the ReturnsRetainedCoreAnimationLayer 1140 // plug-in quirk. Plug-ins can also check for whether the browser expects a non-retained layer to 1141 // be returned by using NPN_GetValue and pass the WKNVExpectsNonretainedLayer parameter. 1142 // https://bugs.webkit.org/show_bug.cgi?id=58282 describes the bug where WebKit expects retained layers. 1143 if (m_pluginReturnsNonretainedLayer) 1144 m_pluginLayer = reinterpret_cast<CALayer *>(value); 1145 else 1146 m_pluginLayer = adoptNS(reinterpret_cast<CALayer *>(value)); 1147 1148 if (m_pluginModule->pluginQuirks().contains(PluginQuirks::MakeOpaqueUnlessTransparentSilverlightBackgroundAttributeExists) && 1149 !m_isTransparent) 1150 makeCGLPresentLayerOpaque(m_pluginLayer.get()); 1151} 1152 1153#ifndef NP_NO_CARBON 1154void NetscapePlugin::nullEventTimerFired() 1155{ 1156 EventRecord event = initializeEventRecord(nullEvent); 1157 1158 event.message = 0; 1159 CGPoint mousePosition; 1160 HIGetMousePosition(kHICoordSpaceScreenPixel, 0, &mousePosition); 1161 event.where.h = mousePosition.x; 1162 event.where.v = mousePosition.y; 1163 1164 event.modifiers = GetCurrentKeyModifiers(); 1165 if (!Button()) 1166 event.modifiers |= btnState; 1167 1168 NPP_HandleEvent(&event); 1169} 1170#endif 1171 1172} // namespace WebKit 1173 1174#endif // ENABLE(NETSCAPE_PLUGIN_API) 1175