dom/plugins/test/testplugin/nptest_gtk2.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* ***** BEGIN LICENSE BLOCK *****
michael@0 3 *
michael@0 4 * Copyright (c) 2008, Mozilla Corporation
michael@0 5 * All rights reserved.
michael@0 6 *
michael@0 7 * Redistribution and use in source and binary forms, with or without
michael@0 8 * modification, are permitted provided that the following conditions are met:
michael@0 9 *
michael@0 10 * * Redistributions of source code must retain the above copyright notice, this
michael@0 11 * list of conditions and the following disclaimer.
michael@0 12 * * Redistributions in binary form must reproduce the above copyright notice,
michael@0 13 * this list of conditions and the following disclaimer in the documentation
michael@0 14 * and/or other materials provided with the distribution.
michael@0 15 * * Neither the name of the Mozilla Corporation nor the names of its
michael@0 16 * contributors may be used to endorse or promote products derived from this
michael@0 17 * software without specific prior written permission.
michael@0 18 *
michael@0 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
michael@0 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
michael@0 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
michael@0 22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
michael@0 23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
michael@0 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
michael@0 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
michael@0 26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
michael@0 28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 29 *
michael@0 30 * Contributor(s):
michael@0 31 * Josh Aas <josh@mozilla.com>
michael@0 32 * Michael Ventnor <mventnor@mozilla.com>
michael@0 33 *
michael@0 34 * ***** END LICENSE BLOCK ***** */
michael@0 35
michael@0 36 #include "nptest_platform.h"
michael@0 37 #include "npapi.h"
michael@0 38 #include <pthread.h>
michael@0 39 #include <gdk/gdk.h>
michael@0 40 #ifdef MOZ_X11
michael@0 41 #include <gdk/gdkx.h>
michael@0 42 #include <X11/extensions/shape.h>
michael@0 43 #endif
michael@0 44 #include <glib.h>
michael@0 45 #include <gtk/gtk.h>
michael@0 46 #include <unistd.h>
michael@0 47
michael@0 48 #include "mozilla/NullPtr.h"
michael@0 49 #include "mozilla/IntentionalCrash.h"
michael@0 50
michael@0 51 using namespace std;
michael@0 52
michael@0 53 struct _PlatformData {
michael@0 54 #ifdef MOZ_X11
michael@0 55 Display* display;
michael@0 56 Visual* visual;
michael@0 57 Colormap colormap;
michael@0 58 #endif
michael@0 59 GtkWidget* plug;
michael@0 60 };
michael@0 61
michael@0 62 bool
michael@0 63 pluginSupportsWindowMode()
michael@0 64 {
michael@0 65 return true;
michael@0 66 }
michael@0 67
michael@0 68 bool
michael@0 69 pluginSupportsWindowlessMode()
michael@0 70 {
michael@0 71 return true;
michael@0 72 }
michael@0 73
michael@0 74 bool
michael@0 75 pluginSupportsAsyncBitmapDrawing()
michael@0 76 {
michael@0 77 return false;
michael@0 78 }
michael@0 79
michael@0 80 NPError
michael@0 81 pluginInstanceInit(InstanceData* instanceData)
michael@0 82 {
michael@0 83 #ifdef MOZ_X11
michael@0 84 instanceData->platformData = static_cast<PlatformData*>
michael@0 85 (NPN_MemAlloc(sizeof(PlatformData)));
michael@0 86 if (!instanceData->platformData)
michael@0 87 return NPERR_OUT_OF_MEMORY_ERROR;
michael@0 88
michael@0 89 instanceData->platformData->display = nullptr;
michael@0 90 instanceData->platformData->visual = nullptr;
michael@0 91 instanceData->platformData->colormap = None;
michael@0 92 instanceData->platformData->plug = nullptr;
michael@0 93
michael@0 94 return NPERR_NO_ERROR;
michael@0 95 #else
michael@0 96 // we only support X11 here, since thats what the plugin system uses
michael@0 97 return NPERR_INCOMPATIBLE_VERSION_ERROR;
michael@0 98 #endif
michael@0 99 }
michael@0 100
michael@0 101 void
michael@0 102 pluginInstanceShutdown(InstanceData* instanceData)
michael@0 103 {
michael@0 104 if (instanceData->hasWidget) {
michael@0 105 Window window = reinterpret_cast<XID>(instanceData->window.window);
michael@0 106
michael@0 107 if (window != None) {
michael@0 108 // This window XID should still be valid.
michael@0 109 // See bug 429604 and bug 454756.
michael@0 110 XWindowAttributes attributes;
michael@0 111 if (!XGetWindowAttributes(instanceData->platformData->display, window,
michael@0 112 &attributes))
michael@0 113 g_error("XGetWindowAttributes failed at plugin instance shutdown");
michael@0 114 }
michael@0 115 }
michael@0 116
michael@0 117 GtkWidget* plug = instanceData->platformData->plug;
michael@0 118 if (plug) {
michael@0 119 instanceData->platformData->plug = 0;
michael@0 120 if (instanceData->cleanupWidget) {
michael@0 121 // Default/tidy behavior
michael@0 122 gtk_widget_destroy(plug);
michael@0 123 } else {
michael@0 124 // Flash Player style: let the GtkPlug destroy itself on disconnect.
michael@0 125 g_signal_handlers_disconnect_matched(plug, G_SIGNAL_MATCH_DATA, 0, 0,
michael@0 126 nullptr, nullptr, instanceData);
michael@0 127 }
michael@0 128 }
michael@0 129
michael@0 130 NPN_MemFree(instanceData->platformData);
michael@0 131 instanceData->platformData = 0;
michael@0 132 }
michael@0 133
michael@0 134 static void
michael@0 135 SetCairoRGBA(cairo_t* cairoWindow, uint32_t rgba)
michael@0 136 {
michael@0 137 float b = (rgba & 0xFF) / 255.0;
michael@0 138 float g = ((rgba & 0xFF00) >> 8) / 255.0;
michael@0 139 float r = ((rgba & 0xFF0000) >> 16) / 255.0;
michael@0 140 float a = ((rgba & 0xFF000000) >> 24) / 255.0;
michael@0 141
michael@0 142 cairo_set_source_rgba(cairoWindow, r, g, b, a);
michael@0 143 }
michael@0 144
michael@0 145 static void
michael@0 146 pluginDrawSolid(InstanceData* instanceData, GdkDrawable* gdkWindow,
michael@0 147 int x, int y, int width, int height)
michael@0 148 {
michael@0 149 cairo_t* cairoWindow = gdk_cairo_create(gdkWindow);
michael@0 150
michael@0 151 if (!instanceData->hasWidget) {
michael@0 152 NPRect* clip = &instanceData->window.clipRect;
michael@0 153 cairo_rectangle(cairoWindow, clip->left, clip->top,
michael@0 154 clip->right - clip->left, clip->bottom - clip->top);
michael@0 155 cairo_clip(cairoWindow);
michael@0 156 }
michael@0 157
michael@0 158 GdkRectangle windowRect = { x, y, width, height };
michael@0 159 gdk_cairo_rectangle(cairoWindow, &windowRect);
michael@0 160 SetCairoRGBA(cairoWindow, instanceData->scriptableObject->drawColor);
michael@0 161
michael@0 162 cairo_fill(cairoWindow);
michael@0 163 cairo_destroy(cairoWindow);
michael@0 164 }
michael@0 165
michael@0 166 static void
michael@0 167 pluginDrawWindow(InstanceData* instanceData, GdkDrawable* gdkWindow,
michael@0 168 const GdkRectangle& invalidRect)
michael@0 169 {
michael@0 170 NPWindow& window = instanceData->window;
michael@0 171 // When we have a widget, window.x/y are meaningless since our
michael@0 172 // widget is always positioned correctly and we just draw into it at 0,0
michael@0 173 int x = instanceData->hasWidget ? 0 : window.x;
michael@0 174 int y = instanceData->hasWidget ? 0 : window.y;
michael@0 175 int width = window.width;
michael@0 176 int height = window.height;
michael@0 177
michael@0 178 notifyDidPaint(instanceData);
michael@0 179
michael@0 180 if (instanceData->scriptableObject->drawMode == DM_SOLID_COLOR) {
michael@0 181 // drawing a solid color for reftests
michael@0 182 pluginDrawSolid(instanceData, gdkWindow,
michael@0 183 invalidRect.x, invalidRect.y,
michael@0 184 invalidRect.width, invalidRect.height);
michael@0 185 return;
michael@0 186 }
michael@0 187
michael@0 188 NPP npp = instanceData->npp;
michael@0 189 if (!npp)
michael@0 190 return;
michael@0 191
michael@0 192 const char* uaString = NPN_UserAgent(npp);
michael@0 193 if (!uaString)
michael@0 194 return;
michael@0 195
michael@0 196 GdkGC* gdkContext = gdk_gc_new(gdkWindow);
michael@0 197 if (!gdkContext)
michael@0 198 return;
michael@0 199
michael@0 200 if (!instanceData->hasWidget) {
michael@0 201 NPRect* clip = &window.clipRect;
michael@0 202 GdkRectangle gdkClip = { clip->left, clip->top, clip->right - clip->left,
michael@0 203 clip->bottom - clip->top };
michael@0 204 gdk_gc_set_clip_rectangle(gdkContext, &gdkClip);
michael@0 205 }
michael@0 206
michael@0 207 // draw a grey background for the plugin frame
michael@0 208 GdkColor grey;
michael@0 209 grey.red = grey.blue = grey.green = 32767;
michael@0 210 gdk_gc_set_rgb_fg_color(gdkContext, &grey);
michael@0 211 gdk_draw_rectangle(gdkWindow, gdkContext, TRUE, x, y, width, height);
michael@0 212
michael@0 213 // draw a 3-pixel-thick black frame around the plugin
michael@0 214 GdkColor black;
michael@0 215 black.red = black.green = black.blue = 0;
michael@0 216 gdk_gc_set_rgb_fg_color(gdkContext, &black);
michael@0 217 gdk_gc_set_line_attributes(gdkContext, 3, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
michael@0 218 gdk_draw_rectangle(gdkWindow, gdkContext, FALSE, x + 1, y + 1,
michael@0 219 width - 3, height - 3);
michael@0 220
michael@0 221 // paint the UA string
michael@0 222 PangoContext* pangoContext = gdk_pango_context_get();
michael@0 223 PangoLayout* pangoTextLayout = pango_layout_new(pangoContext);
michael@0 224 pango_layout_set_width(pangoTextLayout, (width - 10) * PANGO_SCALE);
michael@0 225 pango_layout_set_text(pangoTextLayout, uaString, -1);
michael@0 226 gdk_draw_layout(gdkWindow, gdkContext, x + 5, y + 5, pangoTextLayout);
michael@0 227 g_object_unref(pangoTextLayout);
michael@0 228
michael@0 229 g_object_unref(gdkContext);
michael@0 230 }
michael@0 231
michael@0 232 static gboolean
michael@0 233 ExposeWidget(GtkWidget* widget, GdkEventExpose* event,
michael@0 234 gpointer user_data)
michael@0 235 {
michael@0 236 InstanceData* instanceData = static_cast<InstanceData*>(user_data);
michael@0 237 pluginDrawWindow(instanceData, event->window, event->area);
michael@0 238 return TRUE;
michael@0 239 }
michael@0 240
michael@0 241 static gboolean
michael@0 242 MotionEvent(GtkWidget* widget, GdkEventMotion* event,
michael@0 243 gpointer user_data)
michael@0 244 {
michael@0 245 InstanceData* instanceData = static_cast<InstanceData*>(user_data);
michael@0 246 instanceData->lastMouseX = event->x;
michael@0 247 instanceData->lastMouseY = event->y;
michael@0 248 return TRUE;
michael@0 249 }
michael@0 250
michael@0 251 static gboolean
michael@0 252 ButtonEvent(GtkWidget* widget, GdkEventButton* event,
michael@0 253 gpointer user_data)
michael@0 254 {
michael@0 255 InstanceData* instanceData = static_cast<InstanceData*>(user_data);
michael@0 256 instanceData->lastMouseX = event->x;
michael@0 257 instanceData->lastMouseY = event->y;
michael@0 258 if (event->type == GDK_BUTTON_RELEASE) {
michael@0 259 instanceData->mouseUpEventCount++;
michael@0 260 }
michael@0 261 return TRUE;
michael@0 262 }
michael@0 263
michael@0 264 static gboolean
michael@0 265 DeleteWidget(GtkWidget* widget, GdkEvent* event, gpointer user_data)
michael@0 266 {
michael@0 267 InstanceData* instanceData = static_cast<InstanceData*>(user_data);
michael@0 268 // Some plugins do not expect the plug to be removed from the socket before
michael@0 269 // the plugin instance is destroyed. e.g. bug 485125
michael@0 270 if (instanceData->platformData->plug)
michael@0 271 g_error("plug removed"); // this aborts
michael@0 272
michael@0 273 return FALSE;
michael@0 274 }
michael@0 275
michael@0 276 void
michael@0 277 pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow)
michael@0 278 {
michael@0 279 instanceData->window = *newWindow;
michael@0 280
michael@0 281 #ifdef MOZ_X11
michael@0 282 NPSetWindowCallbackStruct *ws_info =
michael@0 283 static_cast<NPSetWindowCallbackStruct*>(newWindow->ws_info);
michael@0 284 instanceData->platformData->display = ws_info->display;
michael@0 285 instanceData->platformData->visual = ws_info->visual;
michael@0 286 instanceData->platformData->colormap = ws_info->colormap;
michael@0 287 #endif
michael@0 288 }
michael@0 289
michael@0 290 void
michael@0 291 pluginWidgetInit(InstanceData* instanceData, void* oldWindow)
michael@0 292 {
michael@0 293 #ifdef MOZ_X11
michael@0 294 GtkWidget* oldPlug = instanceData->platformData->plug;
michael@0 295 if (oldPlug) {
michael@0 296 instanceData->platformData->plug = 0;
michael@0 297 gtk_widget_destroy(oldPlug);
michael@0 298 }
michael@0 299
michael@0 300 GdkNativeWindow nativeWinId =
michael@0 301 reinterpret_cast<XID>(instanceData->window.window);
michael@0 302
michael@0 303 /* create a GtkPlug container */
michael@0 304 GtkWidget* plug = gtk_plug_new(nativeWinId);
michael@0 305
michael@0 306 // Test for bugs 539138 and 561308
michael@0 307 if (!plug->window)
michael@0 308 g_error("Plug has no window"); // aborts
michael@0 309
michael@0 310 /* make sure the widget is capable of receiving focus */
michael@0 311 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(plug), GTK_CAN_FOCUS);
michael@0 312
michael@0 313 /* all the events that our widget wants to receive */
michael@0 314 gtk_widget_add_events(plug, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK |
michael@0 315 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
michael@0 316 g_signal_connect(plug, "expose-event", G_CALLBACK(ExposeWidget),
michael@0 317 instanceData);
michael@0 318 g_signal_connect(plug, "motion_notify_event", G_CALLBACK(MotionEvent),
michael@0 319 instanceData);
michael@0 320 g_signal_connect(plug, "button_press_event", G_CALLBACK(ButtonEvent),
michael@0 321 instanceData);
michael@0 322 g_signal_connect(plug, "button_release_event", G_CALLBACK(ButtonEvent),
michael@0 323 instanceData);
michael@0 324 g_signal_connect(plug, "delete-event", G_CALLBACK(DeleteWidget),
michael@0 325 instanceData);
michael@0 326 gtk_widget_show(plug);
michael@0 327
michael@0 328 instanceData->platformData->plug = plug;
michael@0 329 #endif
michael@0 330 }
michael@0 331
michael@0 332 int16_t
michael@0 333 pluginHandleEvent(InstanceData* instanceData, void* event)
michael@0 334 {
michael@0 335 #ifdef MOZ_X11
michael@0 336 XEvent* nsEvent = (XEvent*)event;
michael@0 337
michael@0 338 switch (nsEvent->type) {
michael@0 339 case GraphicsExpose: {
michael@0 340 const XGraphicsExposeEvent& expose = nsEvent->xgraphicsexpose;
michael@0 341 NPWindow& window = instanceData->window;
michael@0 342 window.window = (void*)(expose.drawable);
michael@0 343
michael@0 344 GdkNativeWindow nativeWinId = reinterpret_cast<XID>(window.window);
michael@0 345
michael@0 346 GdkDisplay* gdkDisplay = gdk_x11_lookup_xdisplay(expose.display);
michael@0 347 if (!gdkDisplay) {
michael@0 348 g_warning("Display not opened by GDK");
michael@0 349 return 0;
michael@0 350 }
michael@0 351 // gdk_pixmap_foreign_new() doesn't check whether a GdkPixmap already
michael@0 352 // exists, so check first.
michael@0 353 // https://bugzilla.gnome.org/show_bug.cgi?id=590690
michael@0 354 GdkPixmap* gdkDrawable =
michael@0 355 GDK_DRAWABLE(gdk_pixmap_lookup_for_display(gdkDisplay, nativeWinId));
michael@0 356 // If there is no existing GdkPixmap or it doesn't have a colormap then
michael@0 357 // create our own.
michael@0 358 if (gdkDrawable) {
michael@0 359 GdkColormap* gdkColormap = gdk_drawable_get_colormap(gdkDrawable);
michael@0 360 if (!gdkColormap) {
michael@0 361 g_warning("No GdkColormap on GdkPixmap");
michael@0 362 return 0;
michael@0 363 }
michael@0 364 if (gdk_x11_colormap_get_xcolormap(gdkColormap)
michael@0 365 != instanceData->platformData->colormap) {
michael@0 366 g_warning("wrong Colormap");
michael@0 367 return 0;
michael@0 368 }
michael@0 369 if (gdk_x11_visual_get_xvisual(gdk_colormap_get_visual(gdkColormap))
michael@0 370 != instanceData->platformData->visual) {
michael@0 371 g_warning("wrong Visual");
michael@0 372 return 0;
michael@0 373 }
michael@0 374 g_object_ref(gdkDrawable);
michael@0 375 } else {
michael@0 376 gdkDrawable =
michael@0 377 GDK_DRAWABLE(gdk_pixmap_foreign_new_for_display(gdkDisplay,
michael@0 378 nativeWinId));
michael@0 379 VisualID visualID = instanceData->platformData->visual->visualid;
michael@0 380 GdkVisual* gdkVisual =
michael@0 381 gdk_x11_screen_lookup_visual(gdk_drawable_get_screen(gdkDrawable),
michael@0 382 visualID);
michael@0 383 GdkColormap* gdkColormap =
michael@0 384 gdk_x11_colormap_foreign_new(gdkVisual,
michael@0 385 instanceData->platformData->colormap);
michael@0 386 gdk_drawable_set_colormap(gdkDrawable, gdkColormap);
michael@0 387 g_object_unref(gdkColormap);
michael@0 388 }
michael@0 389
michael@0 390 const NPRect& clip = window.clipRect;
michael@0 391 if (expose.x < clip.left || expose.y < clip.top ||
michael@0 392 expose.x + expose.width > clip.right ||
michael@0 393 expose.y + expose.height > clip.bottom) {
michael@0 394 g_warning("expose rectangle (x=%d,y=%d,w=%d,h=%d) not in clip rectangle (l=%d,t=%d,r=%d,b=%d)",
michael@0 395 expose.x, expose.y, expose.width, expose.height,
michael@0 396 clip.left, clip.top, clip.right, clip.bottom);
michael@0 397 return 0;
michael@0 398 }
michael@0 399 if (expose.x < window.x || expose.y < window.y ||
michael@0 400 expose.x + expose.width > window.x + int32_t(window.width) ||
michael@0 401 expose.y + expose.height > window.y + int32_t(window.height)) {
michael@0 402 g_warning("expose rectangle (x=%d,y=%d,w=%d,h=%d) not in plugin rectangle (x=%d,y=%d,w=%d,h=%d)",
michael@0 403 expose.x, expose.y, expose.width, expose.height,
michael@0 404 window.x, window.y, window.width, window.height);
michael@0 405 return 0;
michael@0 406 }
michael@0 407
michael@0 408 GdkRectangle invalidRect =
michael@0 409 { expose.x, expose.y, expose.width, expose.height };
michael@0 410 pluginDrawWindow(instanceData, gdkDrawable, invalidRect);
michael@0 411 g_object_unref(gdkDrawable);
michael@0 412 break;
michael@0 413 }
michael@0 414 case MotionNotify: {
michael@0 415 XMotionEvent* motion = &nsEvent->xmotion;
michael@0 416 instanceData->lastMouseX = motion->x;
michael@0 417 instanceData->lastMouseY = motion->y;
michael@0 418 break;
michael@0 419 }
michael@0 420 case ButtonPress:
michael@0 421 case ButtonRelease: {
michael@0 422 XButtonEvent* button = &nsEvent->xbutton;
michael@0 423 instanceData->lastMouseX = button->x;
michael@0 424 instanceData->lastMouseY = button->y;
michael@0 425 if (nsEvent->type == ButtonRelease) {
michael@0 426 instanceData->mouseUpEventCount++;
michael@0 427 }
michael@0 428 break;
michael@0 429 }
michael@0 430 default:
michael@0 431 break;
michael@0 432 }
michael@0 433 #endif
michael@0 434
michael@0 435 return 0;
michael@0 436 }
michael@0 437
michael@0 438 int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge)
michael@0 439 {
michael@0 440 if (!instanceData->hasWidget)
michael@0 441 return NPTEST_INT32_ERROR;
michael@0 442 GtkWidget* plug = instanceData->platformData->plug;
michael@0 443 if (!plug)
michael@0 444 return NPTEST_INT32_ERROR;
michael@0 445 GdkWindow* plugWnd = plug->window;
michael@0 446 if (!plugWnd)
michael@0 447 return NPTEST_INT32_ERROR;
michael@0 448
michael@0 449 GdkWindow* toplevelGdk = 0;
michael@0 450 #ifdef MOZ_X11
michael@0 451 Window toplevel = 0;
michael@0 452 NPN_GetValue(instanceData->npp, NPNVnetscapeWindow, &toplevel);
michael@0 453 if (!toplevel)
michael@0 454 return NPTEST_INT32_ERROR;
michael@0 455 toplevelGdk = gdk_window_foreign_new(toplevel);
michael@0 456 #endif
michael@0 457 if (!toplevelGdk)
michael@0 458 return NPTEST_INT32_ERROR;
michael@0 459
michael@0 460 GdkRectangle toplevelFrameExtents;
michael@0 461 gdk_window_get_frame_extents(toplevelGdk, &toplevelFrameExtents);
michael@0 462 g_object_unref(toplevelGdk);
michael@0 463
michael@0 464 gint pluginWidth, pluginHeight;
michael@0 465 gdk_drawable_get_size(GDK_DRAWABLE(plugWnd), &pluginWidth, &pluginHeight);
michael@0 466 gint pluginOriginX, pluginOriginY;
michael@0 467 gdk_window_get_origin(plugWnd, &pluginOriginX, &pluginOriginY);
michael@0 468 gint pluginX = pluginOriginX - toplevelFrameExtents.x;
michael@0 469 gint pluginY = pluginOriginY - toplevelFrameExtents.y;
michael@0 470
michael@0 471 switch (edge) {
michael@0 472 case EDGE_LEFT:
michael@0 473 return pluginX;
michael@0 474 case EDGE_TOP:
michael@0 475 return pluginY;
michael@0 476 case EDGE_RIGHT:
michael@0 477 return pluginX + pluginWidth;
michael@0 478 case EDGE_BOTTOM:
michael@0 479 return pluginY + pluginHeight;
michael@0 480 }
michael@0 481
michael@0 482 return NPTEST_INT32_ERROR;
michael@0 483 }
michael@0 484
michael@0 485 #ifdef MOZ_X11
michael@0 486 static void intersectWithShapeRects(Display* display, Window window,
michael@0 487 int kind, GdkRegion* region)
michael@0 488 {
michael@0 489 int count = -1, order;
michael@0 490 XRectangle* shapeRects =
michael@0 491 XShapeGetRectangles(display, window, kind, &count, &order);
michael@0 492 // The documentation says that shapeRects will be nullptr when the
michael@0 493 // extension is not supported. Unfortunately XShapeGetRectangles
michael@0 494 // also returns nullptr when the region is empty, so we can't treat
michael@0 495 // nullptr as failure. I hope this way is OK.
michael@0 496 if (count < 0)
michael@0 497 return;
michael@0 498
michael@0 499 GdkRegion* shapeRegion = gdk_region_new();
michael@0 500 if (!shapeRegion) {
michael@0 501 XFree(shapeRects);
michael@0 502 return;
michael@0 503 }
michael@0 504
michael@0 505 for (int i = 0; i < count; ++i) {
michael@0 506 XRectangle* r = &shapeRects[i];
michael@0 507 GdkRectangle rect = { r->x, r->y, r->width, r->height };
michael@0 508 gdk_region_union_with_rect(shapeRegion, &rect);
michael@0 509 }
michael@0 510 XFree(shapeRects);
michael@0 511
michael@0 512 gdk_region_intersect(region, shapeRegion);
michael@0 513 gdk_region_destroy(shapeRegion);
michael@0 514 }
michael@0 515 #endif
michael@0 516
michael@0 517 static GdkRegion* computeClipRegion(InstanceData* instanceData)
michael@0 518 {
michael@0 519 if (!instanceData->hasWidget)
michael@0 520 return 0;
michael@0 521
michael@0 522 GtkWidget* plug = instanceData->platformData->plug;
michael@0 523 if (!plug)
michael@0 524 return 0;
michael@0 525 GdkWindow* plugWnd = plug->window;
michael@0 526 if (!plugWnd)
michael@0 527 return 0;
michael@0 528
michael@0 529 gint plugWidth, plugHeight;
michael@0 530 gdk_drawable_get_size(GDK_DRAWABLE(plugWnd), &plugWidth, &plugHeight);
michael@0 531 GdkRectangle pluginRect = { 0, 0, plugWidth, plugHeight };
michael@0 532 GdkRegion* region = gdk_region_rectangle(&pluginRect);
michael@0 533 if (!region)
michael@0 534 return 0;
michael@0 535
michael@0 536 int pluginX = 0, pluginY = 0;
michael@0 537
michael@0 538 #ifdef MOZ_X11
michael@0 539 Display* display = GDK_WINDOW_XDISPLAY(plugWnd);
michael@0 540 Window window = GDK_WINDOW_XWINDOW(plugWnd);
michael@0 541
michael@0 542 Window toplevel = 0;
michael@0 543 NPN_GetValue(instanceData->npp, NPNVnetscapeWindow, &toplevel);
michael@0 544 if (!toplevel)
michael@0 545 return 0;
michael@0 546
michael@0 547 for (;;) {
michael@0 548 Window root;
michael@0 549 int x, y;
michael@0 550 unsigned int width, height, border_width, depth;
michael@0 551 if (!XGetGeometry(display, window, &root, &x, &y, &width, &height,
michael@0 552 &border_width, &depth)) {
michael@0 553 gdk_region_destroy(region);
michael@0 554 return 0;
michael@0 555 }
michael@0 556
michael@0 557 GdkRectangle windowRect = { 0, 0, static_cast<gint>(width),
michael@0 558 static_cast<gint>(height) };
michael@0 559 GdkRegion* windowRgn = gdk_region_rectangle(&windowRect);
michael@0 560 if (!windowRgn) {
michael@0 561 gdk_region_destroy(region);
michael@0 562 return 0;
michael@0 563 }
michael@0 564 intersectWithShapeRects(display, window, ShapeBounding, windowRgn);
michael@0 565 intersectWithShapeRects(display, window, ShapeClip, windowRgn);
michael@0 566 gdk_region_offset(windowRgn, -pluginX, -pluginY);
michael@0 567 gdk_region_intersect(region, windowRgn);
michael@0 568 gdk_region_destroy(windowRgn);
michael@0 569
michael@0 570 // Stop now if we've reached the toplevel. Stopping here means
michael@0 571 // clipping performed by the toplevel window is taken into account.
michael@0 572 if (window == toplevel)
michael@0 573 break;
michael@0 574
michael@0 575 Window parent;
michael@0 576 Window* children;
michael@0 577 unsigned int nchildren;
michael@0 578 if (!XQueryTree(display, window, &root, &parent, &children, &nchildren)) {
michael@0 579 gdk_region_destroy(region);
michael@0 580 return 0;
michael@0 581 }
michael@0 582 XFree(children);
michael@0 583
michael@0 584 pluginX += x;
michael@0 585 pluginY += y;
michael@0 586
michael@0 587 window = parent;
michael@0 588 }
michael@0 589 #endif
michael@0 590 // pluginX and pluginY are now relative to the toplevel. Make them
michael@0 591 // relative to the window frame top-left.
michael@0 592 GdkWindow* toplevelGdk = gdk_window_foreign_new(window);
michael@0 593 if (!toplevelGdk)
michael@0 594 return 0;
michael@0 595 GdkRectangle toplevelFrameExtents;
michael@0 596 gdk_window_get_frame_extents(toplevelGdk, &toplevelFrameExtents);
michael@0 597 gint toplevelOriginX, toplevelOriginY;
michael@0 598 gdk_window_get_origin(toplevelGdk, &toplevelOriginX, &toplevelOriginY);
michael@0 599 g_object_unref(toplevelGdk);
michael@0 600
michael@0 601 pluginX += toplevelOriginX - toplevelFrameExtents.x;
michael@0 602 pluginY += toplevelOriginY - toplevelFrameExtents.y;
michael@0 603
michael@0 604 gdk_region_offset(region, pluginX, pluginY);
michael@0 605 return region;
michael@0 606 }
michael@0 607
michael@0 608 int32_t pluginGetClipRegionRectCount(InstanceData* instanceData)
michael@0 609 {
michael@0 610 GdkRegion* region = computeClipRegion(instanceData);
michael@0 611 if (!region)
michael@0 612 return NPTEST_INT32_ERROR;
michael@0 613
michael@0 614 GdkRectangle* rects;
michael@0 615 gint nrects;
michael@0 616 gdk_region_get_rectangles(region, &rects, &nrects);
michael@0 617 gdk_region_destroy(region);
michael@0 618 g_free(rects);
michael@0 619 return nrects;
michael@0 620 }
michael@0 621
michael@0 622 int32_t pluginGetClipRegionRectEdge(InstanceData* instanceData,
michael@0 623 int32_t rectIndex, RectEdge edge)
michael@0 624 {
michael@0 625 GdkRegion* region = computeClipRegion(instanceData);
michael@0 626 if (!region)
michael@0 627 return NPTEST_INT32_ERROR;
michael@0 628
michael@0 629 GdkRectangle* rects;
michael@0 630 gint nrects;
michael@0 631 gdk_region_get_rectangles(region, &rects, &nrects);
michael@0 632 gdk_region_destroy(region);
michael@0 633 if (rectIndex >= nrects) {
michael@0 634 g_free(rects);
michael@0 635 return NPTEST_INT32_ERROR;
michael@0 636 }
michael@0 637
michael@0 638 GdkRectangle rect = rects[rectIndex];
michael@0 639 g_free(rects);
michael@0 640
michael@0 641 switch (edge) {
michael@0 642 case EDGE_LEFT:
michael@0 643 return rect.x;
michael@0 644 case EDGE_TOP:
michael@0 645 return rect.y;
michael@0 646 case EDGE_RIGHT:
michael@0 647 return rect.x + rect.width;
michael@0 648 case EDGE_BOTTOM:
michael@0 649 return rect.y + rect.height;
michael@0 650 }
michael@0 651 return NPTEST_INT32_ERROR;
michael@0 652 }
michael@0 653
michael@0 654 void pluginDoInternalConsistencyCheck(InstanceData* instanceData, string& error)
michael@0 655 {
michael@0 656 }
michael@0 657
michael@0 658 string
michael@0 659 pluginGetClipboardText(InstanceData* instanceData)
michael@0 660 {
michael@0 661 GtkClipboard* cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
michael@0 662 // XXX this is a BAD WAY to interact with GtkClipboard. We use this
michael@0 663 // deprecated interface only to test nested event loop handling.
michael@0 664 gchar* text = gtk_clipboard_wait_for_text(cb);
michael@0 665 string retText = text ? text : "";
michael@0 666
michael@0 667 g_free(text);
michael@0 668
michael@0 669 return retText;
michael@0 670 }
michael@0 671
michael@0 672 //-----------------------------------------------------------------------------
michael@0 673 // NB: this test is quite gross in that it's not only
michael@0 674 // nondeterministic, but dependent on the guts of the nested glib
michael@0 675 // event loop handling code in PluginModule. We first sleep long
michael@0 676 // enough to make sure that the "detection timer" will be pending when
michael@0 677 // we enter the nested glib loop, then similarly for the "process browser
michael@0 678 // events" timer. Then we "schedule" the crasher thread to run at about the
michael@0 679 // same time we expect that the PluginModule "process browser events" task
michael@0 680 // will run. If all goes well, the plugin process will crash and generate the
michael@0 681 // XPCOM "plugin crashed" task, and the browser will run that task while still
michael@0 682 // in the "process some events" loop.
michael@0 683
michael@0 684 static void*
michael@0 685 CrasherThread(void* data)
michael@0 686 {
michael@0 687 // Give the parent thread a chance to send the message.
michael@0 688 usleep(200);
michael@0 689
michael@0 690 // Exit (without running atexit hooks) rather than crashing with a signal
michael@0 691 // so as to make timing more reliable. The process terminates immediately
michael@0 692 // rather than waiting for a thread in the parent process to attach and
michael@0 693 // generate a minidump.
michael@0 694 _exit(1);
michael@0 695
michael@0 696 // not reached
michael@0 697 return(nullptr);
michael@0 698 }
michael@0 699
michael@0 700 bool
michael@0 701 pluginCrashInNestedLoop(InstanceData* instanceData)
michael@0 702 {
michael@0 703 // wait at least long enough for nested loop detector task to be pending ...
michael@0 704 sleep(1);
michael@0 705
michael@0 706 // Run the nested loop detector by processing all events that are waiting.
michael@0 707 bool found_event = false;
michael@0 708 while (g_main_context_iteration(nullptr, FALSE)) {
michael@0 709 found_event = true;
michael@0 710 }
michael@0 711 if (!found_event) {
michael@0 712 g_warning("DetectNestedEventLoop did not fire");
michael@0 713 return true; // trigger a test failure
michael@0 714 }
michael@0 715
michael@0 716 // wait at least long enough for the "process browser events" task to be
michael@0 717 // pending ...
michael@0 718 sleep(1);
michael@0 719
michael@0 720 // we'll be crashing soon, note that fact now to avoid messing with
michael@0 721 // timing too much
michael@0 722 mozilla::NoteIntentionalCrash("plugin");
michael@0 723
michael@0 724 // schedule the crasher thread ...
michael@0 725 pthread_t crasherThread;
michael@0 726 if (0 != pthread_create(&crasherThread, nullptr, CrasherThread, nullptr)) {
michael@0 727 g_warning("Failed to create thread");
michael@0 728 return true; // trigger a test failure
michael@0 729 }
michael@0 730
michael@0 731 // .. and hope it crashes at about the same time as the "process browser
michael@0 732 // events" task (that should run in this loop) is being processed in the
michael@0 733 // parent.
michael@0 734 found_event = false;
michael@0 735 while (g_main_context_iteration(nullptr, FALSE)) {
michael@0 736 found_event = true;
michael@0 737 }
michael@0 738 if (found_event) {
michael@0 739 g_warning("Should have crashed in ProcessBrowserEvents");
michael@0 740 } else {
michael@0 741 g_warning("ProcessBrowserEvents did not fire");
michael@0 742 }
michael@0 743
michael@0 744 // if we get here without crashing, then we'll trigger a test failure
michael@0 745 return true;
michael@0 746 }
michael@0 747
michael@0 748 static int
michael@0 749 SleepThenDie(Display* display)
michael@0 750 {
michael@0 751 mozilla::NoteIntentionalCrash("plugin");
michael@0 752 fprintf(stderr, "[testplugin:%d] SleepThenDie: sleeping\n", getpid());
michael@0 753 sleep(1);
michael@0 754
michael@0 755 fprintf(stderr, "[testplugin:%d] SleepThenDie: dying\n", getpid());
michael@0 756 _exit(1);
michael@0 757 }
michael@0 758
michael@0 759 bool
michael@0 760 pluginDestroySharedGfxStuff(InstanceData* instanceData)
michael@0 761 {
michael@0 762 // Closing the X socket results in the gdk error handler being
michael@0 763 // invoked, which exit()s us. We want to give the parent process a
michael@0 764 // little while to do whatever it wanted to do, so steal the IO
michael@0 765 // handler from gdk and set up our own that delays seppuku.
michael@0 766 XSetIOErrorHandler(SleepThenDie);
michael@0 767 close(ConnectionNumber(GDK_DISPLAY()));
michael@0 768 return true;
michael@0 769 }

mercurial