dom/plugins/test/testplugin/nptest_gtk2.cpp

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

mercurial