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->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor());
     53     carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor());
     54     carbonEvent->modifiers = GetCurrentKeyModifiers();
     55     if (!Button())
     56         carbonEvent->modifiers |= btnState;
     57 }
     58 
     59 static EventModifiers modifiersForEvent(NSEvent *event)
     60 {
     61     EventModifiers modifiers;
     62     unsigned int modifierFlags = [event modifierFlags];
     63     NSEventType eventType = [event type];
     64 
     65     modifiers = 0;
     66 
     67     if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown)
     68         modifiers |= btnState;
     69 
     70     if (modifierFlags & NSCommandKeyMask)
     71         modifiers |= cmdKey;
     72 
     73     if (modifierFlags & NSShiftKeyMask)
     74         modifiers |= shiftKey;
     75 
     76     if (modifierFlags & NSAlphaShiftKeyMask)
     77         modifiers |= alphaLock;
     78 
     79     if (modifierFlags & NSAlternateKeyMask)
     80         modifiers |= optionKey;
     81 
     82     if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
     83         modifiers |= controlKey;
     84 
     85     return modifiers;
     86 }
     87 
     88 static void getCarbonEvent(EventRecord *carbonEvent, NSEvent *cocoaEvent)
     89 {
     90     if (WKConvertNSEventToCarbonEvent(carbonEvent, cocoaEvent)) {
     91         carbonEvent->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor());
     92         carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor());
     93         return;
     94     }
     95 
     96     NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]];
     97 
     98     carbonEvent->what = nullEvent;
     99     carbonEvent->message = 0;
    100     carbonEvent->when = (UInt32)([cocoaEvent timestamp] * 60); // seconds to ticks
    101     carbonEvent->where.h = (short)where.x;
    102     carbonEvent->where.v = (short)(NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - where.y);
    103     carbonEvent->modifiers = modifiersForEvent(cocoaEvent);
    104 }
    105 
    106 void WebNetscapePluginEventHandlerCarbon::sendNullEvent()
    107 {
    108     EventRecord event;
    109 
    110     getCarbonEvent(&event);
    111 
    112     // Plug-in should not react to cursor position when not active or when a menu is down.
    113     MenuTrackingData trackingData;
    114     OSStatus error = GetMenuTrackingData(NULL, &trackingData);
    115 
    116     // Plug-in should not react to cursor position when the actual window is not key.
    117     if (![[m_pluginView window] isKeyWindow] || (error == noErr && trackingData.menu)) {
    118         // FIXME: Does passing a v and h of -1 really prevent it from reacting to the cursor position?
    119         event.where.v = -1;
    120         event.where.h = -1;
    121     }
    122 
    123     sendEvent(&event);
    124 }
    125 
    126 void WebNetscapePluginEventHandlerCarbon::drawRect(CGContextRef, const NSRect&)
    127 {
    128     EventRecord event;
    129 
    130     getCarbonEvent(&event);
    131     event.what = updateEvt;
    132     WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef];
    133     event.message = (unsigned long)windowRef;
    134 
    135     BOOL acceptedEvent;
    136     acceptedEvent = sendEvent(&event);
    137 
    138     LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent);
    139 }
    140 
    141 void WebNetscapePluginEventHandlerCarbon::mouseDown(NSEvent* theEvent)
    142 {
    143     EventRecord event;
    144 
    145     getCarbonEvent(&event, theEvent);
    146     event.what = ::mouseDown;
    147 
    148     BOOL acceptedEvent;
    149     acceptedEvent = sendEvent(&event);
    150 
    151     LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
    152 }
    153 
    154 void WebNetscapePluginEventHandlerCarbon::mouseUp(NSEvent* theEvent)
    155 {
    156     EventRecord event;
    157 
    158     getCarbonEvent(&event, theEvent);
    159     event.what = ::mouseUp;
    160 
    161     BOOL acceptedEvent;
    162     acceptedEvent = sendEvent(&event);
    163 
    164     LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
    165 }
    166 
    167 bool WebNetscapePluginEventHandlerCarbon::scrollWheel(NSEvent* theEvent)
    168 {
    169     return false;
    170 }
    171 
    172 void WebNetscapePluginEventHandlerCarbon::mouseEntered(NSEvent* theEvent)
    173 {
    174     EventRecord event;
    175 
    176     getCarbonEvent(&event, theEvent);
    177     event.what = adjustCursorEvent;
    178 
    179     BOOL acceptedEvent;
    180     acceptedEvent = sendEvent(&event);
    181 
    182     LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);
    183 }
    184 
    185 void WebNetscapePluginEventHandlerCarbon::mouseExited(NSEvent* theEvent)
    186 {
    187     EventRecord event;
    188 
    189     getCarbonEvent(&event, theEvent);
    190     event.what = adjustCursorEvent;
    191 
    192     BOOL acceptedEvent;
    193     acceptedEvent = sendEvent(&event);
    194 
    195     LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);
    196 }
    197 
    198 void WebNetscapePluginEventHandlerCarbon::mouseDragged(NSEvent*)
    199 {
    200 }
    201 
    202 void WebNetscapePluginEventHandlerCarbon::mouseMoved(NSEvent*)
    203 {
    204 }
    205 
    206 void WebNetscapePluginEventHandlerCarbon::keyDown(NSEvent *theEvent)
    207 {
    208     m_suspendKeyUpEvents = true;
    209     WKSendKeyEventToTSM(theEvent);
    210 }
    211 
    212 void WebNetscapePluginEventHandlerCarbon::syntheticKeyDownWithCommandModifier(int keyCode, char character)
    213 {
    214     EventRecord event;
    215     getCarbonEvent(&event);
    216 
    217     event.what = ::keyDown;
    218     event.modifiers |= cmdKey;
    219     event.message = keyCode << 8 | character;
    220     sendEvent(&event);
    221 }
    222 
    223 static UInt32 keyMessageForEvent(NSEvent *event)
    224 {
    225     NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
    226     if (!data)
    227         return 0;
    228 
    229     UInt8 characterCode;
    230     [data getBytes:&characterCode length:1];
    231     UInt16 keyCode = [event keyCode];
    232     return keyCode << 8 | characterCode;
    233 }
    234 
    235 void WebNetscapePluginEventHandlerCarbon::keyUp(NSEvent* theEvent)
    236 {
    237     WKSendKeyEventToTSM(theEvent);
    238 
    239     // TSM won't send keyUp events so we have to send them ourselves.
    240     // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9.
    241     if (!m_suspendKeyUpEvents) {
    242         EventRecord event;
    243 
    244         getCarbonEvent(&event, theEvent);
    245         event.what = ::keyUp;
    246 
    247         if (event.message == 0)
    248             event.message = keyMessageForEvent(theEvent);
    249 
    250         sendEvent(&event);
    251     }
    252 }
    253 
    254 void WebNetscapePluginEventHandlerCarbon::flagsChanged(NSEvent*)
    255 {
    256 }
    257 
    258 void WebNetscapePluginEventHandlerCarbon::focusChanged(bool hasFocus)
    259 {
    260     EventRecord event;
    261 
    262     getCarbonEvent(&event);
    263     bool acceptedEvent;
    264     if (hasFocus) {
    265         event.what = getFocusEvent;
    266         acceptedEvent = sendEvent(&event);
    267         LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent);
    268         installKeyEventHandler();
    269     } else {
    270         event.what = loseFocusEvent;
    271         acceptedEvent = sendEvent(&event);
    272         LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent);
    273         removeKeyEventHandler();
    274     }
    275 }
    276 
    277 void WebNetscapePluginEventHandlerCarbon::windowFocusChanged(bool hasFocus)
    278 {
    279     WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef];
    280 
    281     SetUserFocusWindow(windowRef);
    282 
    283     EventRecord event;
    284 
    285     getCarbonEvent(&event);
    286     event.what = activateEvt;
    287     event.message = (unsigned long)windowRef;
    288     if (hasFocus)
    289         event.modifiers |= activeFlag;
    290 
    291     BOOL acceptedEvent;
    292     acceptedEvent = sendEvent(&event);
    293 
    294     LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d  isActive: %d", acceptedEvent, hasFocus);
    295 }
    296 
    297 OSStatus WebNetscapePluginEventHandlerCarbon::TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *eventHandler)
    298 {
    299     EventRef rawKeyEventRef;
    300     OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef);
    301     if (status != noErr) {
    302         LOG_ERROR("GetEventParameter failed with error: %d", status);
    303         return noErr;
    304     }
    305 
    306     // Two-pass read to allocate/extract Mac charCodes
    307     ByteCount numBytes;
    308     status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
    309     if (status != noErr) {
    310         LOG_ERROR("GetEventParameter failed with error: %d", status);
    311         return noErr;
    312     }
    313     char *buffer = (char *)malloc(numBytes);
    314     status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer);
    315     if (status != noErr) {
    316         LOG_ERROR("GetEventParameter failed with error: %d", status);
    317         free(buffer);
    318         return noErr;
    319     }
    320 
    321     EventRef cloneEvent = CopyEvent(rawKeyEventRef);
    322     unsigned i;
    323     for (i = 0; i < numBytes; i++) {
    324         status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]);
    325         if (status != noErr) {
    326             LOG_ERROR("SetEventParameter failed with error: %d", status);
    327             free(buffer);
    328             return noErr;
    329         }
    330 
    331         EventRecord eventRec;
    332         if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
    333             BOOL acceptedEvent;
    334             acceptedEvent = static_cast<WebNetscapePluginEventHandlerCarbon*>(eventHandler)->sendEvent(&eventRec);
    335 
    336             LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
    337                 acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
    338 
    339             // We originally thought that if the plug-in didn't accept this event,
    340             // we should pass it along so that keyboard scrolling, for example, will work.
    341             // In practice, this is not a good idea, because plug-ins tend to eat the event but return false.
    342             // MacIE handles each key event twice because of this, but we will emulate the other browsers instead.
    343         }
    344     }
    345     ReleaseEvent(cloneEvent);
    346 
    347     free(buffer);
    348 
    349     return noErr;
    350 }
    351 
    352 void WebNetscapePluginEventHandlerCarbon::installKeyEventHandler()
    353 {
    354     static const EventTypeSpec sTSMEvents[] =
    355     {
    356         { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
    357     };
    358 
    359     if (!m_keyEventHandler) {
    360         InstallEventHandler(GetWindowEventTarget((WindowRef)[[m_pluginView window] windowRef]),
    361                             NewEventHandlerUPP(TSMEventHandler),
    362                             GetEventTypeCount(sTSMEvents),
    363                             sTSMEvents,
    364                             this,
    365                             &m_keyEventHandler);
    366     }
    367 }
    368 
    369 void WebNetscapePluginEventHandlerCarbon::removeKeyEventHandler()
    370 {
    371     if (m_keyEventHandler) {
    372         RemoveEventHandler(m_keyEventHandler);
    373         m_keyEventHandler = 0;
    374     }
    375 }
    376 
    377 void WebNetscapePluginEventHandlerCarbon::nullEventTimerFired(CFRunLoopTimerRef timerRef, void *context)
    378 {
    379     static_cast<WebNetscapePluginEventHandlerCarbon*>(context)->sendNullEvent();
    380 }
    381 
    382 void WebNetscapePluginEventHandlerCarbon::startTimers(bool throttleTimers)
    383 {
    384     ASSERT(!m_nullEventTimer);
    385 
    386     CFTimeInterval interval = !throttleTimers ? NullEventIntervalActive : NullEventIntervalNotActive;
    387 
    388     CFRunLoopTimerContext context = { 0, this, NULL, NULL, NULL };
    389     m_nullEventTimer.adoptCF(CFRunLoopTimerCreate(0, CFAbsoluteTimeGetCurrent() + interval, interval,
    390                                                    0, 0, nullEventTimerFired, &context));
    391     CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_nullEventTimer.get(), kCFRunLoopDefaultMode);
    392 }
    393 
    394 void WebNetscapePluginEventHandlerCarbon::stopTimers()
    395 {
    396     if (!m_nullEventTimer)
    397         return;
    398 
    399     CFRunLoopTimerInvalidate(m_nullEventTimer.get());
    400     m_nullEventTimer = 0;
    401 }
    402 
    403 void* WebNetscapePluginEventHandlerCarbon::platformWindow(NSWindow* window)
    404 {
    405     return [window windowRef];
    406 }
    407 
    408 bool WebNetscapePluginEventHandlerCarbon::sendEvent(EventRecord* event)
    409 {
    410     // If at any point the user clicks or presses a key from within a plugin, set the
    411     // currentEventIsUserGesture flag to true. This is important to differentiate legitimate
    412     // window.open() calls;  we still want to allow those.  See rdar://problem/4010765
    413     if (event->what == ::mouseDown || event->what == ::keyDown || event->what == ::mouseUp || event->what == ::autoKey)
    414         m_currentEventIsUserGesture = true;
    415 
    416     m_suspendKeyUpEvents = false;
    417 
    418     bool result = [m_pluginView sendEvent:event isDrawRect:event->what == updateEvt];
    419 
    420     m_currentEventIsUserGesture = false;
    421 
    422     return result;
    423 }
    424 
    425 #endif // ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__)
    426