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 #import "PluginController.h" 30 #import "WebEvent.h" 31 #import <WebCore/GraphicsContext.h> 32 #import <Carbon/Carbon.h> 33 #import <WebKitSystemInterface.h> 34 35 using namespace WebCore; 36 37 namespace WebKit { 38 39 #ifndef NP_NO_CARBON 40 static const double nullEventIntervalActive = 0.02; 41 static const double nullEventIntervalNotActive = 0.25; 42 43 static unsigned buttonStateFromLastMouseEvent; 44 45 #endif 46 47 NPError NetscapePlugin::setDrawingModel(NPDrawingModel drawingModel) 48 { 49 // The drawing model can only be set from NPP_New. 50 if (!m_inNPPNew) 51 return NPERR_GENERIC_ERROR; 52 53 switch (drawingModel) { 54 #ifndef NP_NO_QUICKDRAW 55 case NPDrawingModelQuickDraw: 56 #endif 57 case NPDrawingModelCoreGraphics: 58 case NPDrawingModelCoreAnimation: 59 m_drawingModel = drawingModel; 60 break; 61 62 default: 63 return NPERR_GENERIC_ERROR; 64 } 65 66 return NPERR_NO_ERROR; 67 } 68 69 NPError NetscapePlugin::setEventModel(NPEventModel eventModel) 70 { 71 // The event model can only be set from NPP_New. 72 if (!m_inNPPNew) 73 return NPERR_GENERIC_ERROR; 74 75 switch (eventModel) { 76 #ifndef NP_NO_CARBON 77 case NPEventModelCarbon: 78 #endif 79 case NPEventModelCocoa: 80 m_eventModel = eventModel; 81 break; 82 83 default: 84 return NPERR_GENERIC_ERROR; 85 } 86 87 return NPERR_NO_ERROR; 88 } 89 90 static double flipScreenYCoordinate(double y) 91 { 92 return [[[NSScreen screens] objectAtIndex:0] frame].size.height - y; 93 } 94 95 NPBool NetscapePlugin::convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double& destX, double& destY, NPCoordinateSpace destSpace) 96 { 97 if (sourceSpace == destSpace) { 98 destX = sourceX; 99 destY = sourceY; 100 return true; 101 } 102 103 double sourceXInScreenSpace; 104 double sourceYInScreenSpace; 105 106 FloatPoint sourceInScreenSpace; 107 switch (sourceSpace) { 108 case NPCoordinateSpacePlugin: 109 sourceXInScreenSpace = sourceX + m_windowFrameInScreenCoordinates.x() + m_viewFrameInWindowCoordinates.x() + m_npWindow.x; 110 sourceYInScreenSpace = m_windowFrameInScreenCoordinates.y() + m_viewFrameInWindowCoordinates.y() + m_viewFrameInWindowCoordinates.height() - (sourceY + m_npWindow.y); 111 break; 112 case NPCoordinateSpaceWindow: 113 sourceXInScreenSpace = sourceX + m_windowFrameInScreenCoordinates.x(); 114 sourceYInScreenSpace = sourceY + m_windowFrameInScreenCoordinates.y(); 115 break; 116 case NPCoordinateSpaceFlippedWindow: 117 sourceXInScreenSpace = sourceX + m_windowFrameInScreenCoordinates.x(); 118 sourceYInScreenSpace = m_windowFrameInScreenCoordinates.y() + m_windowFrameInScreenCoordinates.height() - sourceY; 119 break; 120 case NPCoordinateSpaceScreen: 121 sourceXInScreenSpace = sourceX; 122 sourceYInScreenSpace = sourceY; 123 break; 124 case NPCoordinateSpaceFlippedScreen: 125 sourceXInScreenSpace = sourceX; 126 sourceYInScreenSpace = flipScreenYCoordinate(sourceY); 127 break; 128 default: 129 return false; 130 } 131 132 // Now convert back. 133 switch (destSpace) { 134 case NPCoordinateSpacePlugin: 135 destX = sourceXInScreenSpace - (m_windowFrameInScreenCoordinates.x() + m_viewFrameInWindowCoordinates.x() + m_npWindow.x); 136 destY = m_windowFrameInScreenCoordinates.y() + m_viewFrameInWindowCoordinates.y() + m_viewFrameInWindowCoordinates.height() - (sourceYInScreenSpace + m_npWindow.y); 137 break; 138 case NPCoordinateSpaceWindow: 139 destX = sourceXInScreenSpace - m_windowFrameInScreenCoordinates.x(); 140 destY = sourceYInScreenSpace - m_windowFrameInScreenCoordinates.y(); 141 break; 142 case NPCoordinateSpaceFlippedWindow: 143 destX = sourceXInScreenSpace - m_windowFrameInScreenCoordinates.x(); 144 destY = sourceYInScreenSpace - m_windowFrameInScreenCoordinates.y(); 145 destY = m_windowFrameInScreenCoordinates.height() - destY; 146 break; 147 case NPCoordinateSpaceScreen: 148 destX = sourceXInScreenSpace; 149 destY = sourceYInScreenSpace; 150 break; 151 case NPCoordinateSpaceFlippedScreen: 152 destX = sourceXInScreenSpace; 153 destY = flipScreenYCoordinate(sourceYInScreenSpace); 154 break; 155 default: 156 return false; 157 } 158 159 return true; 160 } 161 162 163 NPError NetscapePlugin::popUpContextMenu(NPMenu* npMenu) 164 { 165 if (!m_currentMouseEvent) 166 return NPERR_GENERIC_ERROR; 167 168 double screenX, screenY; 169 if (!convertPoint(m_currentMouseEvent->data.mouse.pluginX, m_currentMouseEvent->data.mouse.pluginY, NPCoordinateSpacePlugin, screenX, screenY, NPCoordinateSpaceScreen)) 170 ASSERT_NOT_REACHED(); 171 172 WKPopupContextMenu(reinterpret_cast<NSMenu *>(npMenu), NSMakePoint(screenX, screenY)); 173 return NPERR_NO_ERROR; 174 } 175 176 mach_port_t NetscapePlugin::compositingRenderServerPort() 177 { 178 return m_pluginController->compositingRenderServerPort(); 179 } 180 181 #ifndef NP_NO_CARBON 182 typedef HashMap<WindowRef, NetscapePlugin*> WindowMap; 183 184 static WindowMap& windowMap() 185 { 186 DEFINE_STATIC_LOCAL(WindowMap, windowMap, ()); 187 188 return windowMap; 189 } 190 #endif 191 192 bool NetscapePlugin::platformPostInitialize() 193 { 194 if (m_drawingModel == static_cast<NPDrawingModel>(-1)) { 195 #ifndef NP_NO_QUICKDRAW 196 // Default to QuickDraw if the plugin did not specify a drawing model. 197 m_drawingModel = NPDrawingModelQuickDraw; 198 #else 199 // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics. 200 m_drawingModel = NPDrawingModelCoreGraphics; 201 #endif 202 } 203 204 if (m_eventModel == static_cast<NPEventModel>(-1)) { 205 // If the plug-in did not specify a drawing model we default to Carbon when it is available. 206 #ifndef NP_NO_CARBON 207 m_eventModel = NPEventModelCarbon; 208 #else 209 m_eventModel = NPEventModelCocoa; 210 #endif // NP_NO_CARBON 211 } 212 213 #if !defined(NP_NO_CARBON) && !defined(NP_NO_QUICKDRAW) 214 // The CA drawing model does not work with the Carbon event model. 215 if (m_drawingModel == NPDrawingModelCoreAnimation && m_eventModel == NPEventModelCarbon) 216 return false; 217 218 // The Cocoa event model does not work with the QuickDraw drawing model. 219 if (m_eventModel == NPEventModelCocoa && m_drawingModel == NPDrawingModelQuickDraw) 220 return false; 221 #endif 222 223 #ifndef NP_NO_QUICKDRAW 224 // Right now we don't support the QuickDraw drawing model at all 225 if (m_drawingModel == NPDrawingModelQuickDraw && 226 !m_pluginModule->pluginQuirks().contains(PluginQuirks::AllowHalfBakedQuickDrawSupport)) 227 return false; 228 #endif 229 230 if (m_drawingModel == NPDrawingModelCoreAnimation) { 231 void* value = 0; 232 // Get the Core Animation layer. 233 if (NPP_GetValue(NPPVpluginCoreAnimationLayer, &value) == NPERR_NO_ERROR && value) { 234 ASSERT(!m_pluginLayer); 235 m_pluginLayer = reinterpret_cast<CALayer *>(value); 236 } 237 } 238 239 #ifndef NP_NO_CARBON 240 if (m_eventModel == NPEventModelCarbon) { 241 // Initialize the fake Carbon window. 242 ::Rect bounds = { 0, 0, 0, 0 }; 243 CreateNewWindow(kDocumentWindowClass, kWindowNoTitleBarAttribute, &bounds, reinterpret_cast<WindowRef*>(&m_npCGContext.window)); 244 ASSERT(m_npCGContext.window); 245 246 // FIXME: Disable the backing store. 247 248 m_npWindow.window = &m_npCGContext; 249 250 ASSERT(!windowMap().contains(windowRef())); 251 windowMap().set(windowRef(), this); 252 253 // Start the null event timer. 254 // FIXME: Throttle null events when the plug-in isn't visible on screen. 255 m_nullEventTimer.startRepeating(nullEventIntervalActive); 256 } 257 #endif 258 259 return true; 260 } 261 262 void NetscapePlugin::platformDestroy() 263 { 264 #ifndef NP_NO_CARBON 265 if (m_eventModel == NPEventModelCarbon) { 266 if (WindowRef window = windowRef()) { 267 // Destroy the fake Carbon window. 268 DisposeWindow(window); 269 270 ASSERT(windowMap().contains(window)); 271 windowMap().remove(window); 272 } 273 274 // Stop the null event timer. 275 m_nullEventTimer.stop(); 276 } 277 #endif 278 } 279 280 bool NetscapePlugin::platformInvalidate(const IntRect&) 281 { 282 // NPN_InvalidateRect is just a no-op in the Core Animation drawing model. 283 if (m_drawingModel == NPDrawingModelCoreAnimation) 284 return true; 285 286 return false; 287 } 288 289 void NetscapePlugin::platformGeometryDidChange() 290 { 291 } 292 293 static inline NPCocoaEvent initializeEvent(NPCocoaEventType type) 294 { 295 NPCocoaEvent event; 296 297 event.type = type; 298 event.version = 0; 299 300 return event; 301 } 302 303 #ifndef NP_NO_CARBON 304 NetscapePlugin* NetscapePlugin::netscapePluginFromWindow(WindowRef windowRef) 305 { 306 return windowMap().get(windowRef); 307 } 308 309 WindowRef NetscapePlugin::windowRef() const 310 { 311 ASSERT(m_eventModel == NPEventModelCarbon); 312 313 return reinterpret_cast<WindowRef>(m_npCGContext.window); 314 } 315 316 unsigned NetscapePlugin::buttonState() 317 { 318 return buttonStateFromLastMouseEvent; 319 } 320 321 static inline EventRecord initializeEventRecord(EventKind eventKind) 322 { 323 EventRecord eventRecord; 324 325 eventRecord.what = eventKind; 326 eventRecord.message = 0; 327 eventRecord.when = TickCount(); 328 eventRecord.where = Point(); 329 eventRecord.modifiers = 0; 330 331 return eventRecord; 332 } 333 334 static bool anyMouseButtonIsDown(const WebEvent& event) 335 { 336 if (event.type() == WebEvent::MouseDown) 337 return true; 338 339 if (event.type() == WebEvent::MouseMove && static_cast<const WebMouseEvent&>(event).button() != WebMouseEvent::NoButton) 340 return true; 341 342 return false; 343 } 344 345 static bool rightMouseButtonIsDown(const WebEvent& event) 346 { 347 if (event.type() == WebEvent::MouseDown && static_cast<const WebMouseEvent&>(event).button() == WebMouseEvent::RightButton) 348 return true; 349 350 if (event.type() == WebEvent::MouseMove && static_cast<const WebMouseEvent&>(event).button() == WebMouseEvent::RightButton) 351 return true; 352 353 return false; 354 } 355 356 static EventModifiers modifiersForEvent(const WebEvent& event) 357 { 358 EventModifiers modifiers = 0; 359 360 // We only want to set the btnState if a mouse button is _not_ down. 361 if (!anyMouseButtonIsDown(event)) 362 modifiers |= btnState; 363 364 if (event.metaKey()) 365 modifiers |= cmdKey; 366 367 if (event.shiftKey()) 368 modifiers |= shiftKey; 369 370 if (event.altKey()) 371 modifiers |= optionKey; 372 373 // Set controlKey if the control key is down or the right mouse button is down. 374 if (event.controlKey() || rightMouseButtonIsDown(event)) 375 modifiers |= controlKey; 376 377 return modifiers; 378 } 379 380 #endif 381 382 void NetscapePlugin::platformPaint(GraphicsContext* context, const IntRect& dirtyRect, bool isSnapshot) 383 { 384 CGContextRef platformContext = context->platformContext(); 385 386 // Translate the context so that the origin is at the top left corner of the plug-in view. 387 context->translate(m_frameRect.x(), m_frameRect.y()); 388 389 switch (m_eventModel) { 390 case NPEventModelCocoa: { 391 // Don't send draw events when we're using the Core Animation drawing model. 392 if (!isSnapshot && m_drawingModel == NPDrawingModelCoreAnimation) 393 return; 394 395 NPCocoaEvent event = initializeEvent(NPCocoaEventDrawRect); 396 397 event.data.draw.context = platformContext; 398 event.data.draw.x = dirtyRect.x() - m_frameRect.x(); 399 event.data.draw.y = dirtyRect.y() - m_frameRect.y(); 400 event.data.draw.width = dirtyRect.width(); 401 event.data.draw.height = dirtyRect.height(); 402 403 NPP_HandleEvent(&event); 404 break; 405 } 406 407 #ifndef NP_NO_CARBON 408 case NPEventModelCarbon: { 409 if (platformContext != m_npCGContext.context) { 410 m_npCGContext.context = platformContext; 411 callSetWindow(); 412 } 413 414 EventRecord event = initializeEventRecord(updateEvt); 415 event.message = reinterpret_cast<unsigned long>(windowRef()); 416 417 NPP_HandleEvent(&event); 418 break; 419 } 420 #endif 421 422 default: 423 ASSERT_NOT_REACHED(); 424 } 425 } 426 427 static uint32_t modifierFlags(const WebEvent& event) 428 { 429 uint32_t modifiers = 0; 430 431 if (event.shiftKey()) 432 modifiers |= NSShiftKeyMask; 433 if (event.controlKey()) 434 modifiers |= NSControlKeyMask; 435 if (event.altKey()) 436 modifiers |= NSAlternateKeyMask; 437 if (event.metaKey()) 438 modifiers |= NSCommandKeyMask; 439 440 return modifiers; 441 } 442 443 static int32_t buttonNumber(WebMouseEvent::Button button) 444 { 445 switch (button) { 446 case WebMouseEvent::NoButton: 447 case WebMouseEvent::LeftButton: 448 return 0; 449 case WebMouseEvent::RightButton: 450 return 1; 451 case WebMouseEvent::MiddleButton: 452 return 2; 453 } 454 455 ASSERT_NOT_REACHED(); 456 return -1; 457 } 458 459 static void fillInCocoaEventFromMouseEvent(NPCocoaEvent& event, const WebMouseEvent& mouseEvent, const WebCore::IntPoint& pluginLocation) 460 { 461 event.data.mouse.modifierFlags = modifierFlags(mouseEvent); 462 event.data.mouse.pluginX = mouseEvent.position().x() - pluginLocation.x(); 463 event.data.mouse.pluginY = mouseEvent.position().y() - pluginLocation.y(); 464 event.data.mouse.buttonNumber = buttonNumber(mouseEvent.button()); 465 event.data.mouse.clickCount = mouseEvent.clickCount(); 466 event.data.mouse.deltaX = mouseEvent.deltaX(); 467 event.data.mouse.deltaY = mouseEvent.deltaY(); 468 event.data.mouse.deltaZ = mouseEvent.deltaZ(); 469 } 470 471 static NPCocoaEvent initializeMouseEvent(const WebMouseEvent& mouseEvent, const WebCore::IntPoint& pluginLocation) 472 { 473 NPCocoaEventType eventType; 474 475 switch (mouseEvent.type()) { 476 case WebEvent::MouseDown: 477 eventType = NPCocoaEventMouseDown; 478 break; 479 case WebEvent::MouseUp: 480 eventType = NPCocoaEventMouseUp; 481 break; 482 case WebEvent::MouseMove: 483 if (mouseEvent.button() == WebMouseEvent::NoButton) 484 eventType = NPCocoaEventMouseMoved; 485 else 486 eventType = NPCocoaEventMouseDragged; 487 break; 488 default: 489 ASSERT_NOT_REACHED(); 490 return NPCocoaEvent(); 491 } 492 493 NPCocoaEvent event = initializeEvent(eventType); 494 fillInCocoaEventFromMouseEvent(event, mouseEvent, pluginLocation); 495 return event; 496 } 497 498 bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& mouseEvent) 499 { 500 switch (m_eventModel) { 501 case NPEventModelCocoa: { 502 NPCocoaEvent event = initializeMouseEvent(mouseEvent, m_frameRect.location()); 503 504 NPCocoaEvent* previousMouseEvent = m_currentMouseEvent; 505 m_currentMouseEvent = &event; 506 507 // Protect against NPP_HandleEvent causing the plug-in to be destroyed, since we 508 // access m_currentMouseEvent afterwards. 509 RefPtr<NetscapePlugin> protect(this); 510 511 NPP_HandleEvent(&event); 512 513 m_currentMouseEvent = previousMouseEvent; 514 515 // Some plug-ins return false even if the mouse event has been handled. 516 // This leads to bugs such as <rdar://problem/9167611>. Work around this 517 // by always returning true. 518 return true; 519 } 520 521 #ifndef NP_NO_CARBON 522 case NPEventModelCarbon: { 523 EventKind eventKind = nullEvent; 524 525 switch (mouseEvent.type()) { 526 case WebEvent::MouseDown: 527 eventKind = mouseDown; 528 buttonStateFromLastMouseEvent |= (1 << buttonNumber(mouseEvent.button())); 529 break; 530 case WebEvent::MouseUp: 531 eventKind = mouseUp; 532 buttonStateFromLastMouseEvent &= ~(1 << buttonNumber(mouseEvent.button())); 533 break; 534 case WebEvent::MouseMove: 535 eventKind = nullEvent; 536 break; 537 default: 538 ASSERT_NOT_REACHED(); 539 } 540 541 EventRecord event = initializeEventRecord(eventKind); 542 event.modifiers = modifiersForEvent(mouseEvent); 543 event.where.h = mouseEvent.globalPosition().x(); 544 event.where.v = mouseEvent.globalPosition().y(); 545 546 NPP_HandleEvent(&event); 547 548 // Some plug-ins return false even if the mouse event has been handled. 549 // This leads to bugs such as <rdar://problem/9167611>. Work around this 550 // by always returning true. 551 return true; 552 } 553 #endif 554 555 default: 556 ASSERT_NOT_REACHED(); 557 } 558 559 return false; 560 } 561 562 bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent& wheelEvent) 563 { 564 switch (m_eventModel) { 565 case NPEventModelCocoa: { 566 NPCocoaEvent event = initializeEvent(NPCocoaEventScrollWheel); 567 568 event.data.mouse.modifierFlags = modifierFlags(wheelEvent); 569 event.data.mouse.pluginX = wheelEvent.position().x() - m_frameRect.x(); 570 event.data.mouse.pluginY = wheelEvent.position().y() - m_frameRect.y(); 571 event.data.mouse.buttonNumber = 0; 572 event.data.mouse.clickCount = 0; 573 event.data.mouse.deltaX = wheelEvent.delta().width(); 574 event.data.mouse.deltaY = wheelEvent.delta().height(); 575 event.data.mouse.deltaZ = 0; 576 return NPP_HandleEvent(&event); 577 } 578 579 #ifndef NP_NO_CARBON 580 case NPEventModelCarbon: 581 // Carbon doesn't have wheel events. 582 break; 583 #endif 584 585 default: 586 ASSERT_NOT_REACHED(); 587 } 588 589 return false; 590 } 591 592 bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& mouseEvent) 593 { 594 switch (m_eventModel) { 595 case NPEventModelCocoa: { 596 NPCocoaEvent event = initializeEvent(NPCocoaEventMouseEntered); 597 598 fillInCocoaEventFromMouseEvent(event, mouseEvent, m_frameRect.location()); 599 return NPP_HandleEvent(&event); 600 } 601 602 #ifndef NP_NO_CARBON 603 case NPEventModelCarbon: { 604 EventRecord eventRecord = initializeEventRecord(NPEventType_AdjustCursorEvent); 605 eventRecord.modifiers = modifiersForEvent(mouseEvent); 606 607 return NPP_HandleEvent(&eventRecord); 608 } 609 #endif 610 611 default: 612 ASSERT_NOT_REACHED(); 613 } 614 615 return false; 616 } 617 618 bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& mouseEvent) 619 { 620 switch (m_eventModel) { 621 case NPEventModelCocoa: { 622 NPCocoaEvent event = initializeEvent(NPCocoaEventMouseExited); 623 624 fillInCocoaEventFromMouseEvent(event, mouseEvent, m_frameRect.location()); 625 return NPP_HandleEvent(&event); 626 } 627 628 #ifndef NP_NO_CARBON 629 case NPEventModelCarbon: { 630 EventRecord eventRecord = initializeEventRecord(NPEventType_AdjustCursorEvent); 631 eventRecord.modifiers = modifiersForEvent(mouseEvent); 632 633 return NPP_HandleEvent(&eventRecord); 634 } 635 #endif 636 637 default: 638 ASSERT_NOT_REACHED(); 639 } 640 641 return false; 642 } 643 644 static unsigned modifierFlags(const WebKeyboardEvent& keyboardEvent) 645 { 646 unsigned modifierFlags = 0; 647 648 if (keyboardEvent.capsLockKey()) 649 modifierFlags |= NSAlphaShiftKeyMask; 650 if (keyboardEvent.shiftKey()) 651 modifierFlags |= NSShiftKeyMask; 652 if (keyboardEvent.controlKey()) 653 modifierFlags |= NSControlKeyMask; 654 if (keyboardEvent.altKey()) 655 modifierFlags |= NSAlternateKeyMask; 656 if (keyboardEvent.metaKey()) 657 modifierFlags |= NSCommandKeyMask; 658 659 return modifierFlags; 660 } 661 662 static bool isFlagsChangedEvent(const WebKeyboardEvent& keyboardEvent) 663 { 664 switch (keyboardEvent.nativeVirtualKeyCode()) { 665 case 54: // Right Command 666 case 55: // Left Command 667 668 case 57: // Capslock 669 670 case 56: // Left Shift 671 case 60: // Right Shift 672 673 case 58: // Left Alt 674 case 61: // Right Alt 675 676 case 59: // Left Ctrl 677 case 62: // Right Ctrl 678 return true; 679 } 680 681 return false; 682 } 683 684 static NPCocoaEvent initializeKeyboardEvent(const WebKeyboardEvent& keyboardEvent) 685 { 686 NPCocoaEventType eventType; 687 688 if (isFlagsChangedEvent(keyboardEvent)) 689 eventType = NPCocoaEventFlagsChanged; 690 else { 691 switch (keyboardEvent.type()) { 692 case WebEvent::KeyDown: 693 eventType = NPCocoaEventKeyDown; 694 break; 695 case WebEvent::KeyUp: 696 eventType = NPCocoaEventKeyUp; 697 break; 698 default: 699 ASSERT_NOT_REACHED(); 700 return NPCocoaEvent(); 701 } 702 } 703 704 NPCocoaEvent event = initializeEvent(eventType); 705 event.data.key.modifierFlags = modifierFlags(keyboardEvent); 706 event.data.key.characters = reinterpret_cast<NPNSString*>(static_cast<NSString*>(keyboardEvent.text())); 707 event.data.key.charactersIgnoringModifiers = reinterpret_cast<NPNSString*>(static_cast<NSString*>(keyboardEvent.unmodifiedText())); 708 event.data.key.isARepeat = keyboardEvent.isAutoRepeat(); 709 event.data.key.keyCode = keyboardEvent.nativeVirtualKeyCode(); 710 711 return event; 712 } 713 714 bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent& keyboardEvent) 715 { 716 bool handled = false; 717 718 switch (m_eventModel) { 719 case NPEventModelCocoa: { 720 NPCocoaEvent event = initializeKeyboardEvent(keyboardEvent); 721 handled = NPP_HandleEvent(&event); 722 break; 723 } 724 725 #ifndef NP_NO_CARBON 726 case NPEventModelCarbon: { 727 EventKind eventKind = nullEvent; 728 729 switch (keyboardEvent.type()) { 730 case WebEvent::KeyDown: 731 eventKind = keyboardEvent.isAutoRepeat() ? autoKey : keyDown; 732 break; 733 case WebEvent::KeyUp: 734 eventKind = keyUp; 735 break; 736 default: 737 ASSERT_NOT_REACHED(); 738 } 739 740 EventRecord event = initializeEventRecord(eventKind); 741 event.modifiers = modifiersForEvent(keyboardEvent); 742 event.message = keyboardEvent.nativeVirtualKeyCode() << 8 | keyboardEvent.macCharCode(); 743 handled = NPP_HandleEvent(&event); 744 break; 745 } 746 #endif 747 748 default: 749 ASSERT_NOT_REACHED(); 750 } 751 752 // Most plug-ins simply return true for all keyboard events, even those that aren't handled. 753 // This leads to bugs such as <rdar://problem/8740926>. We work around this by returning false 754 // if the keyboard event has the command modifier pressed. 755 if (keyboardEvent.metaKey()) 756 return false; 757 758 return handled; 759 } 760 761 void NetscapePlugin::platformSetFocus(bool hasFocus) 762 { 763 m_pluginHasFocus = hasFocus; 764 m_pluginController->setComplexTextInputEnabled(m_pluginHasFocus && m_windowHasFocus); 765 766 switch (m_eventModel) { 767 case NPEventModelCocoa: { 768 NPCocoaEvent event = initializeEvent(NPCocoaEventFocusChanged); 769 770 event.data.focus.hasFocus = hasFocus; 771 NPP_HandleEvent(&event); 772 break; 773 } 774 775 #ifndef NP_NO_CARBON 776 case NPEventModelCarbon: { 777 EventRecord event = initializeEventRecord(hasFocus ? NPEventType_GetFocusEvent : NPEventType_LoseFocusEvent); 778 779 NPP_HandleEvent(&event); 780 break; 781 } 782 #endif 783 784 default: 785 ASSERT_NOT_REACHED(); 786 } 787 } 788 789 void NetscapePlugin::windowFocusChanged(bool hasFocus) 790 { 791 m_windowHasFocus = hasFocus; 792 m_pluginController->setComplexTextInputEnabled(m_pluginHasFocus && m_windowHasFocus); 793 794 switch (m_eventModel) { 795 case NPEventModelCocoa: { 796 NPCocoaEvent event = initializeEvent(NPCocoaEventWindowFocusChanged); 797 798 event.data.focus.hasFocus = hasFocus; 799 NPP_HandleEvent(&event); 800 break; 801 } 802 803 #ifndef NP_NO_CARBON 804 case NPEventModelCarbon: { 805 HiliteWindow(windowRef(), hasFocus); 806 if (hasFocus) 807 SetUserFocusWindow(windowRef()); 808 809 EventRecord event = initializeEventRecord(activateEvt); 810 event.message = reinterpret_cast<unsigned long>(windowRef()); 811 if (hasFocus) 812 event.modifiers |= activeFlag; 813 814 NPP_HandleEvent(&event); 815 break; 816 } 817 #endif 818 819 default: 820 ASSERT_NOT_REACHED(); 821 } 822 } 823 824 #ifndef NP_NO_CARBON 825 static Rect computeFakeWindowBoundsRect(const WebCore::IntRect& windowFrameInScreenCoordinates, const WebCore::IntRect& viewFrameInWindowCoordinates) 826 { 827 // Carbon global coordinates has the origin set at the top left corner of the main viewing screen, so we want to flip the y coordinate. 828 CGFloat maxY = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]); 829 830 int flippedWindowFrameYCoordinate = maxY - windowFrameInScreenCoordinates.maxY(); 831 int flippedViewFrameYCoordinate = windowFrameInScreenCoordinates.height() - viewFrameInWindowCoordinates.maxY(); 832 833 Rect bounds; 834 835 bounds.top = flippedWindowFrameYCoordinate + flippedViewFrameYCoordinate; 836 bounds.left = windowFrameInScreenCoordinates.x(); 837 bounds.right = bounds.left + viewFrameInWindowCoordinates.width(); 838 bounds.bottom = bounds.top + viewFrameInWindowCoordinates.height(); 839 840 return bounds; 841 } 842 #endif 843 844 void NetscapePlugin::windowAndViewFramesChanged(const IntRect& windowFrameInScreenCoordinates, const IntRect& viewFrameInWindowCoordinates) 845 { 846 m_windowFrameInScreenCoordinates = windowFrameInScreenCoordinates; 847 m_viewFrameInWindowCoordinates = viewFrameInWindowCoordinates; 848 849 switch (m_eventModel) { 850 case NPEventModelCocoa: 851 // Nothing to do. 852 break; 853 854 #ifndef NP_NO_CARBON 855 case NPEventModelCarbon: { 856 Rect bounds = computeFakeWindowBoundsRect(windowFrameInScreenCoordinates, viewFrameInWindowCoordinates); 857 858 ::SetWindowBounds(windowRef(), kWindowStructureRgn, &bounds); 859 break; 860 } 861 #endif 862 863 default: 864 ASSERT_NOT_REACHED(); 865 } 866 } 867 868 void NetscapePlugin::windowVisibilityChanged(bool) 869 { 870 // FIXME: Implement. 871 } 872 873 uint64_t NetscapePlugin::pluginComplexTextInputIdentifier() const 874 { 875 // Just return a dummy value; this is only called for in-process plug-ins, which we don't support on Mac. 876 return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this)); 877 } 878 879 880 #ifndef NP_NO_CARBON 881 static bool convertStringToKeyCodes(const String& string, ScriptCode scriptCode, Vector<UInt8>& keyCodes) 882 { 883 // Create the mapping. 884 UnicodeMapping mapping; 885 886 if (GetTextEncodingFromScriptInfo(scriptCode, kTextLanguageDontCare, kTextRegionDontCare, &mapping.otherEncoding) != noErr) 887 return false; 888 889 mapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, kTextEncodingDefaultVariant, kTextEncodingDefaultFormat); 890 mapping.mappingVersion = kUnicodeUseLatestMapping; 891 892 // Create the converter 893 UnicodeToTextInfo textInfo; 894 895 if (CreateUnicodeToTextInfo(&mapping, &textInfo) != noErr) 896 return false; 897 898 ByteCount inputLength = string.length() * sizeof(UniChar); 899 ByteCount inputRead; 900 ByteCount outputLength; 901 ByteCount maxOutputLength = string.length() * sizeof(UniChar); 902 903 Vector<UInt8> outputData(maxOutputLength); 904 OSStatus status = ConvertFromUnicodeToText(textInfo, inputLength, string.characters(), kNilOptions, 0, 0, 0, 0, maxOutputLength, &inputRead, &outputLength, outputData.data()); 905 906 DisposeUnicodeToTextInfo(&textInfo); 907 908 if (status != noErr) 909 return false; 910 911 outputData.swap(keyCodes); 912 return true; 913 } 914 #endif 915 916 void NetscapePlugin::sendComplexTextInput(const String& textInput) 917 { 918 switch (m_eventModel) { 919 case NPEventModelCocoa: { 920 NPCocoaEvent event = initializeEvent(NPCocoaEventTextInput); 921 event.data.text.text = reinterpret_cast<NPNSString*>(static_cast<NSString*>(textInput)); 922 NPP_HandleEvent(&event); 923 break; 924 } 925 #ifndef NP_NO_CARBON 926 case NPEventModelCarbon: { 927 ScriptCode scriptCode = WKGetScriptCodeFromCurrentKeyboardInputSource(); 928 Vector<UInt8> keyCodes; 929 930 if (!convertStringToKeyCodes(textInput, scriptCode, keyCodes)) 931 return; 932 933 // Set the script code as the keyboard script. Normally Carbon does this whenever the input source changes. 934 // However, this is only done for the process that has the keyboard focus. We cheat and do it here instead. 935 SetScriptManagerVariable(smKeyScript, scriptCode); 936 937 EventRecord event = initializeEventRecord(keyDown); 938 event.modifiers = 0; 939 940 for (size_t i = 0; i < keyCodes.size(); i++) { 941 event.message = keyCodes[i]; 942 NPP_HandleEvent(&event); 943 } 944 break; 945 } 946 #endif 947 default: 948 ASSERT_NOT_REACHED(); 949 } 950 } 951 952 PlatformLayer* NetscapePlugin::pluginLayer() 953 { 954 return static_cast<PlatformLayer*>(m_pluginLayer.get()); 955 } 956 957 #ifndef NP_NO_CARBON 958 void NetscapePlugin::nullEventTimerFired() 959 { 960 EventRecord event = initializeEventRecord(nullEvent); 961 962 event.message = 0; 963 CGPoint mousePosition; 964 HIGetMousePosition(kHICoordSpaceScreenPixel, 0, &mousePosition); 965 event.where.h = mousePosition.x; 966 event.where.v = mousePosition.y; 967 968 event.modifiers = GetCurrentKeyModifiers(); 969 if (!Button()) 970 event.modifiers |= btnState; 971 972 NPP_HandleEvent(&event); 973 } 974 #endif 975 976 } // namespace WebKit 977