1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/plugins/test/testplugin/nptest_gtk2.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,769 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* ***** BEGIN LICENSE BLOCK ***** 1.6 + * 1.7 + * Copyright (c) 2008, Mozilla Corporation 1.8 + * All rights reserved. 1.9 + * 1.10 + * Redistribution and use in source and binary forms, with or without 1.11 + * modification, are permitted provided that the following conditions are met: 1.12 + * 1.13 + * * Redistributions of source code must retain the above copyright notice, this 1.14 + * list of conditions and the following disclaimer. 1.15 + * * Redistributions in binary form must reproduce the above copyright notice, 1.16 + * this list of conditions and the following disclaimer in the documentation 1.17 + * and/or other materials provided with the distribution. 1.18 + * * Neither the name of the Mozilla Corporation nor the names of its 1.19 + * contributors may be used to endorse or promote products derived from this 1.20 + * software without specific prior written permission. 1.21 + * 1.22 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 1.23 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1.24 + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 1.25 + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 1.26 + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 1.27 + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 1.28 + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 1.29 + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.30 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 1.31 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.32 + * 1.33 + * Contributor(s): 1.34 + * Josh Aas <josh@mozilla.com> 1.35 + * Michael Ventnor <mventnor@mozilla.com> 1.36 + * 1.37 + * ***** END LICENSE BLOCK ***** */ 1.38 + 1.39 +#include "nptest_platform.h" 1.40 +#include "npapi.h" 1.41 +#include <pthread.h> 1.42 +#include <gdk/gdk.h> 1.43 +#ifdef MOZ_X11 1.44 +#include <gdk/gdkx.h> 1.45 +#include <X11/extensions/shape.h> 1.46 +#endif 1.47 +#include <glib.h> 1.48 +#include <gtk/gtk.h> 1.49 +#include <unistd.h> 1.50 + 1.51 +#include "mozilla/NullPtr.h" 1.52 +#include "mozilla/IntentionalCrash.h" 1.53 + 1.54 + using namespace std; 1.55 + 1.56 +struct _PlatformData { 1.57 +#ifdef MOZ_X11 1.58 + Display* display; 1.59 + Visual* visual; 1.60 + Colormap colormap; 1.61 +#endif 1.62 + GtkWidget* plug; 1.63 +}; 1.64 + 1.65 +bool 1.66 +pluginSupportsWindowMode() 1.67 +{ 1.68 + return true; 1.69 +} 1.70 + 1.71 +bool 1.72 +pluginSupportsWindowlessMode() 1.73 +{ 1.74 + return true; 1.75 +} 1.76 + 1.77 +bool 1.78 +pluginSupportsAsyncBitmapDrawing() 1.79 +{ 1.80 + return false; 1.81 +} 1.82 + 1.83 +NPError 1.84 +pluginInstanceInit(InstanceData* instanceData) 1.85 +{ 1.86 +#ifdef MOZ_X11 1.87 + instanceData->platformData = static_cast<PlatformData*> 1.88 + (NPN_MemAlloc(sizeof(PlatformData))); 1.89 + if (!instanceData->platformData) 1.90 + return NPERR_OUT_OF_MEMORY_ERROR; 1.91 + 1.92 + instanceData->platformData->display = nullptr; 1.93 + instanceData->platformData->visual = nullptr; 1.94 + instanceData->platformData->colormap = None; 1.95 + instanceData->platformData->plug = nullptr; 1.96 + 1.97 + return NPERR_NO_ERROR; 1.98 +#else 1.99 + // we only support X11 here, since thats what the plugin system uses 1.100 + return NPERR_INCOMPATIBLE_VERSION_ERROR; 1.101 +#endif 1.102 +} 1.103 + 1.104 +void 1.105 +pluginInstanceShutdown(InstanceData* instanceData) 1.106 +{ 1.107 + if (instanceData->hasWidget) { 1.108 + Window window = reinterpret_cast<XID>(instanceData->window.window); 1.109 + 1.110 + if (window != None) { 1.111 + // This window XID should still be valid. 1.112 + // See bug 429604 and bug 454756. 1.113 + XWindowAttributes attributes; 1.114 + if (!XGetWindowAttributes(instanceData->platformData->display, window, 1.115 + &attributes)) 1.116 + g_error("XGetWindowAttributes failed at plugin instance shutdown"); 1.117 + } 1.118 + } 1.119 + 1.120 + GtkWidget* plug = instanceData->platformData->plug; 1.121 + if (plug) { 1.122 + instanceData->platformData->plug = 0; 1.123 + if (instanceData->cleanupWidget) { 1.124 + // Default/tidy behavior 1.125 + gtk_widget_destroy(plug); 1.126 + } else { 1.127 + // Flash Player style: let the GtkPlug destroy itself on disconnect. 1.128 + g_signal_handlers_disconnect_matched(plug, G_SIGNAL_MATCH_DATA, 0, 0, 1.129 + nullptr, nullptr, instanceData); 1.130 + } 1.131 + } 1.132 + 1.133 + NPN_MemFree(instanceData->platformData); 1.134 + instanceData->platformData = 0; 1.135 +} 1.136 + 1.137 +static void 1.138 +SetCairoRGBA(cairo_t* cairoWindow, uint32_t rgba) 1.139 +{ 1.140 + float b = (rgba & 0xFF) / 255.0; 1.141 + float g = ((rgba & 0xFF00) >> 8) / 255.0; 1.142 + float r = ((rgba & 0xFF0000) >> 16) / 255.0; 1.143 + float a = ((rgba & 0xFF000000) >> 24) / 255.0; 1.144 + 1.145 + cairo_set_source_rgba(cairoWindow, r, g, b, a); 1.146 +} 1.147 + 1.148 +static void 1.149 +pluginDrawSolid(InstanceData* instanceData, GdkDrawable* gdkWindow, 1.150 + int x, int y, int width, int height) 1.151 +{ 1.152 + cairo_t* cairoWindow = gdk_cairo_create(gdkWindow); 1.153 + 1.154 + if (!instanceData->hasWidget) { 1.155 + NPRect* clip = &instanceData->window.clipRect; 1.156 + cairo_rectangle(cairoWindow, clip->left, clip->top, 1.157 + clip->right - clip->left, clip->bottom - clip->top); 1.158 + cairo_clip(cairoWindow); 1.159 + } 1.160 + 1.161 + GdkRectangle windowRect = { x, y, width, height }; 1.162 + gdk_cairo_rectangle(cairoWindow, &windowRect); 1.163 + SetCairoRGBA(cairoWindow, instanceData->scriptableObject->drawColor); 1.164 + 1.165 + cairo_fill(cairoWindow); 1.166 + cairo_destroy(cairoWindow); 1.167 +} 1.168 + 1.169 +static void 1.170 +pluginDrawWindow(InstanceData* instanceData, GdkDrawable* gdkWindow, 1.171 + const GdkRectangle& invalidRect) 1.172 +{ 1.173 + NPWindow& window = instanceData->window; 1.174 + // When we have a widget, window.x/y are meaningless since our 1.175 + // widget is always positioned correctly and we just draw into it at 0,0 1.176 + int x = instanceData->hasWidget ? 0 : window.x; 1.177 + int y = instanceData->hasWidget ? 0 : window.y; 1.178 + int width = window.width; 1.179 + int height = window.height; 1.180 + 1.181 + notifyDidPaint(instanceData); 1.182 + 1.183 + if (instanceData->scriptableObject->drawMode == DM_SOLID_COLOR) { 1.184 + // drawing a solid color for reftests 1.185 + pluginDrawSolid(instanceData, gdkWindow, 1.186 + invalidRect.x, invalidRect.y, 1.187 + invalidRect.width, invalidRect.height); 1.188 + return; 1.189 + } 1.190 + 1.191 + NPP npp = instanceData->npp; 1.192 + if (!npp) 1.193 + return; 1.194 + 1.195 + const char* uaString = NPN_UserAgent(npp); 1.196 + if (!uaString) 1.197 + return; 1.198 + 1.199 + GdkGC* gdkContext = gdk_gc_new(gdkWindow); 1.200 + if (!gdkContext) 1.201 + return; 1.202 + 1.203 + if (!instanceData->hasWidget) { 1.204 + NPRect* clip = &window.clipRect; 1.205 + GdkRectangle gdkClip = { clip->left, clip->top, clip->right - clip->left, 1.206 + clip->bottom - clip->top }; 1.207 + gdk_gc_set_clip_rectangle(gdkContext, &gdkClip); 1.208 + } 1.209 + 1.210 + // draw a grey background for the plugin frame 1.211 + GdkColor grey; 1.212 + grey.red = grey.blue = grey.green = 32767; 1.213 + gdk_gc_set_rgb_fg_color(gdkContext, &grey); 1.214 + gdk_draw_rectangle(gdkWindow, gdkContext, TRUE, x, y, width, height); 1.215 + 1.216 + // draw a 3-pixel-thick black frame around the plugin 1.217 + GdkColor black; 1.218 + black.red = black.green = black.blue = 0; 1.219 + gdk_gc_set_rgb_fg_color(gdkContext, &black); 1.220 + gdk_gc_set_line_attributes(gdkContext, 3, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER); 1.221 + gdk_draw_rectangle(gdkWindow, gdkContext, FALSE, x + 1, y + 1, 1.222 + width - 3, height - 3); 1.223 + 1.224 + // paint the UA string 1.225 + PangoContext* pangoContext = gdk_pango_context_get(); 1.226 + PangoLayout* pangoTextLayout = pango_layout_new(pangoContext); 1.227 + pango_layout_set_width(pangoTextLayout, (width - 10) * PANGO_SCALE); 1.228 + pango_layout_set_text(pangoTextLayout, uaString, -1); 1.229 + gdk_draw_layout(gdkWindow, gdkContext, x + 5, y + 5, pangoTextLayout); 1.230 + g_object_unref(pangoTextLayout); 1.231 + 1.232 + g_object_unref(gdkContext); 1.233 +} 1.234 + 1.235 +static gboolean 1.236 +ExposeWidget(GtkWidget* widget, GdkEventExpose* event, 1.237 + gpointer user_data) 1.238 +{ 1.239 + InstanceData* instanceData = static_cast<InstanceData*>(user_data); 1.240 + pluginDrawWindow(instanceData, event->window, event->area); 1.241 + return TRUE; 1.242 +} 1.243 + 1.244 +static gboolean 1.245 +MotionEvent(GtkWidget* widget, GdkEventMotion* event, 1.246 + gpointer user_data) 1.247 +{ 1.248 + InstanceData* instanceData = static_cast<InstanceData*>(user_data); 1.249 + instanceData->lastMouseX = event->x; 1.250 + instanceData->lastMouseY = event->y; 1.251 + return TRUE; 1.252 +} 1.253 + 1.254 +static gboolean 1.255 +ButtonEvent(GtkWidget* widget, GdkEventButton* event, 1.256 + gpointer user_data) 1.257 +{ 1.258 + InstanceData* instanceData = static_cast<InstanceData*>(user_data); 1.259 + instanceData->lastMouseX = event->x; 1.260 + instanceData->lastMouseY = event->y; 1.261 + if (event->type == GDK_BUTTON_RELEASE) { 1.262 + instanceData->mouseUpEventCount++; 1.263 + } 1.264 + return TRUE; 1.265 +} 1.266 + 1.267 +static gboolean 1.268 +DeleteWidget(GtkWidget* widget, GdkEvent* event, gpointer user_data) 1.269 +{ 1.270 + InstanceData* instanceData = static_cast<InstanceData*>(user_data); 1.271 + // Some plugins do not expect the plug to be removed from the socket before 1.272 + // the plugin instance is destroyed. e.g. bug 485125 1.273 + if (instanceData->platformData->plug) 1.274 + g_error("plug removed"); // this aborts 1.275 + 1.276 + return FALSE; 1.277 +} 1.278 + 1.279 +void 1.280 +pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow) 1.281 +{ 1.282 + instanceData->window = *newWindow; 1.283 + 1.284 +#ifdef MOZ_X11 1.285 + NPSetWindowCallbackStruct *ws_info = 1.286 + static_cast<NPSetWindowCallbackStruct*>(newWindow->ws_info); 1.287 + instanceData->platformData->display = ws_info->display; 1.288 + instanceData->platformData->visual = ws_info->visual; 1.289 + instanceData->platformData->colormap = ws_info->colormap; 1.290 +#endif 1.291 +} 1.292 + 1.293 +void 1.294 +pluginWidgetInit(InstanceData* instanceData, void* oldWindow) 1.295 +{ 1.296 +#ifdef MOZ_X11 1.297 + GtkWidget* oldPlug = instanceData->platformData->plug; 1.298 + if (oldPlug) { 1.299 + instanceData->platformData->plug = 0; 1.300 + gtk_widget_destroy(oldPlug); 1.301 + } 1.302 + 1.303 + GdkNativeWindow nativeWinId = 1.304 + reinterpret_cast<XID>(instanceData->window.window); 1.305 + 1.306 + /* create a GtkPlug container */ 1.307 + GtkWidget* plug = gtk_plug_new(nativeWinId); 1.308 + 1.309 + // Test for bugs 539138 and 561308 1.310 + if (!plug->window) 1.311 + g_error("Plug has no window"); // aborts 1.312 + 1.313 + /* make sure the widget is capable of receiving focus */ 1.314 + GTK_WIDGET_SET_FLAGS (GTK_WIDGET(plug), GTK_CAN_FOCUS); 1.315 + 1.316 + /* all the events that our widget wants to receive */ 1.317 + gtk_widget_add_events(plug, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | 1.318 + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); 1.319 + g_signal_connect(plug, "expose-event", G_CALLBACK(ExposeWidget), 1.320 + instanceData); 1.321 + g_signal_connect(plug, "motion_notify_event", G_CALLBACK(MotionEvent), 1.322 + instanceData); 1.323 + g_signal_connect(plug, "button_press_event", G_CALLBACK(ButtonEvent), 1.324 + instanceData); 1.325 + g_signal_connect(plug, "button_release_event", G_CALLBACK(ButtonEvent), 1.326 + instanceData); 1.327 + g_signal_connect(plug, "delete-event", G_CALLBACK(DeleteWidget), 1.328 + instanceData); 1.329 + gtk_widget_show(plug); 1.330 + 1.331 + instanceData->platformData->plug = plug; 1.332 +#endif 1.333 +} 1.334 + 1.335 +int16_t 1.336 +pluginHandleEvent(InstanceData* instanceData, void* event) 1.337 +{ 1.338 +#ifdef MOZ_X11 1.339 + XEvent* nsEvent = (XEvent*)event; 1.340 + 1.341 + switch (nsEvent->type) { 1.342 + case GraphicsExpose: { 1.343 + const XGraphicsExposeEvent& expose = nsEvent->xgraphicsexpose; 1.344 + NPWindow& window = instanceData->window; 1.345 + window.window = (void*)(expose.drawable); 1.346 + 1.347 + GdkNativeWindow nativeWinId = reinterpret_cast<XID>(window.window); 1.348 + 1.349 + GdkDisplay* gdkDisplay = gdk_x11_lookup_xdisplay(expose.display); 1.350 + if (!gdkDisplay) { 1.351 + g_warning("Display not opened by GDK"); 1.352 + return 0; 1.353 + } 1.354 + // gdk_pixmap_foreign_new() doesn't check whether a GdkPixmap already 1.355 + // exists, so check first. 1.356 + // https://bugzilla.gnome.org/show_bug.cgi?id=590690 1.357 + GdkPixmap* gdkDrawable = 1.358 + GDK_DRAWABLE(gdk_pixmap_lookup_for_display(gdkDisplay, nativeWinId)); 1.359 + // If there is no existing GdkPixmap or it doesn't have a colormap then 1.360 + // create our own. 1.361 + if (gdkDrawable) { 1.362 + GdkColormap* gdkColormap = gdk_drawable_get_colormap(gdkDrawable); 1.363 + if (!gdkColormap) { 1.364 + g_warning("No GdkColormap on GdkPixmap"); 1.365 + return 0; 1.366 + } 1.367 + if (gdk_x11_colormap_get_xcolormap(gdkColormap) 1.368 + != instanceData->platformData->colormap) { 1.369 + g_warning("wrong Colormap"); 1.370 + return 0; 1.371 + } 1.372 + if (gdk_x11_visual_get_xvisual(gdk_colormap_get_visual(gdkColormap)) 1.373 + != instanceData->platformData->visual) { 1.374 + g_warning("wrong Visual"); 1.375 + return 0; 1.376 + } 1.377 + g_object_ref(gdkDrawable); 1.378 + } else { 1.379 + gdkDrawable = 1.380 + GDK_DRAWABLE(gdk_pixmap_foreign_new_for_display(gdkDisplay, 1.381 + nativeWinId)); 1.382 + VisualID visualID = instanceData->platformData->visual->visualid; 1.383 + GdkVisual* gdkVisual = 1.384 + gdk_x11_screen_lookup_visual(gdk_drawable_get_screen(gdkDrawable), 1.385 + visualID); 1.386 + GdkColormap* gdkColormap = 1.387 + gdk_x11_colormap_foreign_new(gdkVisual, 1.388 + instanceData->platformData->colormap); 1.389 + gdk_drawable_set_colormap(gdkDrawable, gdkColormap); 1.390 + g_object_unref(gdkColormap); 1.391 + } 1.392 + 1.393 + const NPRect& clip = window.clipRect; 1.394 + if (expose.x < clip.left || expose.y < clip.top || 1.395 + expose.x + expose.width > clip.right || 1.396 + expose.y + expose.height > clip.bottom) { 1.397 + g_warning("expose rectangle (x=%d,y=%d,w=%d,h=%d) not in clip rectangle (l=%d,t=%d,r=%d,b=%d)", 1.398 + expose.x, expose.y, expose.width, expose.height, 1.399 + clip.left, clip.top, clip.right, clip.bottom); 1.400 + return 0; 1.401 + } 1.402 + if (expose.x < window.x || expose.y < window.y || 1.403 + expose.x + expose.width > window.x + int32_t(window.width) || 1.404 + expose.y + expose.height > window.y + int32_t(window.height)) { 1.405 + g_warning("expose rectangle (x=%d,y=%d,w=%d,h=%d) not in plugin rectangle (x=%d,y=%d,w=%d,h=%d)", 1.406 + expose.x, expose.y, expose.width, expose.height, 1.407 + window.x, window.y, window.width, window.height); 1.408 + return 0; 1.409 + } 1.410 + 1.411 + GdkRectangle invalidRect = 1.412 + { expose.x, expose.y, expose.width, expose.height }; 1.413 + pluginDrawWindow(instanceData, gdkDrawable, invalidRect); 1.414 + g_object_unref(gdkDrawable); 1.415 + break; 1.416 + } 1.417 + case MotionNotify: { 1.418 + XMotionEvent* motion = &nsEvent->xmotion; 1.419 + instanceData->lastMouseX = motion->x; 1.420 + instanceData->lastMouseY = motion->y; 1.421 + break; 1.422 + } 1.423 + case ButtonPress: 1.424 + case ButtonRelease: { 1.425 + XButtonEvent* button = &nsEvent->xbutton; 1.426 + instanceData->lastMouseX = button->x; 1.427 + instanceData->lastMouseY = button->y; 1.428 + if (nsEvent->type == ButtonRelease) { 1.429 + instanceData->mouseUpEventCount++; 1.430 + } 1.431 + break; 1.432 + } 1.433 + default: 1.434 + break; 1.435 + } 1.436 +#endif 1.437 + 1.438 + return 0; 1.439 +} 1.440 + 1.441 +int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge) 1.442 +{ 1.443 + if (!instanceData->hasWidget) 1.444 + return NPTEST_INT32_ERROR; 1.445 + GtkWidget* plug = instanceData->platformData->plug; 1.446 + if (!plug) 1.447 + return NPTEST_INT32_ERROR; 1.448 + GdkWindow* plugWnd = plug->window; 1.449 + if (!plugWnd) 1.450 + return NPTEST_INT32_ERROR; 1.451 + 1.452 + GdkWindow* toplevelGdk = 0; 1.453 +#ifdef MOZ_X11 1.454 + Window toplevel = 0; 1.455 + NPN_GetValue(instanceData->npp, NPNVnetscapeWindow, &toplevel); 1.456 + if (!toplevel) 1.457 + return NPTEST_INT32_ERROR; 1.458 + toplevelGdk = gdk_window_foreign_new(toplevel); 1.459 +#endif 1.460 + if (!toplevelGdk) 1.461 + return NPTEST_INT32_ERROR; 1.462 + 1.463 + GdkRectangle toplevelFrameExtents; 1.464 + gdk_window_get_frame_extents(toplevelGdk, &toplevelFrameExtents); 1.465 + g_object_unref(toplevelGdk); 1.466 + 1.467 + gint pluginWidth, pluginHeight; 1.468 + gdk_drawable_get_size(GDK_DRAWABLE(plugWnd), &pluginWidth, &pluginHeight); 1.469 + gint pluginOriginX, pluginOriginY; 1.470 + gdk_window_get_origin(plugWnd, &pluginOriginX, &pluginOriginY); 1.471 + gint pluginX = pluginOriginX - toplevelFrameExtents.x; 1.472 + gint pluginY = pluginOriginY - toplevelFrameExtents.y; 1.473 + 1.474 + switch (edge) { 1.475 + case EDGE_LEFT: 1.476 + return pluginX; 1.477 + case EDGE_TOP: 1.478 + return pluginY; 1.479 + case EDGE_RIGHT: 1.480 + return pluginX + pluginWidth; 1.481 + case EDGE_BOTTOM: 1.482 + return pluginY + pluginHeight; 1.483 + } 1.484 + 1.485 + return NPTEST_INT32_ERROR; 1.486 +} 1.487 + 1.488 +#ifdef MOZ_X11 1.489 +static void intersectWithShapeRects(Display* display, Window window, 1.490 + int kind, GdkRegion* region) 1.491 +{ 1.492 + int count = -1, order; 1.493 + XRectangle* shapeRects = 1.494 + XShapeGetRectangles(display, window, kind, &count, &order); 1.495 + // The documentation says that shapeRects will be nullptr when the 1.496 + // extension is not supported. Unfortunately XShapeGetRectangles 1.497 + // also returns nullptr when the region is empty, so we can't treat 1.498 + // nullptr as failure. I hope this way is OK. 1.499 + if (count < 0) 1.500 + return; 1.501 + 1.502 + GdkRegion* shapeRegion = gdk_region_new(); 1.503 + if (!shapeRegion) { 1.504 + XFree(shapeRects); 1.505 + return; 1.506 + } 1.507 + 1.508 + for (int i = 0; i < count; ++i) { 1.509 + XRectangle* r = &shapeRects[i]; 1.510 + GdkRectangle rect = { r->x, r->y, r->width, r->height }; 1.511 + gdk_region_union_with_rect(shapeRegion, &rect); 1.512 + } 1.513 + XFree(shapeRects); 1.514 + 1.515 + gdk_region_intersect(region, shapeRegion); 1.516 + gdk_region_destroy(shapeRegion); 1.517 +} 1.518 +#endif 1.519 + 1.520 +static GdkRegion* computeClipRegion(InstanceData* instanceData) 1.521 +{ 1.522 + if (!instanceData->hasWidget) 1.523 + return 0; 1.524 + 1.525 + GtkWidget* plug = instanceData->platformData->plug; 1.526 + if (!plug) 1.527 + return 0; 1.528 + GdkWindow* plugWnd = plug->window; 1.529 + if (!plugWnd) 1.530 + return 0; 1.531 + 1.532 + gint plugWidth, plugHeight; 1.533 + gdk_drawable_get_size(GDK_DRAWABLE(plugWnd), &plugWidth, &plugHeight); 1.534 + GdkRectangle pluginRect = { 0, 0, plugWidth, plugHeight }; 1.535 + GdkRegion* region = gdk_region_rectangle(&pluginRect); 1.536 + if (!region) 1.537 + return 0; 1.538 + 1.539 + int pluginX = 0, pluginY = 0; 1.540 + 1.541 +#ifdef MOZ_X11 1.542 + Display* display = GDK_WINDOW_XDISPLAY(plugWnd); 1.543 + Window window = GDK_WINDOW_XWINDOW(plugWnd); 1.544 + 1.545 + Window toplevel = 0; 1.546 + NPN_GetValue(instanceData->npp, NPNVnetscapeWindow, &toplevel); 1.547 + if (!toplevel) 1.548 + return 0; 1.549 + 1.550 + for (;;) { 1.551 + Window root; 1.552 + int x, y; 1.553 + unsigned int width, height, border_width, depth; 1.554 + if (!XGetGeometry(display, window, &root, &x, &y, &width, &height, 1.555 + &border_width, &depth)) { 1.556 + gdk_region_destroy(region); 1.557 + return 0; 1.558 + } 1.559 + 1.560 + GdkRectangle windowRect = { 0, 0, static_cast<gint>(width), 1.561 + static_cast<gint>(height) }; 1.562 + GdkRegion* windowRgn = gdk_region_rectangle(&windowRect); 1.563 + if (!windowRgn) { 1.564 + gdk_region_destroy(region); 1.565 + return 0; 1.566 + } 1.567 + intersectWithShapeRects(display, window, ShapeBounding, windowRgn); 1.568 + intersectWithShapeRects(display, window, ShapeClip, windowRgn); 1.569 + gdk_region_offset(windowRgn, -pluginX, -pluginY); 1.570 + gdk_region_intersect(region, windowRgn); 1.571 + gdk_region_destroy(windowRgn); 1.572 + 1.573 + // Stop now if we've reached the toplevel. Stopping here means 1.574 + // clipping performed by the toplevel window is taken into account. 1.575 + if (window == toplevel) 1.576 + break; 1.577 + 1.578 + Window parent; 1.579 + Window* children; 1.580 + unsigned int nchildren; 1.581 + if (!XQueryTree(display, window, &root, &parent, &children, &nchildren)) { 1.582 + gdk_region_destroy(region); 1.583 + return 0; 1.584 + } 1.585 + XFree(children); 1.586 + 1.587 + pluginX += x; 1.588 + pluginY += y; 1.589 + 1.590 + window = parent; 1.591 + } 1.592 +#endif 1.593 + // pluginX and pluginY are now relative to the toplevel. Make them 1.594 + // relative to the window frame top-left. 1.595 + GdkWindow* toplevelGdk = gdk_window_foreign_new(window); 1.596 + if (!toplevelGdk) 1.597 + return 0; 1.598 + GdkRectangle toplevelFrameExtents; 1.599 + gdk_window_get_frame_extents(toplevelGdk, &toplevelFrameExtents); 1.600 + gint toplevelOriginX, toplevelOriginY; 1.601 + gdk_window_get_origin(toplevelGdk, &toplevelOriginX, &toplevelOriginY); 1.602 + g_object_unref(toplevelGdk); 1.603 + 1.604 + pluginX += toplevelOriginX - toplevelFrameExtents.x; 1.605 + pluginY += toplevelOriginY - toplevelFrameExtents.y; 1.606 + 1.607 + gdk_region_offset(region, pluginX, pluginY); 1.608 + return region; 1.609 +} 1.610 + 1.611 +int32_t pluginGetClipRegionRectCount(InstanceData* instanceData) 1.612 +{ 1.613 + GdkRegion* region = computeClipRegion(instanceData); 1.614 + if (!region) 1.615 + return NPTEST_INT32_ERROR; 1.616 + 1.617 + GdkRectangle* rects; 1.618 + gint nrects; 1.619 + gdk_region_get_rectangles(region, &rects, &nrects); 1.620 + gdk_region_destroy(region); 1.621 + g_free(rects); 1.622 + return nrects; 1.623 +} 1.624 + 1.625 +int32_t pluginGetClipRegionRectEdge(InstanceData* instanceData, 1.626 + int32_t rectIndex, RectEdge edge) 1.627 +{ 1.628 + GdkRegion* region = computeClipRegion(instanceData); 1.629 + if (!region) 1.630 + return NPTEST_INT32_ERROR; 1.631 + 1.632 + GdkRectangle* rects; 1.633 + gint nrects; 1.634 + gdk_region_get_rectangles(region, &rects, &nrects); 1.635 + gdk_region_destroy(region); 1.636 + if (rectIndex >= nrects) { 1.637 + g_free(rects); 1.638 + return NPTEST_INT32_ERROR; 1.639 + } 1.640 + 1.641 + GdkRectangle rect = rects[rectIndex]; 1.642 + g_free(rects); 1.643 + 1.644 + switch (edge) { 1.645 + case EDGE_LEFT: 1.646 + return rect.x; 1.647 + case EDGE_TOP: 1.648 + return rect.y; 1.649 + case EDGE_RIGHT: 1.650 + return rect.x + rect.width; 1.651 + case EDGE_BOTTOM: 1.652 + return rect.y + rect.height; 1.653 + } 1.654 + return NPTEST_INT32_ERROR; 1.655 +} 1.656 + 1.657 +void pluginDoInternalConsistencyCheck(InstanceData* instanceData, string& error) 1.658 +{ 1.659 +} 1.660 + 1.661 +string 1.662 +pluginGetClipboardText(InstanceData* instanceData) 1.663 +{ 1.664 + GtkClipboard* cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); 1.665 + // XXX this is a BAD WAY to interact with GtkClipboard. We use this 1.666 + // deprecated interface only to test nested event loop handling. 1.667 + gchar* text = gtk_clipboard_wait_for_text(cb); 1.668 + string retText = text ? text : ""; 1.669 + 1.670 + g_free(text); 1.671 + 1.672 + return retText; 1.673 +} 1.674 + 1.675 +//----------------------------------------------------------------------------- 1.676 +// NB: this test is quite gross in that it's not only 1.677 +// nondeterministic, but dependent on the guts of the nested glib 1.678 +// event loop handling code in PluginModule. We first sleep long 1.679 +// enough to make sure that the "detection timer" will be pending when 1.680 +// we enter the nested glib loop, then similarly for the "process browser 1.681 +// events" timer. Then we "schedule" the crasher thread to run at about the 1.682 +// same time we expect that the PluginModule "process browser events" task 1.683 +// will run. If all goes well, the plugin process will crash and generate the 1.684 +// XPCOM "plugin crashed" task, and the browser will run that task while still 1.685 +// in the "process some events" loop. 1.686 + 1.687 +static void* 1.688 +CrasherThread(void* data) 1.689 +{ 1.690 + // Give the parent thread a chance to send the message. 1.691 + usleep(200); 1.692 + 1.693 + // Exit (without running atexit hooks) rather than crashing with a signal 1.694 + // so as to make timing more reliable. The process terminates immediately 1.695 + // rather than waiting for a thread in the parent process to attach and 1.696 + // generate a minidump. 1.697 + _exit(1); 1.698 + 1.699 + // not reached 1.700 + return(nullptr); 1.701 +} 1.702 + 1.703 +bool 1.704 +pluginCrashInNestedLoop(InstanceData* instanceData) 1.705 +{ 1.706 + // wait at least long enough for nested loop detector task to be pending ... 1.707 + sleep(1); 1.708 + 1.709 + // Run the nested loop detector by processing all events that are waiting. 1.710 + bool found_event = false; 1.711 + while (g_main_context_iteration(nullptr, FALSE)) { 1.712 + found_event = true; 1.713 + } 1.714 + if (!found_event) { 1.715 + g_warning("DetectNestedEventLoop did not fire"); 1.716 + return true; // trigger a test failure 1.717 + } 1.718 + 1.719 + // wait at least long enough for the "process browser events" task to be 1.720 + // pending ... 1.721 + sleep(1); 1.722 + 1.723 + // we'll be crashing soon, note that fact now to avoid messing with 1.724 + // timing too much 1.725 + mozilla::NoteIntentionalCrash("plugin"); 1.726 + 1.727 + // schedule the crasher thread ... 1.728 + pthread_t crasherThread; 1.729 + if (0 != pthread_create(&crasherThread, nullptr, CrasherThread, nullptr)) { 1.730 + g_warning("Failed to create thread"); 1.731 + return true; // trigger a test failure 1.732 + } 1.733 + 1.734 + // .. and hope it crashes at about the same time as the "process browser 1.735 + // events" task (that should run in this loop) is being processed in the 1.736 + // parent. 1.737 + found_event = false; 1.738 + while (g_main_context_iteration(nullptr, FALSE)) { 1.739 + found_event = true; 1.740 + } 1.741 + if (found_event) { 1.742 + g_warning("Should have crashed in ProcessBrowserEvents"); 1.743 + } else { 1.744 + g_warning("ProcessBrowserEvents did not fire"); 1.745 + } 1.746 + 1.747 + // if we get here without crashing, then we'll trigger a test failure 1.748 + return true; 1.749 +} 1.750 + 1.751 +static int 1.752 +SleepThenDie(Display* display) 1.753 +{ 1.754 + mozilla::NoteIntentionalCrash("plugin"); 1.755 + fprintf(stderr, "[testplugin:%d] SleepThenDie: sleeping\n", getpid()); 1.756 + sleep(1); 1.757 + 1.758 + fprintf(stderr, "[testplugin:%d] SleepThenDie: dying\n", getpid()); 1.759 + _exit(1); 1.760 +} 1.761 + 1.762 +bool 1.763 +pluginDestroySharedGfxStuff(InstanceData* instanceData) 1.764 +{ 1.765 + // Closing the X socket results in the gdk error handler being 1.766 + // invoked, which exit()s us. We want to give the parent process a 1.767 + // little while to do whatever it wanted to do, so steal the IO 1.768 + // handler from gdk and set up our own that delays seppuku. 1.769 + XSetIOErrorHandler(SleepThenDie); 1.770 + close(ConnectionNumber(GDK_DISPLAY())); 1.771 + return true; 1.772 +}