initial import
[vuplus_webkit] / Source / WebKit / mac / Plugins / WebNetscapePluginEventHandlerCarbon.mm
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->modifiers = GetCurrentKeyModifiers();
53     if (!Button())
54         carbonEvent->modifiers |= btnState;
55 }
56
57 static EventModifiers modifiersForEvent(NSEvent *event)
58 {
59     EventModifiers modifiers;
60     unsigned int modifierFlags = [event modifierFlags];
61     NSEventType eventType = [event type];
62     
63     modifiers = 0;
64     
65     if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown)
66         modifiers |= btnState;
67     
68     if (modifierFlags & NSCommandKeyMask)
69         modifiers |= cmdKey;
70     
71     if (modifierFlags & NSShiftKeyMask)
72         modifiers |= shiftKey;
73
74     if (modifierFlags & NSAlphaShiftKeyMask)
75         modifiers |= alphaLock;
76
77     if (modifierFlags & NSAlternateKeyMask)
78         modifiers |= optionKey;
79
80     if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown)
81         modifiers |= controlKey;
82     
83     return modifiers;
84 }
85
86 static void getCarbonEvent(EventRecord *carbonEvent, NSEvent *cocoaEvent)
87 {
88     if (WKConvertNSEventToCarbonEvent(carbonEvent, cocoaEvent))
89         return;
90     
91     NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]];
92         
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);
99 }
100
101 void WebNetscapePluginEventHandlerCarbon::sendNullEvent()
102 {
103     EventRecord event;
104     
105     getCarbonEvent(&event);
106     
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);
110     
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?
114         event.where.v = -1;
115         event.where.h = -1;
116     }
117     
118     sendEvent(&event);
119 }
120
121 void WebNetscapePluginEventHandlerCarbon::drawRect(CGContextRef, const NSRect&)
122 {
123     EventRecord event;
124     
125     getCarbonEvent(&event);
126     event.what = updateEvt;
127     WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef];
128     event.message = (unsigned long)windowRef;
129     
130     BOOL acceptedEvent;
131     acceptedEvent = sendEvent(&event);
132     
133     LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent);
134 }
135
136 void WebNetscapePluginEventHandlerCarbon::mouseDown(NSEvent* theEvent)
137 {
138     EventRecord event;
139     
140     getCarbonEvent(&event, theEvent);
141     event.what = ::mouseDown;
142     
143     BOOL acceptedEvent;
144     acceptedEvent = sendEvent(&event);
145     
146     LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);    
147 }
148
149 void WebNetscapePluginEventHandlerCarbon::mouseUp(NSEvent* theEvent)
150 {
151     EventRecord event;
152     
153     getCarbonEvent(&event, theEvent);
154     event.what = ::mouseUp;
155     
156     BOOL acceptedEvent;
157     acceptedEvent = sendEvent(&event);
158     
159     LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h);    
160 }
161
162 bool WebNetscapePluginEventHandlerCarbon::scrollWheel(NSEvent* theEvent)
163 {
164     return false;
165 }
166
167 void WebNetscapePluginEventHandlerCarbon::mouseEntered(NSEvent* theEvent)
168 {
169     EventRecord event;
170     
171     getCarbonEvent(&event, theEvent);
172     event.what = NPEventType_AdjustCursorEvent;
173     
174     BOOL acceptedEvent;
175     acceptedEvent = sendEvent(&event);
176     
177     LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent);    
178 }
179
180 void WebNetscapePluginEventHandlerCarbon::mouseExited(NSEvent* theEvent)
181 {
182     EventRecord event;
183     
184     getCarbonEvent(&event, theEvent);
185     event.what = NPEventType_AdjustCursorEvent;
186     
187     BOOL acceptedEvent;
188     acceptedEvent = sendEvent(&event);
189     
190     LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent);    
191 }
192
193 void WebNetscapePluginEventHandlerCarbon::mouseDragged(NSEvent*)
194 {
195 }
196
197 void WebNetscapePluginEventHandlerCarbon::mouseMoved(NSEvent* theEvent)
198 {
199     EventRecord event;
200     
201     getCarbonEvent(&event, theEvent);
202     event.what = NPEventType_AdjustCursorEvent;
203     
204     BOOL acceptedEvent;
205     acceptedEvent = sendEvent(&event);
206     
207     LOG(PluginEvents, "NPP_HandleEvent(mouseMoved): %d", acceptedEvent);
208 }
209
210 void WebNetscapePluginEventHandlerCarbon::keyDown(NSEvent *theEvent)
211 {
212     m_suspendKeyUpEvents = true;
213     WKSendKeyEventToTSM(theEvent);
214 }
215
216 void WebNetscapePluginEventHandlerCarbon::syntheticKeyDownWithCommandModifier(int keyCode, char character)
217 {
218     EventRecord event;
219     getCarbonEvent(&event);
220     
221     event.what = ::keyDown;
222     event.modifiers |= cmdKey;
223     event.message = keyCode << 8 | character;
224     sendEvent(&event);
225 }
226
227 static UInt32 keyMessageForEvent(NSEvent *event)
228 {
229     NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())];
230     if (!data)
231         return 0;
232
233     UInt8 characterCode;
234     [data getBytes:&characterCode length:1];
235     UInt16 keyCode = [event keyCode];
236     return keyCode << 8 | characterCode;
237 }    
238     
239 void WebNetscapePluginEventHandlerCarbon::keyUp(NSEvent* theEvent)
240 {
241     WKSendKeyEventToTSM(theEvent);
242     
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) {
246         EventRecord event;
247         
248         getCarbonEvent(&event, theEvent);
249         event.what = ::keyUp;
250         
251         if (event.message == 0)
252             event.message = keyMessageForEvent(theEvent);
253         
254         sendEvent(&event);
255     }    
256 }
257
258 void WebNetscapePluginEventHandlerCarbon::flagsChanged(NSEvent*)
259 {
260 }
261
262 void WebNetscapePluginEventHandlerCarbon::focusChanged(bool hasFocus)
263 {
264     EventRecord event;
265     
266     getCarbonEvent(&event);
267     bool acceptedEvent;
268     if (hasFocus) {
269         event.what = NPEventType_GetFocusEvent;
270         acceptedEvent = sendEvent(&event);
271         LOG(PluginEvents, "NPP_HandleEvent(NPEventType_GetFocusEvent): %d", acceptedEvent);
272         installKeyEventHandler();
273     } else {
274         event.what = NPEventType_LoseFocusEvent;
275         acceptedEvent = sendEvent(&event);
276         LOG(PluginEvents, "NPP_HandleEvent(NPEventType_LoseFocusEvent): %d", acceptedEvent);
277         removeKeyEventHandler();
278     }
279 }
280
281 void WebNetscapePluginEventHandlerCarbon::windowFocusChanged(bool hasFocus)
282 {
283     WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef];
284
285     SetUserFocusWindow(windowRef);
286     
287     EventRecord event;
288     
289     getCarbonEvent(&event);
290     event.what = activateEvt;
291     event.message = (unsigned long)windowRef;
292     if (hasFocus)
293         event.modifiers |= activeFlag;
294     
295     BOOL acceptedEvent;
296     acceptedEvent = sendEvent(&event);
297     
298     LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d  isActive: %d", acceptedEvent, hasFocus);    
299 }
300
301 OSStatus WebNetscapePluginEventHandlerCarbon::TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *eventHandler)
302 {    
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);
307         return noErr;
308     }
309     
310     // Two-pass read to allocate/extract Mac charCodes
311     ByteCount numBytes;    
312     status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL);
313     if (status != noErr) {
314         LOG_ERROR("GetEventParameter failed with error: %d", status);
315         return noErr;
316     }
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);
321         free(buffer);
322         return noErr;
323     }
324     
325     EventRef cloneEvent = CopyEvent(rawKeyEventRef);
326     unsigned i;
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);
331             free(buffer);
332             return noErr;
333         }
334         
335         EventRecord eventRec;
336         if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
337             BOOL acceptedEvent;
338             acceptedEvent = static_cast<WebNetscapePluginEventHandlerCarbon*>(eventHandler)->sendEvent(&eventRec);
339             
340             LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu",
341                 acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask));
342             
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.
347         }
348     }
349     ReleaseEvent(cloneEvent);
350     
351     free(buffer);
352     
353     return noErr;
354 }
355
356 void WebNetscapePluginEventHandlerCarbon::installKeyEventHandler()
357 {
358     static const EventTypeSpec sTSMEvents[] =
359     {
360         { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
361     };
362     
363     if (!m_keyEventHandler) {
364         InstallEventHandler(GetWindowEventTarget((WindowRef)[[m_pluginView window] windowRef]),
365                             NewEventHandlerUPP(TSMEventHandler),
366                             GetEventTypeCount(sTSMEvents),
367                             sTSMEvents,
368                             this,
369                             &m_keyEventHandler);
370     }
371 }
372
373 void WebNetscapePluginEventHandlerCarbon::removeKeyEventHandler()
374 {
375     if (m_keyEventHandler) {
376         RemoveEventHandler(m_keyEventHandler);
377         m_keyEventHandler = 0;
378     }    
379 }
380
381 void WebNetscapePluginEventHandlerCarbon::nullEventTimerFired(CFRunLoopTimerRef timerRef, void *context)
382 {
383     static_cast<WebNetscapePluginEventHandlerCarbon*>(context)->sendNullEvent();
384 }
385
386 void WebNetscapePluginEventHandlerCarbon::startTimers(bool throttleTimers)
387 {
388     ASSERT(!m_nullEventTimer);
389     
390     CFTimeInterval interval = !throttleTimers ? NullEventIntervalActive : NullEventIntervalNotActive;    
391     
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);
396 }
397
398 void WebNetscapePluginEventHandlerCarbon::stopTimers()
399 {
400     if (!m_nullEventTimer)
401         return;
402     
403     CFRunLoopTimerInvalidate(m_nullEventTimer.get());
404     m_nullEventTimer = 0;
405 }
406
407 void* WebNetscapePluginEventHandlerCarbon::platformWindow(NSWindow* window)
408 {
409     return [window windowRef];
410 }
411
412 bool WebNetscapePluginEventHandlerCarbon::sendEvent(EventRecord* event)
413 {
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;
419     
420     m_suspendKeyUpEvents = false; 
421
422     bool result = [m_pluginView sendEvent:event isDrawRect:event->what == updateEvt];
423     
424     m_currentEventIsUserGesture = false;
425     
426     return result;
427 }
428
429 #endif // ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__)