2008-10-29 Anders Carlsson <andersca@apple.com>
[webkit/qt.git] / WebKit / mac / Plugins / WebNetscapePluginEventHandlerCarbon.mm
blobbfdd91c1fbd2eabe62dd95898bf3482a5acc8401
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  */
26 #if ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__)
28 #import "WebNetscapePluginEventHandlerCarbon.h"
30 #import "WebBaseNetscapePluginView.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(WebBaseNetscapePluginView* 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();
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;
59 static EventModifiers modifiersForEvent(NSEvent *event)
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;
76     if (modifierFlags & NSAlphaShiftKeyMask)
77         modifiers |= alphaLock;
79     if (modifierFlags & NSAlternateKeyMask)
80         modifiers |= optionKey;
82     if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
83         modifiers |= controlKey;
84     
85     return modifiers;
88 static void getCarbonEvent(EventRecord *carbonEvent, NSEvent *cocoaEvent)
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);
106 void WebNetscapePluginEventHandlerCarbon::sendNullEvent()
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);
126 void WebNetscapePluginEventHandlerCarbon::drawRect(const NSRect&)
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);
141 void WebNetscapePluginEventHandlerCarbon::mouseDown(NSEvent* theEvent)
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);    
154 void WebNetscapePluginEventHandlerCarbon::mouseUp(NSEvent* theEvent)
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);    
167 bool WebNetscapePluginEventHandlerCarbon::scrollWheel(NSEvent* theEvent)
169     return false;
172 void WebNetscapePluginEventHandlerCarbon::mouseEntered(NSEvent* theEvent)
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);    
185 void WebNetscapePluginEventHandlerCarbon::mouseExited(NSEvent* theEvent)
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);    
198 void WebNetscapePluginEventHandlerCarbon::mouseDragged(NSEvent*)
202 void WebNetscapePluginEventHandlerCarbon::mouseMoved(NSEvent*)
206 void WebNetscapePluginEventHandlerCarbon::keyDown(NSEvent *theEvent)
208     m_suspendKeyUpEvents = true;
209     WKSendKeyEventToTSM(theEvent);
212 static UInt32 keyMessageForEvent(NSEvent *event)
214     NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
215     if (!data)
216         return 0;
218     UInt8 characterCode;
219     [data getBytes:&characterCode length:1];
220     UInt16 keyCode = [event keyCode];
221     return keyCode << 8 | characterCode;
222 }    
223     
224 void WebNetscapePluginEventHandlerCarbon::keyUp(NSEvent* theEvent)
226     WKSendKeyEventToTSM(theEvent);
227     
228     // TSM won't send keyUp events so we have to send them ourselves.
229     // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9.
230     if (!m_suspendKeyUpEvents) {
231         EventRecord event;
232         
233         getCarbonEvent(&event, theEvent);
234         event.what = ::keyUp;
235         
236         if (event.message == 0)
237             event.message = keyMessageForEvent(theEvent);
238         
239         sendEvent(&event);
240     }    
243 void WebNetscapePluginEventHandlerCarbon::flagsChanged(NSEvent*)
247 void WebNetscapePluginEventHandlerCarbon::focusChanged(bool hasFocus)
249     EventRecord event;
250     
251     getCarbonEvent(&event);
252     bool acceptedEvent;
253     if (hasFocus) {
254         event.what = getFocusEvent;
255         acceptedEvent = sendEvent(&event);
256         LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent);
257         installKeyEventHandler();
258     } else {
259         event.what = loseFocusEvent;
260         acceptedEvent = sendEvent(&event);
261         LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent);
262         removeKeyEventHandler();
263     }
266 void WebNetscapePluginEventHandlerCarbon::windowFocusChanged(bool hasFocus)
268     EventRecord event;
269     
270     getCarbonEvent(&event);
271     event.what = activateEvt;
272     WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef];
273     event.message = (unsigned long)windowRef;
274     if (hasFocus)
275         event.modifiers |= activeFlag;
276     
277     BOOL acceptedEvent;
278     acceptedEvent = sendEvent(&event);
279     
280     LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d  isActive: %d", acceptedEvent, hasFocus);    
283 OSStatus WebNetscapePluginEventHandlerCarbon::TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *eventHandler)
284 {    
285     EventRef rawKeyEventRef;
286     OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef);
287     if (status != noErr) {
288         LOG_ERROR("GetEventParameter failed with error: %d", status);
289         return noErr;
290     }
291     
292     // Two-pass read to allocate/extract Mac charCodes
293     ByteCount numBytes;    
294     status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
295     if (status != noErr) {
296         LOG_ERROR("GetEventParameter failed with error: %d", status);
297         return noErr;
298     }
299     char *buffer = (char *)malloc(numBytes);
300     status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer);
301     if (status != noErr) {
302         LOG_ERROR("GetEventParameter failed with error: %d", status);
303         free(buffer);
304         return noErr;
305     }
306     
307     EventRef cloneEvent = CopyEvent(rawKeyEventRef);
308     unsigned i;
309     for (i = 0; i < numBytes; i++) {
310         status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]);
311         if (status != noErr) {
312             LOG_ERROR("SetEventParameter failed with error: %d", status);
313             free(buffer);
314             return noErr;
315         }
316         
317         EventRecord eventRec;
318         if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
319             BOOL acceptedEvent;
320             acceptedEvent = static_cast<WebNetscapePluginEventHandlerCarbon*>(eventHandler)->sendEvent(&eventRec);
321             
322             LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
323                 acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
324             
325             // We originally thought that if the plug-in didn't accept this event,
326             // we should pass it along so that keyboard scrolling, for example, will work.
327             // In practice, this is not a good idea, because plug-ins tend to eat the event but return false.
328             // MacIE handles each key event twice because of this, but we will emulate the other browsers instead.
329         }
330     }
331     ReleaseEvent(cloneEvent);
332     
333     free(buffer);
334     
335     return noErr;
338 void WebNetscapePluginEventHandlerCarbon::installKeyEventHandler()
340     static const EventTypeSpec sTSMEvents[] =
341     {
342         { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
343     };
344     
345     if (!m_keyEventHandler) {
346         InstallEventHandler(GetWindowEventTarget((WindowRef)[[m_pluginView window] windowRef]),
347                             NewEventHandlerUPP(TSMEventHandler),
348                             GetEventTypeCount(sTSMEvents),
349                             sTSMEvents,
350                             this,
351                             &m_keyEventHandler);
352     }
355 void WebNetscapePluginEventHandlerCarbon::removeKeyEventHandler()
357     if (m_keyEventHandler) {
358         RemoveEventHandler(m_keyEventHandler);
359         m_keyEventHandler = 0;
360     }    
363 void WebNetscapePluginEventHandlerCarbon::nullEventTimerFired(CFRunLoopTimerRef timerRef, void *context)
365     static_cast<WebNetscapePluginEventHandlerCarbon*>(context)->sendNullEvent();
368 void WebNetscapePluginEventHandlerCarbon::startTimers(bool throttleTimers)
370     ASSERT(!m_nullEventTimer);
371     
372     CFTimeInterval interval = !throttleTimers ? NullEventIntervalActive : NullEventIntervalNotActive;    
373     
374     CFRunLoopTimerContext context = { 0, this, NULL, NULL, NULL };
375     m_nullEventTimer.adoptCF(CFRunLoopTimerCreate(0, CFAbsoluteTimeGetCurrent() + interval, interval,
376                                                    0, 0, nullEventTimerFired, &context));
377     CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_nullEventTimer.get(), kCFRunLoopDefaultMode);
380 void WebNetscapePluginEventHandlerCarbon::stopTimers()
382     if (!m_nullEventTimer)
383         return;
384     
385     CFRunLoopTimerInvalidate(m_nullEventTimer.get());
386     m_nullEventTimer = 0;
389 void* WebNetscapePluginEventHandlerCarbon::platformWindow(NSWindow* window)
391     return [window windowRef];
394 bool WebNetscapePluginEventHandlerCarbon::sendEvent(EventRecord* event)
396     // If at any point the user clicks or presses a key from within a plugin, set the 
397     // currentEventIsUserGesture flag to true. This is important to differentiate legitimate 
398     // window.open() calls;  we still want to allow those.  See rdar://problem/4010765
399     if (event->what == ::mouseDown || event->what == ::keyDown || event->what == ::mouseUp || event->what == ::autoKey)
400         m_currentEventIsUserGesture = true;
401     
402     m_suspendKeyUpEvents = false; 
404     bool result = [m_pluginView sendEvent:event isDrawRect:event->what == updateEvt];
405     
406     m_currentEventIsUserGesture = false;
407     
408     return result;
411 #endif // ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__)