[GTK] Plugin process crashes with GTK2 windowed plugins
[WebKit-https.git] / Source / WebKit2 / WebProcess / Plugins / Netscape / x11 / NetscapePluginX11.cpp
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) && ENABLE(NETSCAPE_PLUGIN_API)
29
30 #include "NetscapePlugin.h"
31
32 #include "PluginController.h"
33 #include "WebEvent.h"
34 #include <WebCore/GraphicsContext.h>
35 #include <WebCore/NotImplemented.h>
36
37 #if PLATFORM(GTK)
38 #include <gtk/gtk.h>
39 #ifndef GTK_API_VERSION_2
40 #include <gtk/gtkx.h>
41 #endif
42 #include <gdk/gdkx.h>
43 #include <WebCore/GtkVersioning.h>
44 #elif PLATFORM(EFL) && defined(HAVE_ECORE_X)
45 #include <Ecore_X.h>
46 #endif
47
48 #if USE(CAIRO)
49 #include "PlatformContextCairo.h"
50 #include "RefPtrCairo.h"
51 #include <cairo/cairo-xlib.h>
52 #endif
53
54 using namespace WebCore;
55
56 namespace WebKit {
57
58 static Display* getPluginDisplay()
59 {
60 #if PLATFORM(GTK)
61     // Since we're a gdk/gtk app, we'll (probably?) have the same X connection as any gdk-based
62     // plugins, so we can return that. We might want to add other implementations here later.
63     return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
64 #elif PLATFORM(EFL) && defined(HAVE_ECORE_X)
65     return static_cast<Display*>(ecore_x_display_get());
66 #else
67     return 0;
68 #endif
69 }
70
71 static inline int x11Screen()
72 {
73 #if PLATFORM(GTK)
74     return gdk_screen_get_number(gdk_screen_get_default());
75 #elif PLATFORM(EFL) && defined(HAVE_ECORE_X)
76     return ecore_x_screen_index_get(ecore_x_default_screen_get());
77 #else
78     return 0;
79 #endif
80 }
81
82 static inline int displayDepth()
83 {
84 #if PLATFORM(GTK)
85     return gdk_visual_get_depth(gdk_screen_get_system_visual(gdk_screen_get_default()));
86 #elif PLATFORM(EFL) && defined(HAVE_ECORE_X)
87     return ecore_x_default_depth_get(NetscapePlugin::x11HostDisplay(), ecore_x_default_screen_get());
88 #else
89     return 0;
90 #endif
91 }
92
93 static inline unsigned long rootWindowID()
94 {
95 #if PLATFORM(GTK)
96     return GDK_ROOT_WINDOW();
97 #elif PLATFORM(EFL) && defined(HAVE_ECORE_X)
98     return ecore_x_window_root_first_get();
99 #else
100     return 0;
101 #endif
102 }
103
104 #if PLATFORM(GTK)
105 static bool moduleMixesGtkSymbols(Module* module)
106 {
107 #ifdef GTK_API_VERSION_2
108     return module->functionPointer<gpointer>("gtk_application_get_type");
109 #else
110     return module->functionPointer<gpointer>("gtk_object_get_type");
111 #endif
112 }
113 #endif
114
115 Display* NetscapePlugin::x11HostDisplay()
116 {
117 #if PLATFORM(GTK)
118     return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
119 #elif PLATFORM(EFL) && defined(HAVE_ECORE_X)
120     return static_cast<Display*>(ecore_x_display_get());
121 #else
122     return 0;
123 #endif
124 }
125
126 #if PLATFORM(GTK)
127 static gboolean socketPlugRemovedCallback(GtkSocket*)
128 {
129     // Default action is to destroy the GtkSocket, so we just return TRUE here
130     // to be able to reuse the socket. For some obscure reason, newer versions
131     // of flash plugin remove the plug from the socket, probably because the plug
132     // created by the plugin is re-parented.
133     return TRUE;
134 }
135 #endif
136
137 bool NetscapePlugin::platformPostInitializeWindowed(bool needsXEmbed, uint64_t windowID)
138 {
139     m_npWindow.type = NPWindowTypeWindow;
140     if (!needsXEmbed) {
141         notImplemented();
142         return false;
143     }
144
145     Display* display = x11HostDisplay();
146
147 #if PLATFORM(GTK)
148     // It seems flash needs the socket to be in the same process,
149     // I guess it uses gdk_window_lookup(), so we create a new socket here
150     // containing a plug with the UI process socket embedded.
151     m_platformPluginWidget = gtk_plug_new(static_cast<Window>(windowID));
152     GtkWidget* socket = gtk_socket_new();
153     // Do not show the plug widget until the socket is connected.
154     g_signal_connect_swapped(socket, "plug-added", G_CALLBACK(gtk_widget_show), m_platformPluginWidget);
155     g_signal_connect(socket, "plug-removed", G_CALLBACK(socketPlugRemovedCallback), nullptr);
156     gtk_container_add(GTK_CONTAINER(m_platformPluginWidget), socket);
157     gtk_widget_show(socket);
158
159     m_npWindow.window = GINT_TO_POINTER(gtk_socket_get_id(GTK_SOCKET(socket)));
160     GdkWindow* window = gtk_widget_get_window(socket);
161     NPSetWindowCallbackStruct* callbackStruct = static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info);
162     callbackStruct->display = GDK_WINDOW_XDISPLAY(window);
163     callbackStruct->visual = GDK_VISUAL_XVISUAL(gdk_window_get_visual(window));
164     callbackStruct->depth = gdk_visual_get_depth(gdk_window_get_visual(window));
165     callbackStruct->colormap = XCreateColormap(display, GDK_ROOT_WINDOW(), callbackStruct->visual, AllocNone);
166 #else
167     UNUSED_PARAM(windowID);
168 #endif
169
170     XFlush(display);
171
172     callSetWindow();
173
174     return true;
175 }
176
177 bool NetscapePlugin::platformPostInitializeWindowless()
178 {
179     Display* display = x11HostDisplay();
180     m_npWindow.type = NPWindowTypeDrawable;
181     m_npWindow.window = 0;
182
183     int depth = displayDepth();
184
185     NPSetWindowCallbackStruct* callbackStruct = static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info);
186     callbackStruct->display = display;
187     callbackStruct->depth = depth;
188
189     XVisualInfo visualTemplate;
190     visualTemplate.screen = x11Screen();
191     visualTemplate.depth = depth;
192     visualTemplate.c_class = TrueColor;
193     int numMatching;
194     XVisualInfo* visualInfo = XGetVisualInfo(display, VisualScreenMask | VisualDepthMask | VisualClassMask,
195                                              &visualTemplate, &numMatching);
196     ASSERT(visualInfo);
197     Visual* visual = visualInfo[0].visual;
198     ASSERT(visual);
199     XFree(visualInfo);
200
201     callbackStruct->visual = visual;
202     callbackStruct->colormap = XCreateColormap(display, rootWindowID(), visual, AllocNone);
203
204     callSetWindow();
205
206     return true;
207 }
208
209 void NetscapePlugin::platformPreInitialize()
210 {
211 }
212
213 bool NetscapePlugin::platformPostInitialize()
214 {
215 #if PLATFORM(GTK)
216     if (moduleMixesGtkSymbols(m_pluginModule->module()))
217         return false;
218 #endif
219
220     uint64_t windowID = 0;
221     bool needsXEmbed = false;
222     if (m_isWindowed) {
223         NPP_GetValue(NPPVpluginNeedsXEmbed, &needsXEmbed);
224         if (needsXEmbed) {
225             windowID = controller()->createPluginContainer();
226             if (!windowID)
227                 return false;
228         } else {
229             notImplemented();
230             return false;
231         }
232     }
233
234     if (!(m_pluginDisplay = getPluginDisplay()))
235         return false;
236
237     NPSetWindowCallbackStruct* callbackStruct = new NPSetWindowCallbackStruct;
238     callbackStruct->type = 0;
239     m_npWindow.ws_info = callbackStruct;
240
241     if (m_isWindowed)
242         return platformPostInitializeWindowed(needsXEmbed, windowID);
243
244     return platformPostInitializeWindowless();
245 }
246
247 void NetscapePlugin::platformDestroy()
248 {
249     NPSetWindowCallbackStruct* callbackStruct = static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info);
250     Display* hostDisplay = x11HostDisplay();
251     XFreeColormap(hostDisplay, callbackStruct->colormap);
252     delete callbackStruct;
253
254     if (m_drawable) {
255         XFreePixmap(hostDisplay, m_drawable);
256         m_drawable = 0;
257     }
258
259 #if PLATFORM(GTK)
260     if (m_platformPluginWidget) {
261         gtk_widget_destroy(m_platformPluginWidget);
262         m_platformPluginWidget = 0;
263     }
264 #endif
265 }
266
267 bool NetscapePlugin::platformInvalidate(const IntRect&)
268 {
269     notImplemented();
270     return false;
271 }
272
273 void NetscapePlugin::platformGeometryDidChange()
274 {
275     if (m_isWindowed) {
276         uint64_t windowID = 0;
277 #if PLATFORM(GTK)
278         windowID = static_cast<uint64_t>(GDK_WINDOW_XID(gtk_plug_get_socket_window(GTK_PLUG(m_platformPluginWidget))));
279 #endif
280         controller()->windowedPluginGeometryDidChange(m_frameRectInWindowCoordinates, m_clipRect, windowID);
281         return;
282     }
283
284     Display* display = x11HostDisplay();
285     if (m_drawable)
286         XFreePixmap(display, m_drawable);
287
288     if (m_pluginSize.isEmpty()) {
289         m_drawable = 0;
290         return;
291     }
292
293     m_drawable = XCreatePixmap(display, rootWindowID(), m_pluginSize.width(), m_pluginSize.height(), displayDepth());
294
295     XSync(display, false); // Make sure that the server knows about the Drawable.
296 }
297
298 void NetscapePlugin::platformVisibilityDidChange()
299 {
300     notImplemented();
301 }
302
303 void NetscapePlugin::platformPaint(GraphicsContext* context, const IntRect& dirtyRect, bool /*isSnapshot*/)
304 {
305     if (m_isWindowed)
306         return;
307
308     if (!m_isStarted) {
309         // FIXME: we should paint a missing plugin icon.
310         return;
311     }
312
313     if (context->paintingDisabled() || !m_drawable)
314         return;
315
316     XEvent xevent;
317     memset(&xevent, 0, sizeof(XEvent));
318     XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose;
319     exposeEvent.type = GraphicsExpose;
320     exposeEvent.display = x11HostDisplay();
321     exposeEvent.drawable = m_drawable;
322
323     IntRect exposedRect(dirtyRect);
324     exposeEvent.x = exposedRect.x();
325     exposeEvent.y = exposedRect.y();
326
327     // Note: in transparent mode Flash thinks width is the right and height is the bottom.
328     // We should take it into account if we want to support transparency.
329     exposeEvent.width = exposedRect.width();
330     exposeEvent.height = exposedRect.height();
331
332     NPP_HandleEvent(&xevent);
333
334     if (m_pluginDisplay != x11HostDisplay())
335         XSync(m_pluginDisplay, false);
336
337 #if PLATFORM(GTK) || (PLATFORM(EFL) && USE(CAIRO))
338     RefPtr<cairo_surface_t> drawableSurface = adoptRef(cairo_xlib_surface_create(m_pluginDisplay,
339                                                                                  m_drawable,
340                                                                                  static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info)->visual,
341                                                                                  m_pluginSize.width(),
342                                                                                  m_pluginSize.height()));
343     cairo_t* cr = context->platformContext()->cr();
344     cairo_save(cr);
345
346     cairo_set_source_surface(cr, drawableSurface.get(), 0, 0);
347
348     cairo_rectangle(cr, exposedRect.x(), exposedRect.y(), exposedRect.width(), exposedRect.height());
349     cairo_clip(cr);
350     cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
351     cairo_paint(cr);
352
353     cairo_restore(cr);
354 #else
355     notImplemented();
356 #endif
357 }
358
359 static inline void initializeXEvent(XEvent& event)
360 {
361     memset(&event, 0, sizeof(XEvent));
362     event.xany.serial = 0;
363     event.xany.send_event = false;
364     event.xany.display = NetscapePlugin::x11HostDisplay();
365     event.xany.window = 0;
366 }
367
368 static inline uint64_t xTimeStamp(double timestampInSeconds)
369 {
370     return timestampInSeconds * 1000;
371 }
372
373 static inline unsigned xKeyModifiers(const WebEvent& event)
374 {
375     unsigned xModifiers = 0;
376     if (event.controlKey())
377         xModifiers |= ControlMask;
378     if (event.shiftKey())
379         xModifiers |= ShiftMask;
380     if (event.altKey())
381         xModifiers |= Mod1Mask;
382     if (event.metaKey())
383         xModifiers |= Mod4Mask;
384
385     return xModifiers;
386 }
387
388 template <typename XEventType, typename WebEventType>
389 static inline void setCommonMouseEventFields(XEventType& xEvent, const WebEventType& webEvent, const WebCore::IntPoint& pluginLocation)
390 {
391     xEvent.root = rootWindowID();
392     xEvent.subwindow = 0;
393     xEvent.time = xTimeStamp(webEvent.timestamp());
394     xEvent.x = webEvent.position().x() - pluginLocation.x();
395     xEvent.y = webEvent.position().y() - pluginLocation.y();
396     xEvent.x_root = webEvent.globalPosition().x();
397     xEvent.y_root = webEvent.globalPosition().y();
398     xEvent.state = xKeyModifiers(webEvent);
399     xEvent.same_screen = true;
400 }
401
402 static inline void setXMotionEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation)
403 {
404     XMotionEvent& xMotion = xEvent.xmotion;
405     setCommonMouseEventFields(xMotion, webEvent, pluginLocation);
406     xMotion.type = MotionNotify;
407 }
408
409 static inline void setXButtonEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation)
410 {
411     XButtonEvent& xButton = xEvent.xbutton;
412     setCommonMouseEventFields(xButton, webEvent, pluginLocation);
413
414     xButton.type = (webEvent.type() == WebEvent::MouseDown) ? ButtonPress : ButtonRelease;
415     switch (webEvent.button()) {
416     case WebMouseEvent::LeftButton:
417         xButton.button = Button1;
418         break;
419     case WebMouseEvent::MiddleButton:
420         xButton.button = Button2;
421         break;
422     case WebMouseEvent::RightButton:
423         xButton.button = Button3;
424         break;
425     default:
426         ASSERT_NOT_REACHED();
427         break;
428     }
429 }
430
431 static inline void setXButtonEventFieldsByWebWheelEvent(XEvent& xEvent, const WebWheelEvent& webEvent, const WebCore::IntPoint& pluginLocation)
432 {
433     XButtonEvent& xButton = xEvent.xbutton;
434     setCommonMouseEventFields(xButton, webEvent, pluginLocation);
435
436     xButton.type = ButtonPress;
437     FloatSize ticks = webEvent.wheelTicks();
438     if (ticks.height()) {
439         if (ticks.height() > 0)
440             xButton.button = 4; // up
441         else
442             xButton.button = 5; // down
443     } else {
444         if (ticks.width() > 0)
445             xButton.button = 6; // left
446         else
447             xButton.button = 7; // right
448     }
449 }
450
451 static inline void setXCrossingEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation, int type)
452 {
453     XCrossingEvent& xCrossing = xEvent.xcrossing;
454     setCommonMouseEventFields(xCrossing, webEvent, pluginLocation);
455
456     xCrossing.type = type;
457     xCrossing.mode = NotifyNormal;
458     xCrossing.detail = NotifyDetailNone;
459     xCrossing.focus = false;
460 }
461
462 bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& event)
463 {
464     if (m_isWindowed)
465         return false;
466
467     if ((event.type() == WebEvent::MouseDown || event.type() == WebEvent::MouseUp)
468          && event.button() == WebMouseEvent::RightButton
469          && quirks().contains(PluginQuirks::IgnoreRightClickInWindowlessMode))
470         return false;
471
472     XEvent xEvent;
473     initializeXEvent(xEvent);
474
475     switch (event.type()) {
476     case WebEvent::MouseDown:
477     case WebEvent::MouseUp:
478         setXButtonEventFields(xEvent, event, convertToRootView(IntPoint()));
479         break;
480     case WebEvent::MouseMove:
481         setXMotionEventFields(xEvent, event, convertToRootView(IntPoint()));
482         break;
483     case WebEvent::NoType:
484     case WebEvent::Wheel:
485     case WebEvent::KeyDown:
486     case WebEvent::KeyUp:
487     case WebEvent::RawKeyDown:
488     case WebEvent::Char:
489 #if ENABLE(TOUCH_EVENTS)
490     case WebEvent::TouchStart:
491     case WebEvent::TouchMove:
492     case WebEvent::TouchEnd:
493     case WebEvent::TouchCancel:
494 #endif
495         return false;
496     }
497
498     return !NPP_HandleEvent(&xEvent);
499 }
500
501 // We undefine these constants in npruntime_internal.h to avoid collision
502 // with WebKit and platform headers. Values are defined in X.h.
503 const int kKeyPressType = 2;
504 const int kKeyReleaseType = 3;
505 const int kFocusInType = 9;
506 const int kFocusOutType = 10;
507
508 bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent& event)
509 {
510     if (m_isWindowed)
511         return false;
512
513     XEvent xEvent;
514     initializeXEvent(xEvent);
515     setXButtonEventFieldsByWebWheelEvent(xEvent, event, convertToRootView(IntPoint()));
516
517     return !NPP_HandleEvent(&xEvent);
518 }
519
520 void NetscapePlugin::platformSetFocus(bool focusIn)
521 {
522     if (m_isWindowed)
523         return;
524
525     XEvent xEvent;
526     initializeXEvent(xEvent);
527     XFocusChangeEvent& focusEvent = xEvent.xfocus;
528     focusEvent.type = focusIn ? kFocusInType : kFocusOutType;
529     focusEvent.mode = NotifyNormal;
530     focusEvent.detail = NotifyDetailNone;
531
532     NPP_HandleEvent(&xEvent);
533 }
534
535 bool NetscapePlugin::wantsPluginRelativeNPWindowCoordinates()
536 {
537     return true;
538 }
539
540 bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& event)
541 {
542     if (m_isWindowed)
543         return false;
544
545     XEvent xEvent;
546     initializeXEvent(xEvent);
547     setXCrossingEventFields(xEvent, event, convertToRootView(IntPoint()), EnterNotify);
548
549     return !NPP_HandleEvent(&xEvent);
550 }
551
552 bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& event)
553 {
554     if (m_isWindowed)
555         return false;
556
557     XEvent xEvent;
558     initializeXEvent(xEvent);
559     setXCrossingEventFields(xEvent, event, convertToRootView(IntPoint()), LeaveNotify);
560
561     return !NPP_HandleEvent(&xEvent);
562 }
563
564 static inline void setXKeyEventFields(XEvent& xEvent, const WebKeyboardEvent& webEvent)
565 {
566     xEvent.xany.type = (webEvent.type() == WebEvent::KeyDown) ? kKeyPressType : kKeyReleaseType;
567     XKeyEvent& xKey = xEvent.xkey;
568     xKey.root = rootWindowID();
569     xKey.subwindow = 0;
570     xKey.time = xTimeStamp(webEvent.timestamp());
571     xKey.state = xKeyModifiers(webEvent);
572     xKey.keycode = webEvent.nativeVirtualKeyCode();
573
574     xKey.same_screen = true;
575
576     // Key events propagated to the plugin does not need to have position.
577     // source: https://developer.mozilla.org/en/NPEvent
578     xKey.x = 0;
579     xKey.y = 0;
580     xKey.x_root = 0;
581     xKey.y_root = 0;
582 }
583
584 bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent& event)
585 {
586     // We don't generate other types of keyboard events via WebEventFactory.
587     ASSERT(event.type() == WebEvent::KeyDown || event.type() == WebEvent::KeyUp);
588
589     XEvent xEvent;
590     initializeXEvent(xEvent);
591     setXKeyEventFields(xEvent, event);
592
593     return !NPP_HandleEvent(&xEvent);
594 }
595
596 } // namespace WebKit
597
598 #endif // PLUGIN_ARCHITECTURE(X11) && ENABLE(NETSCAPE_PLUGIN_API)