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