2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #if ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__)
28 #import "WebNetscapePluginEventHandlerCarbon.h"
30 #import "WebNetscapePluginView.h"
31 #import "WebKitLogging.h"
32 #import "WebKitSystemInterface.h"
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
38 WebNetscapePluginEventHandlerCarbon::WebNetscapePluginEventHandlerCarbon(WebNetscapePluginView* pluginView)
39 : WebNetscapePluginEventHandler(pluginView)
40 , m_keyEventHandler(0)
41 , m_suspendKeyUpEvents(false)
45 static void getCarbonEvent(EventRecord* carbonEvent)
47 carbonEvent->what = nullEvent;
48 carbonEvent->message = 0;
49 carbonEvent->when = TickCount();
51 GetGlobalMouse(&carbonEvent->where);
52 carbonEvent->modifiers = GetCurrentKeyModifiers();
54 carbonEvent->modifiers |= btnState;
57 static EventModifiers modifiersForEvent(NSEvent *event)
59 EventModifiers modifiers;
60 unsigned int modifierFlags = [event modifierFlags];
61 NSEventType eventType = [event type];
65 if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown)
66 modifiers |= btnState;
68 if (modifierFlags & NSCommandKeyMask)
71 if (modifierFlags & NSShiftKeyMask)
72 modifiers |= shiftKey;
74 if (modifierFlags & NSAlphaShiftKeyMask)
75 modifiers |= alphaLock;
77 if (modifierFlags & NSAlternateKeyMask)
78 modifiers |= optionKey;
80 if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
81 modifiers |= controlKey;
86 static void getCarbonEvent(EventRecord *carbonEvent, NSEvent *cocoaEvent)
88 if (WKConvertNSEventToCarbonEvent(carbonEvent, cocoaEvent))
91 NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]];
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 *)[[NSScreen screens] objectAtIndex:0] frame]) - where.y);
98 carbonEvent->modifiers = modifiersForEvent(cocoaEvent);
101 void WebNetscapePluginEventHandlerCarbon::sendNullEvent()
105 getCarbonEvent(&event);
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);
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?
121 void WebNetscapePluginEventHandlerCarbon::drawRect(CGContextRef, const NSRect&)
125 getCarbonEvent(&event);
126 event.what = updateEvt;
127 WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef];
128 event.message = (unsigned long)windowRef;
131 acceptedEvent = sendEvent(&event);
133 LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent);
136 void WebNetscapePluginEventHandlerCarbon::mouseDown(NSEvent* theEvent)
140 getCarbonEvent(&event, theEvent);
141 event.what = ::mouseDown;
144 acceptedEvent = sendEvent(&event);
146 LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
149 void WebNetscapePluginEventHandlerCarbon::mouseUp(NSEvent* theEvent)
153 getCarbonEvent(&event, theEvent);
154 event.what = ::mouseUp;
157 acceptedEvent = sendEvent(&event);
159 LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);
162 bool WebNetscapePluginEventHandlerCarbon::scrollWheel(NSEvent* theEvent)
167 void WebNetscapePluginEventHandlerCarbon::mouseEntered(NSEvent* theEvent)
171 getCarbonEvent(&event, theEvent);
172 event.what = NPEventType_AdjustCursorEvent;
175 acceptedEvent = sendEvent(&event);
177 LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);
180 void WebNetscapePluginEventHandlerCarbon::mouseExited(NSEvent* theEvent)
184 getCarbonEvent(&event, theEvent);
185 event.what = NPEventType_AdjustCursorEvent;
188 acceptedEvent = sendEvent(&event);
190 LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);
193 void WebNetscapePluginEventHandlerCarbon::mouseDragged(NSEvent*)
197 void WebNetscapePluginEventHandlerCarbon::mouseMoved(NSEvent* theEvent)
201 getCarbonEvent(&event, theEvent);
202 event.what = NPEventType_AdjustCursorEvent;
205 acceptedEvent = sendEvent(&event);
207 LOG(PluginEvents, "NPP_HandleEvent(mouseMoved): %d", acceptedEvent);
210 void WebNetscapePluginEventHandlerCarbon::keyDown(NSEvent *theEvent)
212 m_suspendKeyUpEvents = true;
213 WKSendKeyEventToTSM(theEvent);
216 void WebNetscapePluginEventHandlerCarbon::syntheticKeyDownWithCommandModifier(int keyCode, char character)
219 getCarbonEvent(&event);
221 event.what = ::keyDown;
222 event.modifiers |= cmdKey;
223 event.message = keyCode << 8 | character;
227 static UInt32 keyMessageForEvent(NSEvent *event)
229 NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
234 [data getBytes:&characterCode length:1];
235 UInt16 keyCode = [event keyCode];
236 return keyCode << 8 | characterCode;
239 void WebNetscapePluginEventHandlerCarbon::keyUp(NSEvent* theEvent)
241 WKSendKeyEventToTSM(theEvent);
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) {
248 getCarbonEvent(&event, theEvent);
249 event.what = ::keyUp;
251 if (event.message == 0)
252 event.message = keyMessageForEvent(theEvent);
258 void WebNetscapePluginEventHandlerCarbon::flagsChanged(NSEvent*)
262 void WebNetscapePluginEventHandlerCarbon::focusChanged(bool hasFocus)
266 getCarbonEvent(&event);
269 event.what = NPEventType_GetFocusEvent;
270 acceptedEvent = sendEvent(&event);
271 LOG(PluginEvents, "NPP_HandleEvent(NPEventType_GetFocusEvent): %d", acceptedEvent);
272 installKeyEventHandler();
274 event.what = NPEventType_LoseFocusEvent;
275 acceptedEvent = sendEvent(&event);
276 LOG(PluginEvents, "NPP_HandleEvent(NPEventType_LoseFocusEvent): %d", acceptedEvent);
277 removeKeyEventHandler();
281 void WebNetscapePluginEventHandlerCarbon::windowFocusChanged(bool hasFocus)
283 WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef];
285 SetUserFocusWindow(windowRef);
289 getCarbonEvent(&event);
290 event.what = activateEvt;
291 event.message = (unsigned long)windowRef;
293 event.modifiers |= activeFlag;
296 acceptedEvent = sendEvent(&event);
298 LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d isActive: %d", acceptedEvent, hasFocus);
301 OSStatus WebNetscapePluginEventHandlerCarbon::TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *eventHandler)
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);
310 // Two-pass read to allocate/extract Mac charCodes
312 status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
313 if (status != noErr) {
314 LOG_ERROR("GetEventParameter failed with error: %d", status);
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);
325 EventRef cloneEvent = CopyEvent(rawKeyEventRef);
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);
335 EventRecord eventRec;
336 if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
338 acceptedEvent = static_cast<WebNetscapePluginEventHandlerCarbon*>(eventHandler)->sendEvent(&eventRec);
340 LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
341 acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
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.
349 ReleaseEvent(cloneEvent);
356 void WebNetscapePluginEventHandlerCarbon::installKeyEventHandler()
358 static const EventTypeSpec sTSMEvents[] =
360 { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
363 if (!m_keyEventHandler) {
364 InstallEventHandler(GetWindowEventTarget((WindowRef)[[m_pluginView window] windowRef]),
365 NewEventHandlerUPP(TSMEventHandler),
366 GetEventTypeCount(sTSMEvents),
373 void WebNetscapePluginEventHandlerCarbon::removeKeyEventHandler()
375 if (m_keyEventHandler) {
376 RemoveEventHandler(m_keyEventHandler);
377 m_keyEventHandler = 0;
381 void WebNetscapePluginEventHandlerCarbon::nullEventTimerFired(CFRunLoopTimerRef timerRef, void *context)
383 static_cast<WebNetscapePluginEventHandlerCarbon*>(context)->sendNullEvent();
386 void WebNetscapePluginEventHandlerCarbon::startTimers(bool throttleTimers)
388 ASSERT(!m_nullEventTimer);
390 CFTimeInterval interval = !throttleTimers ? NullEventIntervalActive : NullEventIntervalNotActive;
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);
398 void WebNetscapePluginEventHandlerCarbon::stopTimers()
400 if (!m_nullEventTimer)
403 CFRunLoopTimerInvalidate(m_nullEventTimer.get());
404 m_nullEventTimer = 0;
407 void* WebNetscapePluginEventHandlerCarbon::platformWindow(NSWindow* window)
409 return [window windowRef];
412 bool WebNetscapePluginEventHandlerCarbon::sendEvent(EventRecord* event)
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;
420 m_suspendKeyUpEvents = false;
422 bool result = [m_pluginView sendEvent:event isDrawRect:event->what == updateEvt];
424 m_currentEventIsUserGesture = false;
429 #endif // ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__)