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