Home | History | Annotate | Download | only in mac
      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