Home | History | Annotate | Download | only in Plugins
      1 /*
      2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #if ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__)
     27 
     28 #import "WebNetscapePluginEventHandlerCarbon.h"
     29 
     30 #import "WebNetscapePluginView.h"
     31 #import "WebKitLogging.h"
     32 #import "WebKitSystemInterface.h"
     33 
     34 // Send null events 50 times a second when active, so plug-ins like Flash get high frame rates.
     35 #define NullEventIntervalActive         0.02
     36 #define NullEventIntervalNotActive      0.25
     37 
     38 WebNetscapePluginEventHandlerCarbon::WebNetscapePluginEventHandlerCarbon(WebNetscapePluginView* pluginView)
     39     : WebNetscapePluginEventHandler(pluginView)
     40     , m_keyEventHandler(0)
     41     , m_suspendKeyUpEvents(false)
     42 {
     43 }
     44 
     45 static void getCarbonEvent(EventRecord* carbonEvent)
     46 {
     47     carbonEvent->what = nullEvent;
     48     carbonEvent->message = 0;
     49     carbonEvent->when = TickCount();
     50 
     51     GetGlobalMouse(&carbonEvent->where);
     52     carbonEvent->modifiers = GetCurrentKeyModifiers();
     53     if (!Button())
     54         carbonEvent->modifiers |= btnState;
     55 }
     56 
     57 static EventModifiers modifiersForEvent(NSEvent *event)
     58 {
     59     EventModifiers modifiers;
     60     unsigned int modifierFlags = [event modifierFlags];
     61     NSEventType eventType = [event type];
     62 
     63     modifiers = 0;
     64 
     65     if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown)
     66         modifiers |= btnState;
     67 
     68     if (modifierFlags & NSCommandKeyMask)
     69         modifiers |= cmdKey;
     70 
     71     if (modifierFlags & NSShiftKeyMask)
     72         modifiers |= shiftKey;
     73 
     74     if (modifierFlags & NSAlphaShiftKeyMask)
     75         modifiers |= alphaLock;
     76 
     77     if (modifierFlags & NSAlternateKeyMask)
     78         modifiers |= optionKey;
     79 
     80     if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
     81         modifiers |= controlKey;
     82 
     83     return modifiers;
     84 }
     85 
     86 static void getCarbonEvent(EventRecord *carbonEvent, NSEvent *cocoaEvent)
     87 {
     88     if (WKConvertNSEventToCarbonEvent(carbonEvent, cocoaEvent))
     89         return;
     90 
     91     NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]];
     92 
     93     carbonEvent->what = nullEvent;
     94     carbonEvent->message = 0;
     95     carbonEvent->when = (UInt32)([cocoaEvent timestamp] * 60); // seconds to ticks
     96     carbonEvent->where.h = (short)where.x;
     97     carbonEvent->where.v = (short)(NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - where.y);
     98     carbonEvent->modifiers = modifiersForEvent(cocoaEvent);
     99 }
    100 
    101 void WebNetscapePluginEventHandlerCarbon::sendNullEvent()
    102 {
    103     EventRecord event;
    104 
    105     getCarbonEvent(&event);
    106 
    107     // Plug-in should not react to cursor position when not active or when a menu is down.
    108     MenuTrackingData trackingData;
    109     OSStatus error = GetMenuTrackingData(NULL, &trackingData);
    110 
    111     // Plug-in should not react to cursor position when the actual window is not key.
    112     if (![[m_pluginView window] isKeyWindow] || (error == noErr && trackingData.menu)) {
    113         // FIXME: Does passing a v and h of -1 really prevent it from reacting to the cursor position?
    114         event.where.v = -1;
    115         event.where.h = -1;
    116     }
    117 
    118     sendEvent(&event);
    119 }
    120 
    121 void WebNetscapePluginEventHandlerCarbon::drawRect(CGContextRef, const NSRect&)
    122 {
    123     EventRecord event;
    124 
    125     getCarbonEvent(&event);
    126     event.what = updateEvt;
    127     WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef];
    128     event.message = (unsigned long)windowRef;
    129 
    130     BOOL acceptedEvent;
    131     acceptedEvent = sendEvent(&event);
    132 
    133     LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent);
    134 }
    135 
    136 void WebNetscapePluginEventHandlerCarbon::mouseDown(NSEvent* theEvent)
    137 {
    138     EventRecord event;
    139 
    140     getCarbonEvent(&event, theEvent);
    141     event.what = ::mouseDown;
    142 
    143     BOOL acceptedEvent;
    144     acceptedEvent = sendEvent(&event);
    145 
    146     LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
    147 }
    148 
    149 void WebNetscapePluginEventHandlerCarbon::mouseUp(NSEvent* theEvent)
    150 {
    151     EventRecord event;
    152 
    153     getCarbonEvent(&event, theEvent);
    154     event.what = ::mouseUp;
    155 
    156     BOOL acceptedEvent;
    157     acceptedEvent = sendEvent(&event);
    158 
    159     LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
    160 }
    161 
    162 bool WebNetscapePluginEventHandlerCarbon::scrollWheel(NSEvent* theEvent)
    163 {
    164     return false;
    165 }
    166 
    167 void WebNetscapePluginEventHandlerCarbon::mouseEntered(NSEvent* theEvent)
    168 {
    169     EventRecord event;
    170 
    171     getCarbonEvent(&event, theEvent);
    172     event.what = NPEventType_AdjustCursorEvent;
    173 
    174     BOOL acceptedEvent;
    175     acceptedEvent = sendEvent(&event);
    176 
    177     LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);
    178 }
    179 
    180 void WebNetscapePluginEventHandlerCarbon::mouseExited(NSEvent* theEvent)
    181 {
    182     EventRecord event;
    183 
    184     getCarbonEvent(&event, theEvent);
    185     event.what = NPEventType_AdjustCursorEvent;
    186 
    187     BOOL acceptedEvent;
    188     acceptedEvent = sendEvent(&event);
    189 
    190     LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);
    191 }
    192 
    193 void WebNetscapePluginEventHandlerCarbon::mouseDragged(NSEvent*)
    194 {
    195 }
    196 
    197 void WebNetscapePluginEventHandlerCarbon::mouseMoved(NSEvent* theEvent)
    198 {
    199     EventRecord event;
    200 
    201     getCarbonEvent(&event, theEvent);
    202     event.what = NPEventType_AdjustCursorEvent;
    203 
    204     BOOL acceptedEvent;
    205     acceptedEvent = sendEvent(&event);
    206 
    207     LOG(PluginEvents, "NPP_HandleEvent(mouseMoved): %d", acceptedEvent);
    208 }
    209 
    210 void WebNetscapePluginEventHandlerCarbon::keyDown(NSEvent *theEvent)
    211 {
    212     m_suspendKeyUpEvents = true;
    213     WKSendKeyEventToTSM(theEvent);
    214 }
    215 
    216 void WebNetscapePluginEventHandlerCarbon::syntheticKeyDownWithCommandModifier(int keyCode, char character)
    217 {
    218     EventRecord event;
    219     getCarbonEvent(&event);
    220 
    221     event.what = ::keyDown;
    222     event.modifiers |= cmdKey;
    223     event.message = keyCode << 8 | character;
    224     sendEvent(&event);
    225 }
    226 
    227 static UInt32 keyMessageForEvent(NSEvent *event)
    228 {
    229     NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
    230     if (!data)
    231         return 0;
    232 
    233     UInt8 characterCode;
    234     [data getBytes:&characterCode length:1];
    235     UInt16 keyCode = [event keyCode];
    236     return keyCode << 8 | characterCode;
    237 }
    238 
    239 void WebNetscapePluginEventHandlerCarbon::keyUp(NSEvent* theEvent)
    240 {
    241     WKSendKeyEventToTSM(theEvent);
    242 
    243     // TSM won't send keyUp events so we have to send them ourselves.
    244     // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9.
    245     if (!m_suspendKeyUpEvents) {
    246         EventRecord event;
    247 
    248         getCarbonEvent(&event, theEvent);
    249         event.what = ::keyUp;
    250 
    251         if (event.message == 0)
    252             event.message = keyMessageForEvent(theEvent);
    253 
    254         sendEvent(&event);
    255     }
    256 }
    257 
    258 void WebNetscapePluginEventHandlerCarbon::flagsChanged(NSEvent*)
    259 {
    260 }
    261 
    262 void WebNetscapePluginEventHandlerCarbon::focusChanged(bool hasFocus)
    263 {
    264     EventRecord event;
    265 
    266     getCarbonEvent(&event);
    267     bool acceptedEvent;
    268     if (hasFocus) {
    269         event.what = NPEventType_GetFocusEvent;
    270         acceptedEvent = sendEvent(&event);
    271         LOG(PluginEvents, "NPP_HandleEvent(NPEventType_GetFocusEvent): %d", acceptedEvent);
    272         installKeyEventHandler();
    273     } else {
    274         event.what = NPEventType_LoseFocusEvent;
    275         acceptedEvent = sendEvent(&event);
    276         LOG(PluginEvents, "NPP_HandleEvent(NPEventType_LoseFocusEvent): %d", acceptedEvent);
    277         removeKeyEventHandler();
    278     }
    279 }
    280 
    281 void WebNetscapePluginEventHandlerCarbon::windowFocusChanged(bool hasFocus)
    282 {
    283     WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef];
    284 
    285     SetUserFocusWindow(windowRef);
    286 
    287     EventRecord event;
    288 
    289     getCarbonEvent(&event);
    290     event.what = activateEvt;
    291     event.message = (unsigned long)windowRef;
    292     if (hasFocus)
    293         event.modifiers |= activeFlag;
    294 
    295     BOOL acceptedEvent;
    296     acceptedEvent = sendEvent(&event);
    297 
    298     LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d  isActive: %d", acceptedEvent, hasFocus);
    299 }
    300 
    301 OSStatus WebNetscapePluginEventHandlerCarbon::TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *eventHandler)
    302 {
    303     EventRef rawKeyEventRef;
    304     OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef);
    305     if (status != noErr) {
    306         LOG_ERROR("GetEventParameter failed with error: %d", status);
    307         return noErr;
    308     }
    309 
    310     // Two-pass read to allocate/extract Mac charCodes
    311     ByteCount numBytes;
    312     status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
    313     if (status != noErr) {
    314         LOG_ERROR("GetEventParameter failed with error: %d", status);
    315         return noErr;
    316     }
    317     char *buffer = (char *)malloc(numBytes);
    318     status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer);
    319     if (status != noErr) {
    320         LOG_ERROR("GetEventParameter failed with error: %d", status);
    321         free(buffer);
    322         return noErr;
    323     }
    324 
    325     EventRef cloneEvent = CopyEvent(rawKeyEventRef);
    326     unsigned i;
    327     for (i = 0; i < numBytes; i++) {
    328         status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]);
    329         if (status != noErr) {
    330             LOG_ERROR("SetEventParameter failed with error: %d", status);
    331             free(buffer);
    332             return noErr;
    333         }
    334 
    335         EventRecord eventRec;
    336         if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
    337             BOOL acceptedEvent;
    338             acceptedEvent = static_cast<WebNetscapePluginEventHandlerCarbon*>(eventHandler)->sendEvent(&eventRec);
    339 
    340             LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
    341                 acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
    342 
    343             // We originally thought that if the plug-in didn't accept this event,
    344             // we should pass it along so that keyboard scrolling, for example, will work.
    345             // In practice, this is not a good idea, because plug-ins tend to eat the event but return false.
    346             // MacIE handles each key event twice because of this, but we will emulate the other browsers instead.
    347         }
    348     }
    349     ReleaseEvent(cloneEvent);
    350 
    351     free(buffer);
    352 
    353     return noErr;
    354 }
    355 
    356 void WebNetscapePluginEventHandlerCarbon::installKeyEventHandler()
    357 {
    358     static const EventTypeSpec sTSMEvents[] =
    359     {
    360         { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
    361     };
    362 
    363     if (!m_keyEventHandler) {
    364         InstallEventHandler(GetWindowEventTarget((WindowRef)[[m_pluginView window] windowRef]),
    365                             NewEventHandlerUPP(TSMEventHandler),
    366                             GetEventTypeCount(sTSMEvents),
    367                             sTSMEvents,
    368                             this,
    369                             &m_keyEventHandler);
    370     }
    371 }
    372 
    373 void WebNetscapePluginEventHandlerCarbon::removeKeyEventHandler()
    374 {
    375     if (m_keyEventHandler) {
    376         RemoveEventHandler(m_keyEventHandler);
    377         m_keyEventHandler = 0;
    378     }
    379 }
    380 
    381 void WebNetscapePluginEventHandlerCarbon::nullEventTimerFired(CFRunLoopTimerRef timerRef, void *context)
    382 {
    383     static_cast<WebNetscapePluginEventHandlerCarbon*>(context)->sendNullEvent();
    384 }
    385 
    386 void WebNetscapePluginEventHandlerCarbon::startTimers(bool throttleTimers)
    387 {
    388     ASSERT(!m_nullEventTimer);
    389 
    390     CFTimeInterval interval = !throttleTimers ? NullEventIntervalActive : NullEventIntervalNotActive;
    391 
    392     CFRunLoopTimerContext context = { 0, this, NULL, NULL, NULL };
    393     m_nullEventTimer.adoptCF(CFRunLoopTimerCreate(0, CFAbsoluteTimeGetCurrent() + interval, interval,
    394                                                    0, 0, nullEventTimerFired, &context));
    395     CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_nullEventTimer.get(), kCFRunLoopDefaultMode);
    396 }
    397 
    398 void WebNetscapePluginEventHandlerCarbon::stopTimers()
    399 {
    400     if (!m_nullEventTimer)
    401         return;
    402 
    403     CFRunLoopTimerInvalidate(m_nullEventTimer.get());
    404     m_nullEventTimer = 0;
    405 }
    406 
    407 void* WebNetscapePluginEventHandlerCarbon::platformWindow(NSWindow* window)
    408 {
    409     return [window windowRef];
    410 }
    411 
    412 bool WebNetscapePluginEventHandlerCarbon::sendEvent(EventRecord* event)
    413 {
    414     // If at any point the user clicks or presses a key from within a plugin, set the
    415     // currentEventIsUserGesture flag to true. This is important to differentiate legitimate
    416     // window.open() calls;  we still want to allow those.  See rdar://problem/4010765
    417     if (event->what == ::mouseDown || event->what == ::keyDown || event->what == ::mouseUp || event->what == ::autoKey)
    418         m_currentEventIsUserGesture = true;
    419 
    420     m_suspendKeyUpEvents = false;
    421 
    422     bool result = [m_pluginView sendEvent:event isDrawRect:event->what == updateEvt];
    423 
    424     m_currentEventIsUserGesture = false;
    425 
    426     return result;
    427 }
    428 
    429 #endif // ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__)
    430