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