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*) 203{ 204} 205 206void WebNetscapePluginEventHandlerCarbon::keyDown(NSEvent *theEvent) 207{ 208 m_suspendKeyUpEvents = true; 209 WKSendKeyEventToTSM(theEvent); 210} 211 212void 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 223static 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 235void 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 254void WebNetscapePluginEventHandlerCarbon::flagsChanged(NSEvent*) 255{ 256} 257 258void 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 277void 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 297OSStatus 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 352void 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 369void WebNetscapePluginEventHandlerCarbon::removeKeyEventHandler() 370{ 371 if (m_keyEventHandler) { 372 RemoveEventHandler(m_keyEventHandler); 373 m_keyEventHandler = 0; 374 } 375} 376 377void WebNetscapePluginEventHandlerCarbon::nullEventTimerFired(CFRunLoopTimerRef timerRef, void *context) 378{ 379 static_cast<WebNetscapePluginEventHandlerCarbon*>(context)->sendNullEvent(); 380} 381 382void 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 394void WebNetscapePluginEventHandlerCarbon::stopTimers() 395{ 396 if (!m_nullEventTimer) 397 return; 398 399 CFRunLoopTimerInvalidate(m_nullEventTimer.get()); 400 m_nullEventTimer = 0; 401} 402 403void* WebNetscapePluginEventHandlerCarbon::platformWindow(NSWindow* window) 404{ 405 return [window windowRef]; 406} 407 408bool 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