WebNetscapePluginEventHandlerCarbon.mm revision dcc8cf2e65d1aa555cce12431a16547e66b469ee
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 38WebNetscapePluginEventHandlerCarbon::WebNetscapePluginEventHandlerCarbon(WebNetscapePluginView* pluginView) 39 : WebNetscapePluginEventHandler(pluginView) 40 , m_keyEventHandler(0) 41 , m_suspendKeyUpEvents(false) 42{ 43} 44 45static 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 59static 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 88static 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 106void 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 126void 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 141void 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 154void 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 167bool WebNetscapePluginEventHandlerCarbon::scrollWheel(NSEvent* theEvent) 168{ 169 return false; 170} 171 172void 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 185void 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 198void WebNetscapePluginEventHandlerCarbon::mouseDragged(NSEvent*) 199{ 200} 201 202void WebNetscapePluginEventHandlerCarbon::mouseMoved(NSEvent* theEvent) 203{ 204 EventRecord event; 205 206 getCarbonEvent(&event, theEvent); 207 event.what = adjustCursorEvent; 208 209 BOOL acceptedEvent; 210 acceptedEvent = sendEvent(&event); 211 212 LOG(PluginEvents, "NPP_HandleEvent(mouseMoved): %d", acceptedEvent); 213} 214 215void WebNetscapePluginEventHandlerCarbon::keyDown(NSEvent *theEvent) 216{ 217 m_suspendKeyUpEvents = true; 218 WKSendKeyEventToTSM(theEvent); 219} 220 221void WebNetscapePluginEventHandlerCarbon::syntheticKeyDownWithCommandModifier(int keyCode, char character) 222{ 223 EventRecord event; 224 getCarbonEvent(&event); 225 226 event.what = ::keyDown; 227 event.modifiers |= cmdKey; 228 event.message = keyCode << 8 | character; 229 sendEvent(&event); 230} 231 232static UInt32 keyMessageForEvent(NSEvent *event) 233{ 234 NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())]; 235 if (!data) 236 return 0; 237 238 UInt8 characterCode; 239 [data getBytes:&characterCode length:1]; 240 UInt16 keyCode = [event keyCode]; 241 return keyCode << 8 | characterCode; 242} 243 244void WebNetscapePluginEventHandlerCarbon::keyUp(NSEvent* theEvent) 245{ 246 WKSendKeyEventToTSM(theEvent); 247 248 // TSM won't send keyUp events so we have to send them ourselves. 249 // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9. 250 if (!m_suspendKeyUpEvents) { 251 EventRecord event; 252 253 getCarbonEvent(&event, theEvent); 254 event.what = ::keyUp; 255 256 if (event.message == 0) 257 event.message = keyMessageForEvent(theEvent); 258 259 sendEvent(&event); 260 } 261} 262 263void WebNetscapePluginEventHandlerCarbon::flagsChanged(NSEvent*) 264{ 265} 266 267void WebNetscapePluginEventHandlerCarbon::focusChanged(bool hasFocus) 268{ 269 EventRecord event; 270 271 getCarbonEvent(&event); 272 bool acceptedEvent; 273 if (hasFocus) { 274 event.what = getFocusEvent; 275 acceptedEvent = sendEvent(&event); 276 LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent); 277 installKeyEventHandler(); 278 } else { 279 event.what = loseFocusEvent; 280 acceptedEvent = sendEvent(&event); 281 LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent); 282 removeKeyEventHandler(); 283 } 284} 285 286void WebNetscapePluginEventHandlerCarbon::windowFocusChanged(bool hasFocus) 287{ 288 WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef]; 289 290 SetUserFocusWindow(windowRef); 291 292 EventRecord event; 293 294 getCarbonEvent(&event); 295 event.what = activateEvt; 296 event.message = (unsigned long)windowRef; 297 if (hasFocus) 298 event.modifiers |= activeFlag; 299 300 BOOL acceptedEvent; 301 acceptedEvent = sendEvent(&event); 302 303 LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d isActive: %d", acceptedEvent, hasFocus); 304} 305 306OSStatus WebNetscapePluginEventHandlerCarbon::TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *eventHandler) 307{ 308 EventRef rawKeyEventRef; 309 OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef); 310 if (status != noErr) { 311 LOG_ERROR("GetEventParameter failed with error: %d", status); 312 return noErr; 313 } 314 315 // Two-pass read to allocate/extract Mac charCodes 316 ByteCount numBytes; 317 status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL); 318 if (status != noErr) { 319 LOG_ERROR("GetEventParameter failed with error: %d", status); 320 return noErr; 321 } 322 char *buffer = (char *)malloc(numBytes); 323 status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer); 324 if (status != noErr) { 325 LOG_ERROR("GetEventParameter failed with error: %d", status); 326 free(buffer); 327 return noErr; 328 } 329 330 EventRef cloneEvent = CopyEvent(rawKeyEventRef); 331 unsigned i; 332 for (i = 0; i < numBytes; i++) { 333 status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]); 334 if (status != noErr) { 335 LOG_ERROR("SetEventParameter failed with error: %d", status); 336 free(buffer); 337 return noErr; 338 } 339 340 EventRecord eventRec; 341 if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) { 342 BOOL acceptedEvent; 343 acceptedEvent = static_cast<WebNetscapePluginEventHandlerCarbon*>(eventHandler)->sendEvent(&eventRec); 344 345 LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu", 346 acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask)); 347 348 // We originally thought that if the plug-in didn't accept this event, 349 // we should pass it along so that keyboard scrolling, for example, will work. 350 // In practice, this is not a good idea, because plug-ins tend to eat the event but return false. 351 // MacIE handles each key event twice because of this, but we will emulate the other browsers instead. 352 } 353 } 354 ReleaseEvent(cloneEvent); 355 356 free(buffer); 357 358 return noErr; 359} 360 361void WebNetscapePluginEventHandlerCarbon::installKeyEventHandler() 362{ 363 static const EventTypeSpec sTSMEvents[] = 364 { 365 { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } 366 }; 367 368 if (!m_keyEventHandler) { 369 InstallEventHandler(GetWindowEventTarget((WindowRef)[[m_pluginView window] windowRef]), 370 NewEventHandlerUPP(TSMEventHandler), 371 GetEventTypeCount(sTSMEvents), 372 sTSMEvents, 373 this, 374 &m_keyEventHandler); 375 } 376} 377 378void WebNetscapePluginEventHandlerCarbon::removeKeyEventHandler() 379{ 380 if (m_keyEventHandler) { 381 RemoveEventHandler(m_keyEventHandler); 382 m_keyEventHandler = 0; 383 } 384} 385 386void WebNetscapePluginEventHandlerCarbon::nullEventTimerFired(CFRunLoopTimerRef timerRef, void *context) 387{ 388 static_cast<WebNetscapePluginEventHandlerCarbon*>(context)->sendNullEvent(); 389} 390 391void WebNetscapePluginEventHandlerCarbon::startTimers(bool throttleTimers) 392{ 393 ASSERT(!m_nullEventTimer); 394 395 CFTimeInterval interval = !throttleTimers ? NullEventIntervalActive : NullEventIntervalNotActive; 396 397 CFRunLoopTimerContext context = { 0, this, NULL, NULL, NULL }; 398 m_nullEventTimer.adoptCF(CFRunLoopTimerCreate(0, CFAbsoluteTimeGetCurrent() + interval, interval, 399 0, 0, nullEventTimerFired, &context)); 400 CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_nullEventTimer.get(), kCFRunLoopDefaultMode); 401} 402 403void WebNetscapePluginEventHandlerCarbon::stopTimers() 404{ 405 if (!m_nullEventTimer) 406 return; 407 408 CFRunLoopTimerInvalidate(m_nullEventTimer.get()); 409 m_nullEventTimer = 0; 410} 411 412void* WebNetscapePluginEventHandlerCarbon::platformWindow(NSWindow* window) 413{ 414 return [window windowRef]; 415} 416 417bool WebNetscapePluginEventHandlerCarbon::sendEvent(EventRecord* event) 418{ 419 // If at any point the user clicks or presses a key from within a plugin, set the 420 // currentEventIsUserGesture flag to true. This is important to differentiate legitimate 421 // window.open() calls; we still want to allow those. See rdar://problem/4010765 422 if (event->what == ::mouseDown || event->what == ::keyDown || event->what == ::mouseUp || event->what == ::autoKey) 423 m_currentEventIsUserGesture = true; 424 425 m_suspendKeyUpEvents = false; 426 427 bool result = [m_pluginView sendEvent:event isDrawRect:event->what == updateEvt]; 428 429 m_currentEventIsUserGesture = false; 430 431 return result; 432} 433 434#endif // ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__) 435