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