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

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

mercurial