LCOV - code coverage report
Current view: directory - dom/plugins/test/testplugin - nptest_gtk2.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 353 0 0.0 %
Date: 2012-06-02 Functions: 26 0 0.0 %

       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                 : }

Generated by: LCOV version 1.7