1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Copyright (C) 2010 University of Szeged
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "config.h"
28 #if PLUGIN_ARCHITECTURE(X11)
29 
30 #include "NetscapePlugin.h"
31 
32 #include "WebEvent.h"
33 #include <WebCore/GraphicsContext.h>
34 #include <WebCore/NotImplemented.h>
35 
36 #if PLATFORM(QT)
37 #include <QApplication>
38 #include <QDesktopWidget>
39 #include <QPixmap>
40 #include <QX11Info>
41 #elif PLATFORM(GTK)
42 #include <gdk/gdkx.h>
43 #include <WebCore/GtkVersioning.h>
44 #endif
45 
46 using namespace WebCore;
47 
48 namespace WebKit {
49 
getPluginDisplay()50 static Display *getPluginDisplay()
51 {
52 #if PLATFORM(QT)
53     // At the moment, we only support gdk based plugins (like Flash) that use a different X connection.
54     // The code below has the same effect as this one:
55     // Display *gdkDisplay = gdk_x11_display_get_xdisplay(gdk_display_get_default());
56 
57     QLibrary library(QLatin1String("libgdk-x11-2.0"), 0);
58     if (!library.load())
59         return 0;
60 
61     typedef void *(*gdk_display_get_default_ptr)();
62     gdk_display_get_default_ptr gdk_display_get_default = (gdk_display_get_default_ptr)library.resolve("gdk_display_get_default");
63     if (!gdk_display_get_default)
64         return 0;
65 
66     typedef void *(*gdk_x11_display_get_xdisplay_ptr)(void *);
67     gdk_x11_display_get_xdisplay_ptr gdk_x11_display_get_xdisplay = (gdk_x11_display_get_xdisplay_ptr)library.resolve("gdk_x11_display_get_xdisplay");
68     if (!gdk_x11_display_get_xdisplay)
69         return 0;
70 
71     return (Display*)gdk_x11_display_get_xdisplay(gdk_display_get_default());
72 #elif PLATFORM(GTK)
73     // Since we're a gdk/gtk app, we'll (probably?) have the same X connection as any gdk-based
74     // plugins, so we can return that. We might want to add other implementations here later.
75     return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
76 #else
77     return 0;
78 #endif
79 }
80 
x11Display()81 static inline Display* x11Display()
82 {
83 #if PLATFORM(QT)
84     return QX11Info::display();
85 #elif PLATFORM(GTK)
86     return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
87 #else
88     return 0;
89 #endif
90 }
91 
displayDepth()92 static inline int displayDepth()
93 {
94 #if PLATFORM(QT)
95     return QApplication::desktop()->x11Info().depth();
96 #elif PLATFORM(GTK)
97     return gdk_visual_get_depth(gdk_screen_get_system_visual(gdk_screen_get_default()));
98 #else
99     return 0;
100 #endif
101 }
102 
rootWindowID()103 static inline unsigned long rootWindowID()
104 {
105 #if PLATFORM(QT)
106     return QX11Info::appRootWindow();
107 #elif PLATFORM(GTK)
108     return GDK_ROOT_WINDOW();
109 #else
110     return 0;
111 #endif
112 }
113 
x11Screen()114 static inline int x11Screen()
115 {
116 #if PLATFORM(QT)
117     return QX11Info::appScreen();
118 #elif PLATFORM(GTK)
119     return gdk_screen_get_number(gdk_screen_get_default());
120 #else
121     return 0;
122 #endif
123 }
124 
platformPostInitialize()125 bool NetscapePlugin::platformPostInitialize()
126 {
127     if (m_isWindowed)
128         return false;
129 
130     if (!(m_pluginDisplay = getPluginDisplay()))
131         return false;
132 
133     NPSetWindowCallbackStruct* callbackStruct = new NPSetWindowCallbackStruct;
134     callbackStruct->type = 0;
135     Display* display = x11Display();
136     int depth = displayDepth();
137     callbackStruct->display = display;
138     callbackStruct->depth = depth;
139 
140     XVisualInfo visualTemplate;
141     visualTemplate.screen = x11Screen();
142     visualTemplate.depth = depth;
143     visualTemplate.c_class = TrueColor;
144     int numMatching;
145     XVisualInfo* visualInfo = XGetVisualInfo(display, VisualScreenMask | VisualDepthMask | VisualClassMask,
146                                              &visualTemplate, &numMatching);
147     ASSERT(visualInfo);
148     Visual* visual = visualInfo[0].visual;
149     ASSERT(visual);
150     XFree(visualInfo);
151 
152     callbackStruct->visual = visual;
153     callbackStruct->colormap = XCreateColormap(display, rootWindowID(), visual, AllocNone);
154 
155     m_npWindow.type = NPWindowTypeDrawable;
156     m_npWindow.window = 0;
157     m_npWindow.ws_info = callbackStruct;
158 
159     callSetWindow();
160 
161     return true;
162 }
163 
platformDestroy()164 void NetscapePlugin::platformDestroy()
165 {
166     delete static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info);
167 
168     if (m_drawable) {
169         XFreePixmap(x11Display(), m_drawable);
170         m_drawable = 0;
171     }
172 }
173 
platformInvalidate(const IntRect &)174 bool NetscapePlugin::platformInvalidate(const IntRect&)
175 {
176     notImplemented();
177     return false;
178 }
179 
platformGeometryDidChange()180 void NetscapePlugin::platformGeometryDidChange()
181 {
182     if (m_isWindowed) {
183         notImplemented();
184         return;
185     }
186 
187     Display* display = x11Display();
188     if (m_drawable)
189         XFreePixmap(display, m_drawable);
190 
191     m_drawable = XCreatePixmap(display, rootWindowID(), m_frameRect.width(), m_frameRect.height(), displayDepth());
192 
193     XSync(display, false); // Make sure that the server knows about the Drawable.
194 }
195 
platformPaint(GraphicsContext * context,const IntRect & dirtyRect,bool)196 void NetscapePlugin::platformPaint(GraphicsContext* context, const IntRect& dirtyRect, bool /*isSnapshot*/)
197 {
198     if (m_isWindowed) {
199         notImplemented();
200         return;
201     }
202 
203     if (!m_isStarted) {
204         // FIXME: we should paint a missing plugin icon.
205         return;
206     }
207 
208     if (context->paintingDisabled())
209         return;
210 
211     ASSERT(m_drawable);
212 
213 #if PLATFORM(QT)
214     QPainter* painter = context->platformContext();
215     painter->translate(m_frameRect.x(), m_frameRect.y());
216 #else
217     notImplemented();
218     return;
219 #endif
220 
221     XEvent xevent;
222     memset(&xevent, 0, sizeof(XEvent));
223     XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose;
224     exposeEvent.type = GraphicsExpose;
225     exposeEvent.display = x11Display();
226     exposeEvent.drawable = m_drawable;
227 
228     IntRect exposedRect(dirtyRect);
229     exposedRect.intersect(m_frameRect);
230     exposedRect.move(-m_frameRect.x(), -m_frameRect.y());
231     exposeEvent.x = exposedRect.x();
232     exposeEvent.y = exposedRect.y();
233 
234     // Note: in transparent mode Flash thinks width is the right and height is the bottom.
235     // We should take it into account if we want to support transparency.
236     exposeEvent.width = exposedRect.width();
237     exposeEvent.height = exposedRect.height();
238 
239     NPP_HandleEvent(&xevent);
240 
241     if (m_pluginDisplay != x11Display())
242         XSync(m_pluginDisplay, false);
243 
244 #if PLATFORM(QT)
245     QPixmap qtDrawable = QPixmap::fromX11Pixmap(m_drawable, QPixmap::ExplicitlyShared);
246     ASSERT(qtDrawable.depth() == static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info)->depth);
247     painter->drawPixmap(QPoint(exposedRect.x(), exposedRect.y()), qtDrawable, exposedRect);
248 
249     painter->translate(-m_frameRect.x(), -m_frameRect.y());
250 #endif
251 }
252 
initializeXEvent(XEvent & event)253 static inline void initializeXEvent(XEvent& event)
254 {
255     memset(&event, 0, sizeof(XEvent));
256     event.xany.serial = 0;
257     event.xany.send_event = false;
258     event.xany.display = x11Display();
259     event.xany.window = 0;
260 }
261 
xTimeStamp(double timestampInSeconds)262 static inline uint64_t xTimeStamp(double timestampInSeconds)
263 {
264     return timestampInSeconds * 1000;
265 }
266 
xKeyModifiers(const WebEvent & event)267 static inline unsigned xKeyModifiers(const WebEvent& event)
268 {
269     unsigned xModifiers = 0;
270     if (event.controlKey())
271         xModifiers |= ControlMask;
272     if (event.shiftKey())
273         xModifiers |= ShiftMask;
274     if (event.altKey())
275         xModifiers |= Mod1Mask;
276     if (event.metaKey())
277         xModifiers |= Mod4Mask;
278 
279     return xModifiers;
280 }
281 
282 template <typename XEventType>
setCommonMouseEventFields(XEventType & xEvent,const WebMouseEvent & webEvent,const WebCore::IntPoint & pluginLocation)283 static inline void setCommonMouseEventFields(XEventType& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation)
284 {
285     xEvent.root = rootWindowID();
286     xEvent.subwindow = 0;
287     xEvent.time = xTimeStamp(webEvent.timestamp());
288     xEvent.x = webEvent.position().x() - pluginLocation.x();
289     xEvent.y = webEvent.position().y() - pluginLocation.y();
290     xEvent.x_root = webEvent.globalPosition().x();
291     xEvent.y_root = webEvent.globalPosition().y();
292     xEvent.state = xKeyModifiers(webEvent);
293     xEvent.same_screen = true;
294 }
295 
setXMotionEventFields(XEvent & xEvent,const WebMouseEvent & webEvent,const WebCore::IntPoint & pluginLocation)296 static inline void setXMotionEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation)
297 {
298     XMotionEvent& xMotion = xEvent.xmotion;
299     setCommonMouseEventFields(xMotion, webEvent, pluginLocation);
300     xMotion.type = MotionNotify;
301 }
302 
setXButtonEventFields(XEvent & xEvent,const WebMouseEvent & webEvent,const WebCore::IntPoint & pluginLocation)303 static inline void setXButtonEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation)
304 {
305     XButtonEvent& xButton = xEvent.xbutton;
306     setCommonMouseEventFields(xButton, webEvent, pluginLocation);
307 
308     xButton.type = (webEvent.type() == WebEvent::MouseDown) ? ButtonPress : ButtonRelease;
309     switch (webEvent.button()) {
310     case WebMouseEvent::LeftButton:
311         xButton.button = Button1;
312         break;
313     case WebMouseEvent::MiddleButton:
314         xButton.button = Button2;
315         break;
316     case WebMouseEvent::RightButton:
317         xButton.button = Button3;
318         break;
319     }
320 }
321 
setXCrossingEventFields(XEvent & xEvent,const WebMouseEvent & webEvent,const WebCore::IntPoint & pluginLocation,int type)322 static inline void setXCrossingEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation, int type)
323 {
324     XCrossingEvent& xCrossing = xEvent.xcrossing;
325     setCommonMouseEventFields(xCrossing, webEvent, pluginLocation);
326 
327     xCrossing.type = type;
328     xCrossing.mode = NotifyNormal;
329     xCrossing.detail = NotifyDetailNone;
330     xCrossing.focus = false;
331 }
332 
platformHandleMouseEvent(const WebMouseEvent & event)333 bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& event)
334 {
335     if (m_isWindowed)
336         return false;
337 
338     XEvent xEvent;
339     initializeXEvent(xEvent);
340 
341     switch (event.type()) {
342     case WebEvent::MouseDown:
343     case WebEvent::MouseUp:
344         setXButtonEventFields(xEvent, event, m_frameRect.location());
345         break;
346     case WebEvent::MouseMove:
347         setXMotionEventFields(xEvent, event, m_frameRect.location());
348         break;
349     }
350 
351     return NPP_HandleEvent(&xEvent);
352 }
353 
354 // We undefine these constants in npruntime_internal.h to avoid collision
355 // with WebKit and platform headers. Values are defined in X.h.
356 const int kKeyPressType = 2;
357 const int kKeyReleaseType = 3;
358 const int kFocusInType = 9;
359 const int kFocusOutType = 10;
360 
platformHandleWheelEvent(const WebWheelEvent &)361 bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent&)
362 {
363     notImplemented();
364     return false;
365 }
366 
platformSetFocus(bool)367 void NetscapePlugin::platformSetFocus(bool)
368 {
369     notImplemented();
370 }
371 
platformHandleMouseEnterEvent(const WebMouseEvent & event)372 bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& event)
373 {
374     if (m_isWindowed)
375         return false;
376 
377     XEvent xEvent;
378     initializeXEvent(xEvent);
379     setXCrossingEventFields(xEvent, event, m_frameRect.location(), EnterNotify);
380 
381     return NPP_HandleEvent(&xEvent);
382 }
383 
platformHandleMouseLeaveEvent(const WebMouseEvent & event)384 bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& event)
385 {
386     if (m_isWindowed)
387         return false;
388 
389     XEvent xEvent;
390     initializeXEvent(xEvent);
391     setXCrossingEventFields(xEvent, event, m_frameRect.location(), LeaveNotify);
392 
393     return NPP_HandleEvent(&xEvent);
394 }
395 
setXKeyEventFields(XEvent & xEvent,const WebKeyboardEvent & webEvent)396 static inline void setXKeyEventFields(XEvent& xEvent, const WebKeyboardEvent& webEvent)
397 {
398     xEvent.xany.type = (webEvent.type() == WebEvent::KeyDown) ? kKeyPressType : kKeyReleaseType;
399     XKeyEvent& xKey = xEvent.xkey;
400     xKey.root = rootWindowID();
401     xKey.subwindow = 0;
402     xKey.time = xTimeStamp(webEvent.timestamp());
403     xKey.state = xKeyModifiers(webEvent);
404     xKey.keycode = webEvent.nativeVirtualKeyCode();
405 
406     xKey.same_screen = true;
407 
408     // Key events propagated to the plugin does not need to have position.
409     // source: https://developer.mozilla.org/en/NPEvent
410     xKey.x = 0;
411     xKey.y = 0;
412     xKey.x_root = 0;
413     xKey.y_root = 0;
414 }
415 
platformHandleKeyboardEvent(const WebKeyboardEvent & event)416 bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent& event)
417 {
418     // We don't generate other types of keyboard events via WebEventFactory.
419     ASSERT(event.type() == WebEvent::KeyDown || event.type() == WebEvent::KeyUp);
420 
421     XEvent xEvent;
422     initializeXEvent(xEvent);
423     setXKeyEventFields(xEvent, event);
424 
425     return NPP_HandleEvent(&xEvent);
426 }
427 
428 } // namespace WebKit
429 
430 #endif // PLUGIN_ARCHITECTURE(X11)
431