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)
27
28#import "WebNetscapePluginEventHandlerCocoa.h"
29
30#import "WebKitSystemInterface.h"
31#import "WebNetscapePluginView.h"
32#import <wtf/Vector.h>
33
34WebNetscapePluginEventHandlerCocoa::WebNetscapePluginEventHandlerCocoa(WebNetscapePluginView* pluginView)
35    : WebNetscapePluginEventHandler(pluginView)
36#ifndef __LP64__
37    , m_keyEventHandler(0)
38#endif
39{
40}
41
42static inline void initializeEvent(NPCocoaEvent* event, NPCocoaEventType type)
43{
44    event->type = type;
45    event->version = 0;
46}
47
48void WebNetscapePluginEventHandlerCocoa::drawRect(CGContextRef context, const NSRect& rect)
49{
50    NPCocoaEvent event;
51
52    initializeEvent(&event, NPCocoaEventDrawRect);
53    event.data.draw.context = context;
54    event.data.draw.x = rect.origin.x;
55    event.data.draw.y = rect.origin.y;
56    event.data.draw.width = rect.size.width;
57    event.data.draw.height = rect.size.height;
58
59    RetainPtr<CGContextRef> protect(context);
60
61    sendEvent(&event);
62}
63
64void WebNetscapePluginEventHandlerCocoa::mouseDown(NSEvent *event)
65{
66    sendMouseEvent(event, NPCocoaEventMouseDown);
67}
68
69void WebNetscapePluginEventHandlerCocoa::mouseDragged(NSEvent *event)
70{
71    sendMouseEvent(event, NPCocoaEventMouseDragged);
72}
73
74void WebNetscapePluginEventHandlerCocoa::mouseEntered(NSEvent *event)
75{
76    sendMouseEvent(event, NPCocoaEventMouseEntered);
77}
78
79void WebNetscapePluginEventHandlerCocoa::mouseExited(NSEvent *event)
80{
81    sendMouseEvent(event, NPCocoaEventMouseExited);
82}
83
84void WebNetscapePluginEventHandlerCocoa::mouseMoved(NSEvent *event)
85{
86    sendMouseEvent(event, NPCocoaEventMouseMoved);
87}
88
89void WebNetscapePluginEventHandlerCocoa::mouseUp(NSEvent *event)
90{
91    sendMouseEvent(event, NPCocoaEventMouseUp);
92}
93
94bool WebNetscapePluginEventHandlerCocoa::scrollWheel(NSEvent* event)
95{
96    return sendMouseEvent(event, NPCocoaEventScrollWheel);
97}
98
99bool WebNetscapePluginEventHandlerCocoa::sendMouseEvent(NSEvent *nsEvent, NPCocoaEventType type)
100{
101    NPCocoaEvent event;
102
103    NSPoint point = [m_pluginView convertPoint:[nsEvent locationInWindow] fromView:nil];
104
105    int clickCount;
106    if (type == NPCocoaEventMouseEntered || type == NPCocoaEventMouseExited || type == NPCocoaEventScrollWheel)
107        clickCount = 0;
108    else
109        clickCount = [nsEvent clickCount];
110
111    initializeEvent(&event, type);
112    event.data.mouse.modifierFlags = [nsEvent modifierFlags];
113    event.data.mouse.buttonNumber = [nsEvent buttonNumber];
114    event.data.mouse.clickCount = clickCount;
115    event.data.mouse.pluginX = point.x;
116    event.data.mouse.pluginY = point.y;
117    event.data.mouse.deltaX = [nsEvent deltaX];
118    event.data.mouse.deltaY = [nsEvent deltaY];
119    event.data.mouse.deltaZ = [nsEvent deltaZ];
120
121    return sendEvent(&event);
122}
123
124void WebNetscapePluginEventHandlerCocoa::keyDown(NSEvent *event)
125{
126    bool retval = sendKeyEvent(event, NPCocoaEventKeyDown);
127
128#ifndef __LP64__
129    // If the plug-in did not handle the event, pass it on to the Input Manager.
130    if (retval)
131        WKSendKeyEventToTSM(event);
132#else
133    UNUSED_PARAM(retval);
134#endif
135}
136
137void WebNetscapePluginEventHandlerCocoa::keyUp(NSEvent *event)
138{
139    sendKeyEvent(event, NPCocoaEventKeyUp);
140}
141
142void WebNetscapePluginEventHandlerCocoa::flagsChanged(NSEvent *nsEvent)
143{
144    NPCocoaEvent event;
145
146    initializeEvent(&event, NPCocoaEventFlagsChanged);
147    event.data.key.modifierFlags = [nsEvent modifierFlags];
148    event.data.key.keyCode = [nsEvent keyCode];
149    event.data.key.isARepeat = false;
150    event.data.key.characters = 0;
151    event.data.key.charactersIgnoringModifiers = 0;
152
153    sendEvent(&event);
154}
155
156void WebNetscapePluginEventHandlerCocoa::syntheticKeyDownWithCommandModifier(int keyCode, char character)
157{
158    char nullTerminatedString[] = { character, '\0' };
159
160    RetainPtr<NSString> characters = adoptNS([[NSString alloc] initWithUTF8String:nullTerminatedString]);
161
162    NPCocoaEvent event;
163    initializeEvent(&event, NPCocoaEventKeyDown);
164    event.data.key.modifierFlags = NSCommandKeyMask;
165    event.data.key.keyCode = keyCode;
166    event.data.key.isARepeat = false;
167    event.data.key.characters = (NPNSString *)characters.get();
168    event.data.key.charactersIgnoringModifiers = (NPNSString *)characters.get();
169
170    sendEvent(&event);
171}
172
173bool WebNetscapePluginEventHandlerCocoa::sendKeyEvent(NSEvent* nsEvent, NPCocoaEventType type)
174{
175    NPCocoaEvent event;
176
177    initializeEvent(&event, type);
178    event.data.key.modifierFlags = [nsEvent modifierFlags];
179    event.data.key.keyCode = [nsEvent keyCode];
180    event.data.key.isARepeat = [nsEvent isARepeat];
181    event.data.key.characters = (NPNSString *)[nsEvent characters];
182    event.data.key.charactersIgnoringModifiers = (NPNSString *)[nsEvent charactersIgnoringModifiers];
183
184    return sendEvent(&event);
185}
186
187void WebNetscapePluginEventHandlerCocoa::windowFocusChanged(bool hasFocus)
188{
189    NPCocoaEvent event;
190
191    initializeEvent(&event, NPCocoaEventWindowFocusChanged);
192    event.data.focus.hasFocus = hasFocus;
193
194    sendEvent(&event);
195}
196
197void WebNetscapePluginEventHandlerCocoa::focusChanged(bool hasFocus)
198{
199    NPCocoaEvent event;
200
201    initializeEvent(&event, NPCocoaEventFocusChanged);
202    event.data.focus.hasFocus = hasFocus;
203
204    sendEvent(&event);
205
206    if (hasFocus)
207        installKeyEventHandler();
208    else
209        removeKeyEventHandler();
210}
211
212void* WebNetscapePluginEventHandlerCocoa::platformWindow(NSWindow* window)
213{
214    return window;
215}
216
217bool WebNetscapePluginEventHandlerCocoa::sendEvent(NPCocoaEvent* event)
218{
219    switch (event->type) {
220        case NPCocoaEventMouseDown:
221        case NPCocoaEventMouseUp:
222        case NPCocoaEventMouseDragged:
223        case NPCocoaEventKeyDown:
224        case NPCocoaEventKeyUp:
225        case NPCocoaEventFlagsChanged:
226        case NPCocoaEventScrollWheel:
227            m_currentEventIsUserGesture = true;
228            break;
229        default:
230            m_currentEventIsUserGesture = false;
231    }
232
233    bool result = [m_pluginView sendEvent:event isDrawRect:event->type == NPCocoaEventDrawRect];
234
235    m_currentEventIsUserGesture = false;
236    return result;
237}
238
239#ifndef __LP64__
240
241void WebNetscapePluginEventHandlerCocoa::installKeyEventHandler()
242{
243    static const EventTypeSpec TSMEvents[] =
244    {
245        { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
246    };
247
248    if (!m_keyEventHandler)
249        InstallEventHandler(GetWindowEventTarget((WindowRef)[[m_pluginView window] windowRef]),
250                            NewEventHandlerUPP(TSMEventHandler),
251                            GetEventTypeCount(TSMEvents),
252                            TSMEvents,
253                            this,
254                            &m_keyEventHandler);
255}
256
257void WebNetscapePluginEventHandlerCocoa::removeKeyEventHandler()
258{
259    if (m_keyEventHandler) {
260        RemoveEventHandler(m_keyEventHandler);
261        m_keyEventHandler = 0;
262    }
263}
264
265OSStatus WebNetscapePluginEventHandlerCocoa::TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef event, void* eventHandler)
266{
267    return static_cast<WebNetscapePluginEventHandlerCocoa*>(eventHandler)->handleTSMEvent(event);
268}
269
270OSStatus WebNetscapePluginEventHandlerCocoa::handleTSMEvent(EventRef eventRef)
271{
272    ASSERT(GetEventKind(eventRef) == kEventTextInputUnicodeForKeyEvent);
273
274    // Get the text buffer size.
275    ByteCount size;
276    OSStatus result = GetEventParameter(eventRef, kEventParamTextInputSendText, typeUnicodeText, 0, 0, &size, 0);
277    if (result != noErr)
278        return result;
279
280    unsigned length = size / sizeof(UniChar);
281    Vector<UniChar, 16> characters(length);
282
283    // Now get the actual text.
284    result = GetEventParameter(eventRef, kEventParamTextInputSendText, typeUnicodeText, 0, size, 0, characters.data());
285    if (result != noErr)
286        return result;
287
288    RetainPtr<CFStringRef> text = adoptCF(CFStringCreateWithCharacters(0, characters.data(), length));
289
290    NPCocoaEvent event;
291
292    initializeEvent(&event, NPCocoaEventTextInput);
293    event.data.text.text = (NPNSString*)text.get();
294
295    sendEvent(&event);
296
297    return noErr;
298}
299
300#endif // __LP64__
301
302#endif // ENABLE(NETSCAPE_PLUGIN_API)
303