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 54using namespace WebCore; 55 56namespace WebKit { 57 58static 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 71static 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 82static 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 93static 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) 105static 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 115Display* 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) 127static 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 137bool 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 177bool 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 209void NetscapePlugin::platformPreInitialize() 210{ 211} 212 213bool 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 247void 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 267bool NetscapePlugin::platformInvalidate(const IntRect&) 268{ 269 notImplemented(); 270 return false; 271} 272 273void 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 298void NetscapePlugin::platformVisibilityDidChange() 299{ 300 if (!m_isWindowed) 301 return; 302 303 uint64_t windowID = 0; 304#if PLATFORM(GTK) 305 windowID = static_cast<uint64_t>(GDK_WINDOW_XID(gtk_plug_get_socket_window(GTK_PLUG(m_platformPluginWidget)))); 306#endif 307 controller()->windowedPluginVisibilityDidChange(m_isVisible, windowID); 308 controller()->windowedPluginGeometryDidChange(m_frameRectInWindowCoordinates, m_clipRect, windowID); 309} 310 311void NetscapePlugin::platformPaint(GraphicsContext* context, const IntRect& dirtyRect, bool /*isSnapshot*/) 312{ 313 if (m_isWindowed) 314 return; 315 316 if (!m_isStarted) { 317 // FIXME: we should paint a missing plugin icon. 318 return; 319 } 320 321 if (context->paintingDisabled() || !m_drawable) 322 return; 323 324 XEvent xevent; 325 memset(&xevent, 0, sizeof(XEvent)); 326 XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; 327 exposeEvent.type = GraphicsExpose; 328 exposeEvent.display = x11HostDisplay(); 329 exposeEvent.drawable = m_drawable; 330 331 IntRect exposedRect(dirtyRect); 332 exposeEvent.x = exposedRect.x(); 333 exposeEvent.y = exposedRect.y(); 334 335 // Note: in transparent mode Flash thinks width is the right and height is the bottom. 336 // We should take it into account if we want to support transparency. 337 exposeEvent.width = exposedRect.width(); 338 exposeEvent.height = exposedRect.height(); 339 340 NPP_HandleEvent(&xevent); 341 342 if (m_pluginDisplay != x11HostDisplay()) 343 XSync(m_pluginDisplay, false); 344 345#if PLATFORM(GTK) || (PLATFORM(EFL) && USE(CAIRO)) 346 RefPtr<cairo_surface_t> drawableSurface = adoptRef(cairo_xlib_surface_create(m_pluginDisplay, 347 m_drawable, 348 static_cast<NPSetWindowCallbackStruct*>(m_npWindow.ws_info)->visual, 349 m_pluginSize.width(), 350 m_pluginSize.height())); 351 cairo_t* cr = context->platformContext()->cr(); 352 cairo_save(cr); 353 354 cairo_set_source_surface(cr, drawableSurface.get(), 0, 0); 355 356 cairo_rectangle(cr, exposedRect.x(), exposedRect.y(), exposedRect.width(), exposedRect.height()); 357 cairo_clip(cr); 358 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); 359 cairo_paint(cr); 360 361 cairo_restore(cr); 362#else 363 notImplemented(); 364#endif 365} 366 367static inline void initializeXEvent(XEvent& event) 368{ 369 memset(&event, 0, sizeof(XEvent)); 370 event.xany.serial = 0; 371 event.xany.send_event = false; 372 event.xany.display = NetscapePlugin::x11HostDisplay(); 373 event.xany.window = 0; 374} 375 376static inline uint64_t xTimeStamp(double timestampInSeconds) 377{ 378 return timestampInSeconds * 1000; 379} 380 381static inline unsigned xKeyModifiers(const WebEvent& event) 382{ 383 unsigned xModifiers = 0; 384 if (event.controlKey()) 385 xModifiers |= ControlMask; 386 if (event.shiftKey()) 387 xModifiers |= ShiftMask; 388 if (event.altKey()) 389 xModifiers |= Mod1Mask; 390 if (event.metaKey()) 391 xModifiers |= Mod4Mask; 392 393 return xModifiers; 394} 395 396template <typename XEventType, typename WebEventType> 397static inline void setCommonMouseEventFields(XEventType& xEvent, const WebEventType& webEvent, const WebCore::IntPoint& pluginLocation) 398{ 399 xEvent.root = rootWindowID(); 400 xEvent.subwindow = 0; 401 xEvent.time = xTimeStamp(webEvent.timestamp()); 402 xEvent.x = webEvent.position().x() - pluginLocation.x(); 403 xEvent.y = webEvent.position().y() - pluginLocation.y(); 404 xEvent.x_root = webEvent.globalPosition().x(); 405 xEvent.y_root = webEvent.globalPosition().y(); 406 xEvent.state = xKeyModifiers(webEvent); 407 xEvent.same_screen = true; 408} 409 410static inline void setXMotionEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation) 411{ 412 XMotionEvent& xMotion = xEvent.xmotion; 413 setCommonMouseEventFields(xMotion, webEvent, pluginLocation); 414 xMotion.type = MotionNotify; 415} 416 417static inline void setXButtonEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation) 418{ 419 XButtonEvent& xButton = xEvent.xbutton; 420 setCommonMouseEventFields(xButton, webEvent, pluginLocation); 421 422 xButton.type = (webEvent.type() == WebEvent::MouseDown) ? ButtonPress : ButtonRelease; 423 switch (webEvent.button()) { 424 case WebMouseEvent::LeftButton: 425 xButton.button = Button1; 426 break; 427 case WebMouseEvent::MiddleButton: 428 xButton.button = Button2; 429 break; 430 case WebMouseEvent::RightButton: 431 xButton.button = Button3; 432 break; 433 default: 434 ASSERT_NOT_REACHED(); 435 break; 436 } 437} 438 439static inline void setXButtonEventFieldsByWebWheelEvent(XEvent& xEvent, const WebWheelEvent& webEvent, const WebCore::IntPoint& pluginLocation) 440{ 441 XButtonEvent& xButton = xEvent.xbutton; 442 setCommonMouseEventFields(xButton, webEvent, pluginLocation); 443 444 xButton.type = ButtonPress; 445 FloatSize ticks = webEvent.wheelTicks(); 446 if (ticks.height()) { 447 if (ticks.height() > 0) 448 xButton.button = 4; // up 449 else 450 xButton.button = 5; // down 451 } else { 452 if (ticks.width() > 0) 453 xButton.button = 6; // left 454 else 455 xButton.button = 7; // right 456 } 457} 458 459static inline void setXCrossingEventFields(XEvent& xEvent, const WebMouseEvent& webEvent, const WebCore::IntPoint& pluginLocation, int type) 460{ 461 XCrossingEvent& xCrossing = xEvent.xcrossing; 462 setCommonMouseEventFields(xCrossing, webEvent, pluginLocation); 463 464 xCrossing.type = type; 465 xCrossing.mode = NotifyNormal; 466 xCrossing.detail = NotifyDetailNone; 467 xCrossing.focus = false; 468} 469 470bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& event) 471{ 472 if (m_isWindowed) 473 return false; 474 475 if ((event.type() == WebEvent::MouseDown || event.type() == WebEvent::MouseUp) 476 && event.button() == WebMouseEvent::RightButton 477 && quirks().contains(PluginQuirks::IgnoreRightClickInWindowlessMode)) 478 return false; 479 480 XEvent xEvent; 481 initializeXEvent(xEvent); 482 483 switch (event.type()) { 484 case WebEvent::MouseDown: 485 case WebEvent::MouseUp: 486 setXButtonEventFields(xEvent, event, convertToRootView(IntPoint())); 487 break; 488 case WebEvent::MouseMove: 489 setXMotionEventFields(xEvent, event, convertToRootView(IntPoint())); 490 break; 491 case WebEvent::NoType: 492 case WebEvent::Wheel: 493 case WebEvent::KeyDown: 494 case WebEvent::KeyUp: 495 case WebEvent::RawKeyDown: 496 case WebEvent::Char: 497#if ENABLE(TOUCH_EVENTS) 498 case WebEvent::TouchStart: 499 case WebEvent::TouchMove: 500 case WebEvent::TouchEnd: 501 case WebEvent::TouchCancel: 502#endif 503 return false; 504 } 505 506 return !NPP_HandleEvent(&xEvent); 507} 508 509// We undefine these constants in npruntime_internal.h to avoid collision 510// with WebKit and platform headers. Values are defined in X.h. 511const int kKeyPressType = 2; 512const int kKeyReleaseType = 3; 513const int kFocusInType = 9; 514const int kFocusOutType = 10; 515 516bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent& event) 517{ 518 if (m_isWindowed) 519 return false; 520 521 XEvent xEvent; 522 initializeXEvent(xEvent); 523 setXButtonEventFieldsByWebWheelEvent(xEvent, event, convertToRootView(IntPoint())); 524 525 return !NPP_HandleEvent(&xEvent); 526} 527 528void NetscapePlugin::platformSetFocus(bool focusIn) 529{ 530 if (m_isWindowed) 531 return; 532 533 XEvent xEvent; 534 initializeXEvent(xEvent); 535 XFocusChangeEvent& focusEvent = xEvent.xfocus; 536 focusEvent.type = focusIn ? kFocusInType : kFocusOutType; 537 focusEvent.mode = NotifyNormal; 538 focusEvent.detail = NotifyDetailNone; 539 540 NPP_HandleEvent(&xEvent); 541} 542 543bool NetscapePlugin::wantsPluginRelativeNPWindowCoordinates() 544{ 545 return true; 546} 547 548bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& event) 549{ 550 if (m_isWindowed) 551 return false; 552 553 XEvent xEvent; 554 initializeXEvent(xEvent); 555 setXCrossingEventFields(xEvent, event, convertToRootView(IntPoint()), EnterNotify); 556 557 return !NPP_HandleEvent(&xEvent); 558} 559 560bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& event) 561{ 562 if (m_isWindowed) 563 return false; 564 565 XEvent xEvent; 566 initializeXEvent(xEvent); 567 setXCrossingEventFields(xEvent, event, convertToRootView(IntPoint()), LeaveNotify); 568 569 return !NPP_HandleEvent(&xEvent); 570} 571 572static inline void setXKeyEventFields(XEvent& xEvent, const WebKeyboardEvent& webEvent) 573{ 574 xEvent.xany.type = (webEvent.type() == WebEvent::KeyDown) ? kKeyPressType : kKeyReleaseType; 575 XKeyEvent& xKey = xEvent.xkey; 576 xKey.root = rootWindowID(); 577 xKey.subwindow = 0; 578 xKey.time = xTimeStamp(webEvent.timestamp()); 579 xKey.state = xKeyModifiers(webEvent); 580 xKey.keycode = webEvent.nativeVirtualKeyCode(); 581 582 xKey.same_screen = true; 583 584 // Key events propagated to the plugin does not need to have position. 585 // source: https://developer.mozilla.org/en/NPEvent 586 xKey.x = 0; 587 xKey.y = 0; 588 xKey.x_root = 0; 589 xKey.y_root = 0; 590} 591 592bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent& event) 593{ 594 // We don't generate other types of keyboard events via WebEventFactory. 595 ASSERT(event.type() == WebEvent::KeyDown || event.type() == WebEvent::KeyUp); 596 597 XEvent xEvent; 598 initializeXEvent(xEvent); 599 setXKeyEventFields(xEvent, event); 600 601 return !NPP_HandleEvent(&xEvent); 602} 603 604} // namespace WebKit 605 606#endif // PLUGIN_ARCHITECTURE(X11) && ENABLE(NETSCAPE_PLUGIN_API) 607