michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* ***** BEGIN LICENSE BLOCK ***** michael@0: * michael@0: * Copyright (c) 2008, Mozilla Corporation michael@0: * All rights reserved. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions are met: michael@0: * michael@0: * * Redistributions of source code must retain the above copyright notice, this michael@0: * list of conditions and the following disclaimer. michael@0: * * Redistributions in binary form must reproduce the above copyright notice, michael@0: * this list of conditions and the following disclaimer in the documentation michael@0: * and/or other materials provided with the distribution. michael@0: * * Neither the name of the Mozilla Corporation nor the names of its michael@0: * contributors may be used to endorse or promote products derived from this michael@0: * software without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND michael@0: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED michael@0: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE michael@0: * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR michael@0: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES michael@0: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; michael@0: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON michael@0: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS michael@0: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: * michael@0: * Contributor(s): michael@0: * Josh Aas michael@0: * Michael Ventnor michael@0: * michael@0: * ***** END LICENSE BLOCK ***** */ michael@0: michael@0: #include "nptest_platform.h" michael@0: #include "npapi.h" michael@0: #include michael@0: #include michael@0: #ifdef MOZ_X11 michael@0: #include michael@0: #include michael@0: #endif michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "mozilla/NullPtr.h" michael@0: #include "mozilla/IntentionalCrash.h" michael@0: michael@0: using namespace std; michael@0: michael@0: struct _PlatformData { michael@0: #ifdef MOZ_X11 michael@0: Display* display; michael@0: Visual* visual; michael@0: Colormap colormap; michael@0: #endif michael@0: GtkWidget* plug; michael@0: }; michael@0: michael@0: bool michael@0: pluginSupportsWindowMode() michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: pluginSupportsWindowlessMode() michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: pluginSupportsAsyncBitmapDrawing() michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: NPError michael@0: pluginInstanceInit(InstanceData* instanceData) michael@0: { michael@0: #ifdef MOZ_X11 michael@0: instanceData->platformData = static_cast michael@0: (NPN_MemAlloc(sizeof(PlatformData))); michael@0: if (!instanceData->platformData) michael@0: return NPERR_OUT_OF_MEMORY_ERROR; michael@0: michael@0: instanceData->platformData->display = nullptr; michael@0: instanceData->platformData->visual = nullptr; michael@0: instanceData->platformData->colormap = None; michael@0: instanceData->platformData->plug = nullptr; michael@0: michael@0: return NPERR_NO_ERROR; michael@0: #else michael@0: // we only support X11 here, since thats what the plugin system uses michael@0: return NPERR_INCOMPATIBLE_VERSION_ERROR; michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: pluginInstanceShutdown(InstanceData* instanceData) michael@0: { michael@0: if (instanceData->hasWidget) { michael@0: Window window = reinterpret_cast(instanceData->window.window); michael@0: michael@0: if (window != None) { michael@0: // This window XID should still be valid. michael@0: // See bug 429604 and bug 454756. michael@0: XWindowAttributes attributes; michael@0: if (!XGetWindowAttributes(instanceData->platformData->display, window, michael@0: &attributes)) michael@0: g_error("XGetWindowAttributes failed at plugin instance shutdown"); michael@0: } michael@0: } michael@0: michael@0: GtkWidget* plug = instanceData->platformData->plug; michael@0: if (plug) { michael@0: instanceData->platformData->plug = 0; michael@0: if (instanceData->cleanupWidget) { michael@0: // Default/tidy behavior michael@0: gtk_widget_destroy(plug); michael@0: } else { michael@0: // Flash Player style: let the GtkPlug destroy itself on disconnect. michael@0: g_signal_handlers_disconnect_matched(plug, G_SIGNAL_MATCH_DATA, 0, 0, michael@0: nullptr, nullptr, instanceData); michael@0: } michael@0: } michael@0: michael@0: NPN_MemFree(instanceData->platformData); michael@0: instanceData->platformData = 0; michael@0: } michael@0: michael@0: static void michael@0: SetCairoRGBA(cairo_t* cairoWindow, uint32_t rgba) michael@0: { michael@0: float b = (rgba & 0xFF) / 255.0; michael@0: float g = ((rgba & 0xFF00) >> 8) / 255.0; michael@0: float r = ((rgba & 0xFF0000) >> 16) / 255.0; michael@0: float a = ((rgba & 0xFF000000) >> 24) / 255.0; michael@0: michael@0: cairo_set_source_rgba(cairoWindow, r, g, b, a); michael@0: } michael@0: michael@0: static void michael@0: pluginDrawSolid(InstanceData* instanceData, GdkDrawable* gdkWindow, michael@0: int x, int y, int width, int height) michael@0: { michael@0: cairo_t* cairoWindow = gdk_cairo_create(gdkWindow); michael@0: michael@0: if (!instanceData->hasWidget) { michael@0: NPRect* clip = &instanceData->window.clipRect; michael@0: cairo_rectangle(cairoWindow, clip->left, clip->top, michael@0: clip->right - clip->left, clip->bottom - clip->top); michael@0: cairo_clip(cairoWindow); michael@0: } michael@0: michael@0: GdkRectangle windowRect = { x, y, width, height }; michael@0: gdk_cairo_rectangle(cairoWindow, &windowRect); michael@0: SetCairoRGBA(cairoWindow, instanceData->scriptableObject->drawColor); michael@0: michael@0: cairo_fill(cairoWindow); michael@0: cairo_destroy(cairoWindow); michael@0: } michael@0: michael@0: static void michael@0: pluginDrawWindow(InstanceData* instanceData, GdkDrawable* gdkWindow, michael@0: const GdkRectangle& invalidRect) michael@0: { michael@0: NPWindow& window = instanceData->window; michael@0: // When we have a widget, window.x/y are meaningless since our michael@0: // widget is always positioned correctly and we just draw into it at 0,0 michael@0: int x = instanceData->hasWidget ? 0 : window.x; michael@0: int y = instanceData->hasWidget ? 0 : window.y; michael@0: int width = window.width; michael@0: int height = window.height; michael@0: michael@0: notifyDidPaint(instanceData); michael@0: michael@0: if (instanceData->scriptableObject->drawMode == DM_SOLID_COLOR) { michael@0: // drawing a solid color for reftests michael@0: pluginDrawSolid(instanceData, gdkWindow, michael@0: invalidRect.x, invalidRect.y, michael@0: invalidRect.width, invalidRect.height); michael@0: return; michael@0: } michael@0: michael@0: NPP npp = instanceData->npp; michael@0: if (!npp) michael@0: return; michael@0: michael@0: const char* uaString = NPN_UserAgent(npp); michael@0: if (!uaString) michael@0: return; michael@0: michael@0: GdkGC* gdkContext = gdk_gc_new(gdkWindow); michael@0: if (!gdkContext) michael@0: return; michael@0: michael@0: if (!instanceData->hasWidget) { michael@0: NPRect* clip = &window.clipRect; michael@0: GdkRectangle gdkClip = { clip->left, clip->top, clip->right - clip->left, michael@0: clip->bottom - clip->top }; michael@0: gdk_gc_set_clip_rectangle(gdkContext, &gdkClip); michael@0: } michael@0: michael@0: // draw a grey background for the plugin frame michael@0: GdkColor grey; michael@0: grey.red = grey.blue = grey.green = 32767; michael@0: gdk_gc_set_rgb_fg_color(gdkContext, &grey); michael@0: gdk_draw_rectangle(gdkWindow, gdkContext, TRUE, x, y, width, height); michael@0: michael@0: // draw a 3-pixel-thick black frame around the plugin michael@0: GdkColor black; michael@0: black.red = black.green = black.blue = 0; michael@0: gdk_gc_set_rgb_fg_color(gdkContext, &black); michael@0: gdk_gc_set_line_attributes(gdkContext, 3, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER); michael@0: gdk_draw_rectangle(gdkWindow, gdkContext, FALSE, x + 1, y + 1, michael@0: width - 3, height - 3); michael@0: michael@0: // paint the UA string michael@0: PangoContext* pangoContext = gdk_pango_context_get(); michael@0: PangoLayout* pangoTextLayout = pango_layout_new(pangoContext); michael@0: pango_layout_set_width(pangoTextLayout, (width - 10) * PANGO_SCALE); michael@0: pango_layout_set_text(pangoTextLayout, uaString, -1); michael@0: gdk_draw_layout(gdkWindow, gdkContext, x + 5, y + 5, pangoTextLayout); michael@0: g_object_unref(pangoTextLayout); michael@0: michael@0: g_object_unref(gdkContext); michael@0: } michael@0: michael@0: static gboolean michael@0: ExposeWidget(GtkWidget* widget, GdkEventExpose* event, michael@0: gpointer user_data) michael@0: { michael@0: InstanceData* instanceData = static_cast(user_data); michael@0: pluginDrawWindow(instanceData, event->window, event->area); michael@0: return TRUE; michael@0: } michael@0: michael@0: static gboolean michael@0: MotionEvent(GtkWidget* widget, GdkEventMotion* event, michael@0: gpointer user_data) michael@0: { michael@0: InstanceData* instanceData = static_cast(user_data); michael@0: instanceData->lastMouseX = event->x; michael@0: instanceData->lastMouseY = event->y; michael@0: return TRUE; michael@0: } michael@0: michael@0: static gboolean michael@0: ButtonEvent(GtkWidget* widget, GdkEventButton* event, michael@0: gpointer user_data) michael@0: { michael@0: InstanceData* instanceData = static_cast(user_data); michael@0: instanceData->lastMouseX = event->x; michael@0: instanceData->lastMouseY = event->y; michael@0: if (event->type == GDK_BUTTON_RELEASE) { michael@0: instanceData->mouseUpEventCount++; michael@0: } michael@0: return TRUE; michael@0: } michael@0: michael@0: static gboolean michael@0: DeleteWidget(GtkWidget* widget, GdkEvent* event, gpointer user_data) michael@0: { michael@0: InstanceData* instanceData = static_cast(user_data); michael@0: // Some plugins do not expect the plug to be removed from the socket before michael@0: // the plugin instance is destroyed. e.g. bug 485125 michael@0: if (instanceData->platformData->plug) michael@0: g_error("plug removed"); // this aborts michael@0: michael@0: return FALSE; michael@0: } michael@0: michael@0: void michael@0: pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow) michael@0: { michael@0: instanceData->window = *newWindow; michael@0: michael@0: #ifdef MOZ_X11 michael@0: NPSetWindowCallbackStruct *ws_info = michael@0: static_cast(newWindow->ws_info); michael@0: instanceData->platformData->display = ws_info->display; michael@0: instanceData->platformData->visual = ws_info->visual; michael@0: instanceData->platformData->colormap = ws_info->colormap; michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: pluginWidgetInit(InstanceData* instanceData, void* oldWindow) michael@0: { michael@0: #ifdef MOZ_X11 michael@0: GtkWidget* oldPlug = instanceData->platformData->plug; michael@0: if (oldPlug) { michael@0: instanceData->platformData->plug = 0; michael@0: gtk_widget_destroy(oldPlug); michael@0: } michael@0: michael@0: GdkNativeWindow nativeWinId = michael@0: reinterpret_cast(instanceData->window.window); michael@0: michael@0: /* create a GtkPlug container */ michael@0: GtkWidget* plug = gtk_plug_new(nativeWinId); michael@0: michael@0: // Test for bugs 539138 and 561308 michael@0: if (!plug->window) michael@0: g_error("Plug has no window"); // aborts michael@0: michael@0: /* make sure the widget is capable of receiving focus */ michael@0: GTK_WIDGET_SET_FLAGS (GTK_WIDGET(plug), GTK_CAN_FOCUS); michael@0: michael@0: /* all the events that our widget wants to receive */ michael@0: gtk_widget_add_events(plug, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | michael@0: GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); michael@0: g_signal_connect(plug, "expose-event", G_CALLBACK(ExposeWidget), michael@0: instanceData); michael@0: g_signal_connect(plug, "motion_notify_event", G_CALLBACK(MotionEvent), michael@0: instanceData); michael@0: g_signal_connect(plug, "button_press_event", G_CALLBACK(ButtonEvent), michael@0: instanceData); michael@0: g_signal_connect(plug, "button_release_event", G_CALLBACK(ButtonEvent), michael@0: instanceData); michael@0: g_signal_connect(plug, "delete-event", G_CALLBACK(DeleteWidget), michael@0: instanceData); michael@0: gtk_widget_show(plug); michael@0: michael@0: instanceData->platformData->plug = plug; michael@0: #endif michael@0: } michael@0: michael@0: int16_t michael@0: pluginHandleEvent(InstanceData* instanceData, void* event) michael@0: { michael@0: #ifdef MOZ_X11 michael@0: XEvent* nsEvent = (XEvent*)event; michael@0: michael@0: switch (nsEvent->type) { michael@0: case GraphicsExpose: { michael@0: const XGraphicsExposeEvent& expose = nsEvent->xgraphicsexpose; michael@0: NPWindow& window = instanceData->window; michael@0: window.window = (void*)(expose.drawable); michael@0: michael@0: GdkNativeWindow nativeWinId = reinterpret_cast(window.window); michael@0: michael@0: GdkDisplay* gdkDisplay = gdk_x11_lookup_xdisplay(expose.display); michael@0: if (!gdkDisplay) { michael@0: g_warning("Display not opened by GDK"); michael@0: return 0; michael@0: } michael@0: // gdk_pixmap_foreign_new() doesn't check whether a GdkPixmap already michael@0: // exists, so check first. michael@0: // https://bugzilla.gnome.org/show_bug.cgi?id=590690 michael@0: GdkPixmap* gdkDrawable = michael@0: GDK_DRAWABLE(gdk_pixmap_lookup_for_display(gdkDisplay, nativeWinId)); michael@0: // If there is no existing GdkPixmap or it doesn't have a colormap then michael@0: // create our own. michael@0: if (gdkDrawable) { michael@0: GdkColormap* gdkColormap = gdk_drawable_get_colormap(gdkDrawable); michael@0: if (!gdkColormap) { michael@0: g_warning("No GdkColormap on GdkPixmap"); michael@0: return 0; michael@0: } michael@0: if (gdk_x11_colormap_get_xcolormap(gdkColormap) michael@0: != instanceData->platformData->colormap) { michael@0: g_warning("wrong Colormap"); michael@0: return 0; michael@0: } michael@0: if (gdk_x11_visual_get_xvisual(gdk_colormap_get_visual(gdkColormap)) michael@0: != instanceData->platformData->visual) { michael@0: g_warning("wrong Visual"); michael@0: return 0; michael@0: } michael@0: g_object_ref(gdkDrawable); michael@0: } else { michael@0: gdkDrawable = michael@0: GDK_DRAWABLE(gdk_pixmap_foreign_new_for_display(gdkDisplay, michael@0: nativeWinId)); michael@0: VisualID visualID = instanceData->platformData->visual->visualid; michael@0: GdkVisual* gdkVisual = michael@0: gdk_x11_screen_lookup_visual(gdk_drawable_get_screen(gdkDrawable), michael@0: visualID); michael@0: GdkColormap* gdkColormap = michael@0: gdk_x11_colormap_foreign_new(gdkVisual, michael@0: instanceData->platformData->colormap); michael@0: gdk_drawable_set_colormap(gdkDrawable, gdkColormap); michael@0: g_object_unref(gdkColormap); michael@0: } michael@0: michael@0: const NPRect& clip = window.clipRect; michael@0: if (expose.x < clip.left || expose.y < clip.top || michael@0: expose.x + expose.width > clip.right || michael@0: expose.y + expose.height > clip.bottom) { michael@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)", michael@0: expose.x, expose.y, expose.width, expose.height, michael@0: clip.left, clip.top, clip.right, clip.bottom); michael@0: return 0; michael@0: } michael@0: if (expose.x < window.x || expose.y < window.y || michael@0: expose.x + expose.width > window.x + int32_t(window.width) || michael@0: expose.y + expose.height > window.y + int32_t(window.height)) { michael@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)", michael@0: expose.x, expose.y, expose.width, expose.height, michael@0: window.x, window.y, window.width, window.height); michael@0: return 0; michael@0: } michael@0: michael@0: GdkRectangle invalidRect = michael@0: { expose.x, expose.y, expose.width, expose.height }; michael@0: pluginDrawWindow(instanceData, gdkDrawable, invalidRect); michael@0: g_object_unref(gdkDrawable); michael@0: break; michael@0: } michael@0: case MotionNotify: { michael@0: XMotionEvent* motion = &nsEvent->xmotion; michael@0: instanceData->lastMouseX = motion->x; michael@0: instanceData->lastMouseY = motion->y; michael@0: break; michael@0: } michael@0: case ButtonPress: michael@0: case ButtonRelease: { michael@0: XButtonEvent* button = &nsEvent->xbutton; michael@0: instanceData->lastMouseX = button->x; michael@0: instanceData->lastMouseY = button->y; michael@0: if (nsEvent->type == ButtonRelease) { michael@0: instanceData->mouseUpEventCount++; michael@0: } michael@0: break; michael@0: } michael@0: default: michael@0: break; michael@0: } michael@0: #endif michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge) michael@0: { michael@0: if (!instanceData->hasWidget) michael@0: return NPTEST_INT32_ERROR; michael@0: GtkWidget* plug = instanceData->platformData->plug; michael@0: if (!plug) michael@0: return NPTEST_INT32_ERROR; michael@0: GdkWindow* plugWnd = plug->window; michael@0: if (!plugWnd) michael@0: return NPTEST_INT32_ERROR; michael@0: michael@0: GdkWindow* toplevelGdk = 0; michael@0: #ifdef MOZ_X11 michael@0: Window toplevel = 0; michael@0: NPN_GetValue(instanceData->npp, NPNVnetscapeWindow, &toplevel); michael@0: if (!toplevel) michael@0: return NPTEST_INT32_ERROR; michael@0: toplevelGdk = gdk_window_foreign_new(toplevel); michael@0: #endif michael@0: if (!toplevelGdk) michael@0: return NPTEST_INT32_ERROR; michael@0: michael@0: GdkRectangle toplevelFrameExtents; michael@0: gdk_window_get_frame_extents(toplevelGdk, &toplevelFrameExtents); michael@0: g_object_unref(toplevelGdk); michael@0: michael@0: gint pluginWidth, pluginHeight; michael@0: gdk_drawable_get_size(GDK_DRAWABLE(plugWnd), &pluginWidth, &pluginHeight); michael@0: gint pluginOriginX, pluginOriginY; michael@0: gdk_window_get_origin(plugWnd, &pluginOriginX, &pluginOriginY); michael@0: gint pluginX = pluginOriginX - toplevelFrameExtents.x; michael@0: gint pluginY = pluginOriginY - toplevelFrameExtents.y; michael@0: michael@0: switch (edge) { michael@0: case EDGE_LEFT: michael@0: return pluginX; michael@0: case EDGE_TOP: michael@0: return pluginY; michael@0: case EDGE_RIGHT: michael@0: return pluginX + pluginWidth; michael@0: case EDGE_BOTTOM: michael@0: return pluginY + pluginHeight; michael@0: } michael@0: michael@0: return NPTEST_INT32_ERROR; michael@0: } michael@0: michael@0: #ifdef MOZ_X11 michael@0: static void intersectWithShapeRects(Display* display, Window window, michael@0: int kind, GdkRegion* region) michael@0: { michael@0: int count = -1, order; michael@0: XRectangle* shapeRects = michael@0: XShapeGetRectangles(display, window, kind, &count, &order); michael@0: // The documentation says that shapeRects will be nullptr when the michael@0: // extension is not supported. Unfortunately XShapeGetRectangles michael@0: // also returns nullptr when the region is empty, so we can't treat michael@0: // nullptr as failure. I hope this way is OK. michael@0: if (count < 0) michael@0: return; michael@0: michael@0: GdkRegion* shapeRegion = gdk_region_new(); michael@0: if (!shapeRegion) { michael@0: XFree(shapeRects); michael@0: return; michael@0: } michael@0: michael@0: for (int i = 0; i < count; ++i) { michael@0: XRectangle* r = &shapeRects[i]; michael@0: GdkRectangle rect = { r->x, r->y, r->width, r->height }; michael@0: gdk_region_union_with_rect(shapeRegion, &rect); michael@0: } michael@0: XFree(shapeRects); michael@0: michael@0: gdk_region_intersect(region, shapeRegion); michael@0: gdk_region_destroy(shapeRegion); michael@0: } michael@0: #endif michael@0: michael@0: static GdkRegion* computeClipRegion(InstanceData* instanceData) michael@0: { michael@0: if (!instanceData->hasWidget) michael@0: return 0; michael@0: michael@0: GtkWidget* plug = instanceData->platformData->plug; michael@0: if (!plug) michael@0: return 0; michael@0: GdkWindow* plugWnd = plug->window; michael@0: if (!plugWnd) michael@0: return 0; michael@0: michael@0: gint plugWidth, plugHeight; michael@0: gdk_drawable_get_size(GDK_DRAWABLE(plugWnd), &plugWidth, &plugHeight); michael@0: GdkRectangle pluginRect = { 0, 0, plugWidth, plugHeight }; michael@0: GdkRegion* region = gdk_region_rectangle(&pluginRect); michael@0: if (!region) michael@0: return 0; michael@0: michael@0: int pluginX = 0, pluginY = 0; michael@0: michael@0: #ifdef MOZ_X11 michael@0: Display* display = GDK_WINDOW_XDISPLAY(plugWnd); michael@0: Window window = GDK_WINDOW_XWINDOW(plugWnd); michael@0: michael@0: Window toplevel = 0; michael@0: NPN_GetValue(instanceData->npp, NPNVnetscapeWindow, &toplevel); michael@0: if (!toplevel) michael@0: return 0; michael@0: michael@0: for (;;) { michael@0: Window root; michael@0: int x, y; michael@0: unsigned int width, height, border_width, depth; michael@0: if (!XGetGeometry(display, window, &root, &x, &y, &width, &height, michael@0: &border_width, &depth)) { michael@0: gdk_region_destroy(region); michael@0: return 0; michael@0: } michael@0: michael@0: GdkRectangle windowRect = { 0, 0, static_cast(width), michael@0: static_cast(height) }; michael@0: GdkRegion* windowRgn = gdk_region_rectangle(&windowRect); michael@0: if (!windowRgn) { michael@0: gdk_region_destroy(region); michael@0: return 0; michael@0: } michael@0: intersectWithShapeRects(display, window, ShapeBounding, windowRgn); michael@0: intersectWithShapeRects(display, window, ShapeClip, windowRgn); michael@0: gdk_region_offset(windowRgn, -pluginX, -pluginY); michael@0: gdk_region_intersect(region, windowRgn); michael@0: gdk_region_destroy(windowRgn); michael@0: michael@0: // Stop now if we've reached the toplevel. Stopping here means michael@0: // clipping performed by the toplevel window is taken into account. michael@0: if (window == toplevel) michael@0: break; michael@0: michael@0: Window parent; michael@0: Window* children; michael@0: unsigned int nchildren; michael@0: if (!XQueryTree(display, window, &root, &parent, &children, &nchildren)) { michael@0: gdk_region_destroy(region); michael@0: return 0; michael@0: } michael@0: XFree(children); michael@0: michael@0: pluginX += x; michael@0: pluginY += y; michael@0: michael@0: window = parent; michael@0: } michael@0: #endif michael@0: // pluginX and pluginY are now relative to the toplevel. Make them michael@0: // relative to the window frame top-left. michael@0: GdkWindow* toplevelGdk = gdk_window_foreign_new(window); michael@0: if (!toplevelGdk) michael@0: return 0; michael@0: GdkRectangle toplevelFrameExtents; michael@0: gdk_window_get_frame_extents(toplevelGdk, &toplevelFrameExtents); michael@0: gint toplevelOriginX, toplevelOriginY; michael@0: gdk_window_get_origin(toplevelGdk, &toplevelOriginX, &toplevelOriginY); michael@0: g_object_unref(toplevelGdk); michael@0: michael@0: pluginX += toplevelOriginX - toplevelFrameExtents.x; michael@0: pluginY += toplevelOriginY - toplevelFrameExtents.y; michael@0: michael@0: gdk_region_offset(region, pluginX, pluginY); michael@0: return region; michael@0: } michael@0: michael@0: int32_t pluginGetClipRegionRectCount(InstanceData* instanceData) michael@0: { michael@0: GdkRegion* region = computeClipRegion(instanceData); michael@0: if (!region) michael@0: return NPTEST_INT32_ERROR; michael@0: michael@0: GdkRectangle* rects; michael@0: gint nrects; michael@0: gdk_region_get_rectangles(region, &rects, &nrects); michael@0: gdk_region_destroy(region); michael@0: g_free(rects); michael@0: return nrects; michael@0: } michael@0: michael@0: int32_t pluginGetClipRegionRectEdge(InstanceData* instanceData, michael@0: int32_t rectIndex, RectEdge edge) michael@0: { michael@0: GdkRegion* region = computeClipRegion(instanceData); michael@0: if (!region) michael@0: return NPTEST_INT32_ERROR; michael@0: michael@0: GdkRectangle* rects; michael@0: gint nrects; michael@0: gdk_region_get_rectangles(region, &rects, &nrects); michael@0: gdk_region_destroy(region); michael@0: if (rectIndex >= nrects) { michael@0: g_free(rects); michael@0: return NPTEST_INT32_ERROR; michael@0: } michael@0: michael@0: GdkRectangle rect = rects[rectIndex]; michael@0: g_free(rects); michael@0: michael@0: switch (edge) { michael@0: case EDGE_LEFT: michael@0: return rect.x; michael@0: case EDGE_TOP: michael@0: return rect.y; michael@0: case EDGE_RIGHT: michael@0: return rect.x + rect.width; michael@0: case EDGE_BOTTOM: michael@0: return rect.y + rect.height; michael@0: } michael@0: return NPTEST_INT32_ERROR; michael@0: } michael@0: michael@0: void pluginDoInternalConsistencyCheck(InstanceData* instanceData, string& error) michael@0: { michael@0: } michael@0: michael@0: string michael@0: pluginGetClipboardText(InstanceData* instanceData) michael@0: { michael@0: GtkClipboard* cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); michael@0: // XXX this is a BAD WAY to interact with GtkClipboard. We use this michael@0: // deprecated interface only to test nested event loop handling. michael@0: gchar* text = gtk_clipboard_wait_for_text(cb); michael@0: string retText = text ? text : ""; michael@0: michael@0: g_free(text); michael@0: michael@0: return retText; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // NB: this test is quite gross in that it's not only michael@0: // nondeterministic, but dependent on the guts of the nested glib michael@0: // event loop handling code in PluginModule. We first sleep long michael@0: // enough to make sure that the "detection timer" will be pending when michael@0: // we enter the nested glib loop, then similarly for the "process browser michael@0: // events" timer. Then we "schedule" the crasher thread to run at about the michael@0: // same time we expect that the PluginModule "process browser events" task michael@0: // will run. If all goes well, the plugin process will crash and generate the michael@0: // XPCOM "plugin crashed" task, and the browser will run that task while still michael@0: // in the "process some events" loop. michael@0: michael@0: static void* michael@0: CrasherThread(void* data) michael@0: { michael@0: // Give the parent thread a chance to send the message. michael@0: usleep(200); michael@0: michael@0: // Exit (without running atexit hooks) rather than crashing with a signal michael@0: // so as to make timing more reliable. The process terminates immediately michael@0: // rather than waiting for a thread in the parent process to attach and michael@0: // generate a minidump. michael@0: _exit(1); michael@0: michael@0: // not reached michael@0: return(nullptr); michael@0: } michael@0: michael@0: bool michael@0: pluginCrashInNestedLoop(InstanceData* instanceData) michael@0: { michael@0: // wait at least long enough for nested loop detector task to be pending ... michael@0: sleep(1); michael@0: michael@0: // Run the nested loop detector by processing all events that are waiting. michael@0: bool found_event = false; michael@0: while (g_main_context_iteration(nullptr, FALSE)) { michael@0: found_event = true; michael@0: } michael@0: if (!found_event) { michael@0: g_warning("DetectNestedEventLoop did not fire"); michael@0: return true; // trigger a test failure michael@0: } michael@0: michael@0: // wait at least long enough for the "process browser events" task to be michael@0: // pending ... michael@0: sleep(1); michael@0: michael@0: // we'll be crashing soon, note that fact now to avoid messing with michael@0: // timing too much michael@0: mozilla::NoteIntentionalCrash("plugin"); michael@0: michael@0: // schedule the crasher thread ... michael@0: pthread_t crasherThread; michael@0: if (0 != pthread_create(&crasherThread, nullptr, CrasherThread, nullptr)) { michael@0: g_warning("Failed to create thread"); michael@0: return true; // trigger a test failure michael@0: } michael@0: michael@0: // .. and hope it crashes at about the same time as the "process browser michael@0: // events" task (that should run in this loop) is being processed in the michael@0: // parent. michael@0: found_event = false; michael@0: while (g_main_context_iteration(nullptr, FALSE)) { michael@0: found_event = true; michael@0: } michael@0: if (found_event) { michael@0: g_warning("Should have crashed in ProcessBrowserEvents"); michael@0: } else { michael@0: g_warning("ProcessBrowserEvents did not fire"); michael@0: } michael@0: michael@0: // if we get here without crashing, then we'll trigger a test failure michael@0: return true; michael@0: } michael@0: michael@0: static int michael@0: SleepThenDie(Display* display) michael@0: { michael@0: mozilla::NoteIntentionalCrash("plugin"); michael@0: fprintf(stderr, "[testplugin:%d] SleepThenDie: sleeping\n", getpid()); michael@0: sleep(1); michael@0: michael@0: fprintf(stderr, "[testplugin:%d] SleepThenDie: dying\n", getpid()); michael@0: _exit(1); michael@0: } michael@0: michael@0: bool michael@0: pluginDestroySharedGfxStuff(InstanceData* instanceData) michael@0: { michael@0: // Closing the X socket results in the gdk error handler being michael@0: // invoked, which exit()s us. We want to give the parent process a michael@0: // little while to do whatever it wanted to do, so steal the IO michael@0: // handler from gdk and set up our own that delays seppuku. michael@0: XSetIOErrorHandler(SleepThenDie); michael@0: close(ConnectionNumber(GDK_DISPLAY())); michael@0: return true; michael@0: }