diff --git a/ChangeLog b/ChangeLog index 0648e9e..7261e20 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,175 @@ +2007-08-26 06:31 Gwenole Beauchesne + + * src/npw-viewer.c: Nuke the window border, it's quite disturbing + to see it during resizing. + +2007-08-25 23:03 Gwenole Beauchesne + + * ChangeLog: Generated by svn2cl. + +2007-08-25 22:48 Gwenole Beauchesne + + * NEWS, nspluginwrapper.spec: 0.9.91.5 + +2007-08-25 22:46 Gwenole Beauchesne + + * src/npruntime.c, src/npw-viewer.c: Remove NPW_DISABLE_NPRUNTIME, + npruntime support has been working for a long time now. + Similarly, remove dummy npruntime handlers now that the common + supported NPAPI version is passed down. + +2007-08-25 22:37 Gwenole Beauchesne + + * src/npw-viewer.c, src/npw-wrapper.c: Pass down common NPAPI + version supported by both the browser and nspluginwrapper. We no + longer need to check for npruntime support ourselves, rely only + on the NPAPI version reported. Yes, if the browser lies we may + crash, but that's be browser's fault anyway. + +2007-08-25 07:05 Gwenole Beauchesne + + * src/npw-wrapper.c: Implement LONG64 NPP_New() and NPP_Destroy(). + Fix NPStream functions so that it works with correct 64-bit + Konqueror versions too. i.e. check for identify stream. + +2007-08-25 07:01 Gwenole Beauchesne + + * src/npw-wrapper.c: Fix memory leak when we don't want the + NPP_Destroy() NPSavedData. Remove useless comment since the + feature was implemented. + +2007-08-25 06:59 Gwenole Beauchesne + + * src/npw-wrapper.c: Fix NPP_GetValue(NPPVformValue) and call new + g_NPN_MemAlloc(). + +2007-08-24 16:07 Gwenole Beauchesne + + * src/npw-wrapper.c: Run-time detect broken 64-bit Konqueror + browsers and use another layer of thunks in that case. + +2007-08-22 22:26 Gwenole Beauchesne + + * ChangeLog: Generated by svn2cl. + * NEWS, nspluginwrapper.spec: Updates. + +2007-08-22 22:23 Gwenole Beauchesne + + * utils/xtrace.c: Add XCreateWindow() wrapper. + +2007-08-22 22:20 Gwenole Beauchesne + + * src/npw-rpc.c, src/npw-wrapper.c: Implement NPAPI 0.15 + FORM_VALUES, i.e. support NPP_GetValue(NPPVformValue,..). + +2007-08-22 21:51 Gwenole Beauchesne + + * src/npw-rpc.h, src/npw-viewer.c, src/npw-wrapper.c: Implement + NPAPI 0.16 NPN_PushPopupsEnabledState() and + NPN_PopPopupsEnabledState(). + +2007-08-22 20:43 Gwenole Beauchesne + + * npapi/npapi.h, src/npw-viewer.c, src/npw-wrapper.c: Implement + NPAPI 0.17 RESPONSE_HEADERS, i.e. propagate NPStream::headers to + the plug-in if it is available (NULL otherwise). Fix NPStream + initialization for NPN_NewStream(). + +2007-08-22 19:23 Gwenole Beauchesne + + * Makefile: Fix build on Debian 4.0 'Etch' (and derivatives) + though libxpcom.so is currently not used and other distributions + were capable enough to compile it without the LSB build-env + hacks. + +2007-08-21 22:12 Gwenole Beauchesne + + * src/npw-viewer.c: Fix Flash Player 9 Update 3 beta plugin + support with XEMBED. + +2007-08-21 05:02 Gwenole Beauchesne + + * src/npw-viewer.c: Fix a (long) compiler warning. + +2007-08-21 05:00 Gwenole Beauchesne + + * src/npw-wrapper.c: Fix a potential buffer overflow. + +2007-08-20 21:46 Gwenole Beauchesne + + * NEWS, configure, src/npw-config-template.h, src/npw-config.c: + Add support for 64-bit plugins and fix elf_swap_sym_32() for + st_shndx swapping (Martin Stransky). + +2007-08-19 22:38 Gwenole Beauchesne + + * ChangeLog: Generated by svn2cl. + * Makefile: Make "make changelog" update the ChangeLog only, + without committing the result. + +2007-08-19 17:30 Gwenole Beauchesne + + * nspluginwrapper.spec: BuildRequires: gtk2-devel everywhere + (gtk+2-devel was MDK specific). + +2007-08-19 17:24 Gwenole Beauchesne + + * NEWS, nspluginwrapper.spec: Next release is 0.9.91.5. + +2007-08-19 17:23 Gwenole Beauchesne + + * src/npw-rpc.h, src/npw-viewer.c, src/npw-wrapper.c: Add + NPN_NewStream(), NPN_DestroyStream(), NPN_Write() support. Fix + NPP_Write() return value on error. + +2007-08-19 09:31 Gwenole Beauchesne + + * npapi/npapi.h, npapi/nptypes.h, npapi/npupp.h: NPAPI updates. + +2007-08-19 09:02 Gwenole Beauchesne + + * src/npw-viewer.c, src/npw-wrapper.c: Rewrite protocol to pass + cached MIME info and plugin name/description strings. This now + is insensitive to possible garbage printed out during the call + to the plugin functions. + +2007-08-19 08:01 Gwenole Beauchesne + + * src/npw-wrapper.c: Drop initialization/destruction locks since + RPC handlers have been called from the Mozilla main loop for a + long time now (and not from a separate thread). + +2007-08-19 07:44 Gwenole Beauchesne + + * src/npw-viewer.c: Clean-up global NP funcs wrappers. + +2007-08-19 06:45 Gwenole Beauchesne + + * src/npw-viewer.c: Override NPPVpluginNeedsXEmbed at a more + natural place. + +2007-08-18 23:43 Gwenole Beauchesne + + * src/npw-viewer.c, src/xembed.h: Fix focus problems (Debian bug + #435912). + +2007-08-18 23:13 Gwenole Beauchesne + + * utils/xtrace.c: Add a few more functions and details. + +2007-08-11 10:55 Gwenole Beauchesne + + * tests/test-plugins.html: Add link for Debian bug report #435912. + +2007-04-04 06:27 Gwenole Beauchesne + + * TODO, src/npw-config.c, src/npw-viewer.sh: Improve fix for + "don't load native plugins". + +2007-04-03 18:00 Gwenole Beauchesne + + * ChangeLog: Generated by svn2cl. + 2007-04-03 17:59 Gwenole Beauchesne * lsb-build/headers/stddef.h: Fix NULL definition for C (openSUSE diff --git a/Makefile b/Makefile index f84e442..94ab23e 100644 --- a/Makefile +++ b/Makefile @@ -121,6 +121,10 @@ libxpcom_RAWSRCS = libxpcom.c debug.c libxpcom_SOURCES = $(libxpcom_RAWSRCS:%.c=$(SRC_PATH)/src/%.c) libxpcom_OBJECTS = $(libxpcom_RAWSRCS:%.c=libxpcom-%.o) libxpcom_CFLAGS = -fPIC +ifeq ($(biarch),yes) +libxpcom_CFLAGS += -I$(LSB_INC_DIR) +libxpcom_LDFLAGS = $(LDFLAGS_32) -L$(LSB_OBJ_DIR) +endif npconfig_PROGRAM = npconfig npconfig_RAWSRCS = npw-config.c @@ -282,6 +286,7 @@ localrpm: $(archivedir)$(SRCARCHIVE).bz2 changelog: ../common/authors.xml svn_prefix=`svn info .|sed -n '/^URL *: .*\/svn\/\(.*\)$$/s//\1\//p'`; \ svn2cl --strip-prefix=$$svn_prefix --authors=../common/authors.xml || : +changelog.commit: changelog svn commit -m "Generated by svn2cl." ChangeLog $(npwrapper_LIBRARY): $(npwrapper_OBJECTS) diff --git a/NEWS b/NEWS index 8f95899..7e58915 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,14 @@ -nspluginwrapper NEWS -- history of user-visible changes. 2007-04-03 +nspluginwrapper NEWS -- history of user-visible changes. 2007-08-26 Copyright (C) 2005-2007 Gwenole Beauchesne +Version 0.9.91.5 (BETA) - 26.Aug.2007 +* Fix a memory leak in NPP_Destroy() +* Fix DiamondX XEmbed example plugin +* Fix focus problems (Debian bug #435912) +* Add support for 64-bit plugins (Martin Stransky) +* Add support for newer NPAPI 0.17 functions and variables +* Add support for broken 64-bit Konqueror versions (run-time detect) + Version 0.9.91.4 (BETA) - 03.Apr.2007 * Don't try to wrap native plugins * Fix build on NetBSD (David Brownlee) diff --git a/TODO b/TODO index d9ce9d2..af39643 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,8 @@ +- Fix "don't try to load native plugins" + * This currently works because we don't ship with native viewers + * Otherwise, the plugin OS,ARCH characterisation through ELF headers only + works for Linux / FreeBSD where they have dedicated tags + => This was only useful for Linux/i386 platforms anyway ATM - Clean-ups * Re-indent -gnu -br -brs * Better split for RPC / NAPI level routines diff --git a/configure b/configure index 010df58..6c139e8 100755 --- a/configure +++ b/configure @@ -647,6 +647,10 @@ if test "$target_cpu" = "i386" ; then echo "TARGET_ARCH=i386" >> $config_mak echo "#define TARGET_ARCH \"i386\"" >> $config_h echo "#define TARGET_I386 1" >> $config_h +elif test "$target_cpu" = "x86_64" ; then + echo "TARGET_ARCH=x86_64" >> $config_mak + echo "#define TARGET_ARCH \"x86_64\"" >> $config_h + echo "#define TARGET_X86_64 1" >> $config_h elif test "$target_cpu" = "ppc" ; then echo "TARGET_ARCH=ppc" >> $config_mak echo "#define TARGET_ARCH \"ppc\"" >> $config_h diff --git a/npapi/npapi.h b/npapi/npapi.h index 35909f8..057b4df 100644 --- a/npapi/npapi.h +++ b/npapi/npapi.h @@ -126,7 +126,7 @@ /*----------------------------------------------------------------------*/ #define NP_VERSION_MAJOR 0 -#define NP_VERSION_MINOR 14 +#define NP_VERSION_MINOR 17 /* The OS/2 version of Netscape uses RC_DATA to define the @@ -256,6 +256,16 @@ typedef struct _NPStream uint32 end; uint32 lastmodified; void* notifyData; + const char* headers; /* Response headers from host. + * Exists only for >= NPVERS_HAS_RESPONSE_HEADERS. + * Used for HTTP only; NULL for non-HTTP. + * Available from NPP_NewStream onwards. + * Plugin should copy this data before storing it. + * Includes HTTP status line and all headers, + * preferably verbatim as received from server, + * headers formatted as in HTTP ("Header: Value"), + * and newlines (\n, NOT \r\n) separating lines. + * Terminated by \n\0 (NOT \n\n\0). */ } NPStream; @@ -385,13 +395,24 @@ typedef enum { NPPVpluginScriptableInstance = (10 | NP_ABI_MASK), NPPVpluginScriptableIID = 11, - /* 12 and over are available on Mozilla builds starting with 0.9.9 */ + /* Introduced in Mozilla 0.9.9 */ NPPVjavascriptPushCallerBool = 12, - NPPVpluginKeepLibraryInMemory = 13, /* available in Mozilla 1.0 */ + + /* Introduced in Mozilla 1.0 */ + NPPVpluginKeepLibraryInMemory = 13, NPPVpluginNeedsXEmbed = 14, - /* Get the NPObject for scripting the plugin. */ - NPPVpluginScriptableNPObject = 15 + /* Get the NPObject for scripting the plugin. Introduced in Firefox + * 1.0 (NPAPI minor version 14). + */ + NPPVpluginScriptableNPObject = 15, + + /* Get the plugin value (as \0-terminated UTF-8 string data) for + * form submission if the plugin is part of a form. Use + * NPN_MemAlloc() to allocate memory for the string data. Introduced + * in Mozilla 1.8b2 (NPAPI minor version 15). + */ + NPPVformValue = 16 } NPPVariable; /* @@ -604,13 +625,17 @@ enum NPEventType { /* * Version feature information */ -#define NPVERS_HAS_STREAMOUTPUT 8 -#define NPVERS_HAS_NOTIFICATION 9 -#define NPVERS_HAS_LIVECONNECT 9 -#define NPVERS_WIN16_HAS_LIVECONNECT 9 -#define NPVERS_68K_HAS_LIVECONNECT 11 -#define NPVERS_HAS_WINDOWLESS 11 -#define NPVERS_HAS_XPCONNECT_SCRIPTING 13 +#define NPVERS_HAS_STREAMOUTPUT 8 +#define NPVERS_HAS_NOTIFICATION 9 +#define NPVERS_HAS_LIVECONNECT 9 +#define NPVERS_WIN16_HAS_LIVECONNECT 9 +#define NPVERS_68K_HAS_LIVECONNECT 11 +#define NPVERS_HAS_WINDOWLESS 11 +#define NPVERS_HAS_XPCONNECT_SCRIPTING 13 +#define NPVERS_HAS_NPRUNTIME_SCRIPTING 14 +#define NPVERS_HAS_FORM_VALUES 15 +#define NPVERS_HAS_POPUPS_ENABLED_STATE 16 +#define NPVERS_HAS_RESPONSE_HEADERS 17 /*----------------------------------------------------------------------*/ /* Function Prototypes */ @@ -663,9 +688,6 @@ void NP_LOADDS NPP_URLNotify(NPP instance, const char* url, jref NP_LOADDS NPP_GetJavaClass(void); #endif NPError NP_LOADDS NPP_GetValue(NPP instance, NPPVariable variable, void *value); -/* - * Uh, shouldn't NPP_SetValue() take an NPPVariable and not an NPNVariable? - */ NPError NP_LOADDS NPP_SetValue(NPP instance, NPNVariable variable, void *value); /* @@ -704,6 +726,8 @@ NPError NP_LOADDS NPN_SetValue(NPP instance, NPPVariable variable, void *value); void NP_LOADDS NPN_InvalidateRect(NPP instance, NPRect *invalidRect); void NP_LOADDS NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion); void NP_LOADDS NPN_ForceRedraw(NPP instance); +void NP_LOADDS NPN_PushPopupsEnabledState(NPP instance, NPBool enabled); +void NP_LOADDS NPN_PopPopupsEnabledState(NPP instance); #ifdef __cplusplus } /* end extern "C" */ diff --git a/npapi/nptypes.h b/npapi/nptypes.h index 918b649..a05d395 100644 --- a/npapi/nptypes.h +++ b/npapi/nptypes.h @@ -48,7 +48,7 @@ */ typedef int int32_t; typedef unsigned int uint32_t; -#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(IRIX) +#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(IRIX) || defined(HPUX) /* * AIX and SunOS ship a inttypes.h header that defines [u]int32_t, * but not bool for C. @@ -67,19 +67,15 @@ /* * BSD/OS ships no header that defines uint32_t, nor bool (for C) - * OpenBSD ships no header that defines uint32_t, but provides bool as a - * macro. + * OpenBSD ships no header that defines uint32_t and using its bool macro is + * unsafe. */ #if defined(bsdi) || defined(OPENBSD) typedef u_int32_t uint32_t; - #if defined(bsdi) #if !defined(__cplusplus) typedef int bool; #endif - #else /* OPENBSD is defined, so use its bool */ - #include - #endif #else /* * FreeBSD defines uint32_t and bool. diff --git a/npapi/npupp.h b/npapi/npupp.h index 274e91e..d0c00a0 100644 --- a/npapi/npupp.h +++ b/npapi/npupp.h @@ -1586,6 +1586,58 @@ typedef void (* NP_LOADDS NPN_SetExceptionUPP)(NPObject *obj, const NPUTF8 *mess #endif +/* NPN_PushPopupsEnabledStateUPP */ + +#if _NPUPP_USE_UPP_ + +typedef UniversalProcPtr NPN_PushPopupsEnabledStateUPP; +enum { + uppNPN_PushPopupsEnabledStateProcInfo = kThinkCStackBased + | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP))) + | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPBool))) + | RESULT_SIZE(SIZE_CODE(0)) +}; + +#define NewNPN_PushPopupsEnabledStateProc(FUNC) \ + (NPN_PushPopupsEnabledStateUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_PushPopupsEnabledStateProcInfo, GetCurrentArchitecture()) +#define CallNPN_PushPopupsEnabledStateProc(FUNC, ARG1, ARG2) \ + (jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_PushPopupsEnabledStateProcInfo, (ARG1), (ARG2)) + +#else + +typedef bool (* NP_LOADDS NPN_PushPopupsEnabledStateUPP)(NPP npp, NPBool enabled); +#define NewNPN_PushPopupsEnabledStateProc(FUNC) \ + ((NPN_PushPopupsEnabledStateUPP) (FUNC)) +#define CallNPN_PushPopupsEnabledStateProc(FUNC, ARG1, ARG2) \ + (*(FUNC))((ARG1), (ARG2)) + +#endif + +/* NPN_PopPopupsEnabledState */ + +#if _NPUPP_USE_UPP_ + +typedef UniversalProcPtr NPN_PopPopupsEnabledStateUPP; +enum { + uppNPN_PopPopupsEnabledStateProcInfo = kThinkCStackBased + | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP))) + | RESULT_SIZE(SIZE_CODE(0)) +}; + +#define NewNPN_PopPopupsEnabledStateProc(FUNC) \ + (NPN_PopPopupsEnabledStateUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_PopPopupsEnabledStateProcInfo, GetCurrentArchitecture()) +#define CallNPN_PopPopupsEnabledStateProc(FUNC, ARG1) \ + (jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_PopPopupsEnabledStateProcInfo, (ARG1)) + +#else + +typedef bool (* NP_LOADDS NPN_PopPopupsEnabledStateUPP)(NPP npp); +#define NewNPN_PopPopupsEnabledStateProc(FUNC) \ + ((NPN_PopPopupsEnabledStateUPP) (FUNC)) +#define CallNPN_PopPopupsEnabledStateProc(FUNC, ARG1) \ + (*(FUNC))((ARG1)) + +#endif @@ -1661,6 +1713,8 @@ typedef struct _NPNetscapeFuncs { NPN_HasMethodUPP hasmethod; NPN_ReleaseVariantValueUPP releasevariantvalue; NPN_SetExceptionUPP setexception; + NPN_PushPopupsEnabledStateUPP pushpopupsenabledstate; + NPN_PopPopupsEnabledStateUPP poppopupsenabledstate; } NPNetscapeFuncs; #ifdef XP_MAC diff --git a/nspluginwrapper.spec b/nspluginwrapper.spec index 2d30407..3ec9225 100644 --- a/nspluginwrapper.spec +++ b/nspluginwrapper.spec @@ -1,7 +1,7 @@ %define name nspluginwrapper -%define version 0.9.91.4 +%define version 0.9.91.5 %define release 1 -#define svndate 20070403 +#define svndate 20070826 # define 32-bit arch of multiarch platforms %define arch_32 %{nil} @@ -53,7 +53,7 @@ Source0: %{name}-%{version}%{?svndate:-%{svndate}}.tar.bz2 License: GPL Group: Networking/WWW Url: http://gwenole.beauchesne.info/projects/nspluginwrapper/ -BuildRequires: gtk+2-devel +BuildRequires: gtk2-devel Provides: %{name}-%{_arch} = %{version}-%{release} Requires: %{name}-%{target_arch} = %{version}-%{release} BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot @@ -158,6 +158,14 @@ fi %endif %changelog +* Sun Aug 26 2007 Gwenole Beauchesne 0.9.91.5-1 +- fix a memory leak in NPP_Destroy() +- fix DiamondX XEmbed example plugin +- fix focus problems (debian bug #435912) +- add support for 64-bit plugins (Martin Stransky) +- add support for newer NPAPI 0.17 functions and variables +- add support for broken 64-bit Konqueror versions (run-time detect) + * Mon Apr 2 2007 Gwenole Beauchesne 0.9.91.4-1 - use anonymous sockets by default - don't try to wrap native plugins diff --git a/src/gtk2xtbin.c b/src/gtk2xtbin.c new file mode 100644 index 0000000..45e87fa --- /dev/null +++ b/src/gtk2xtbin.c @@ -0,0 +1,753 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:expandtab:shiftwidth=2:tabstop=2: */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Gtk2XtBin Widget Implementation. + * + * The Initial Developer of the Original Code is + * Sun Microsystems, Inc. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * The GtkXtBin widget allows for Xt toolkit code to be used + * inside a GTK application. + */ + +#include "sysdeps.h" + +#include +#include +#include +#include + +#include +#include "xembed.h" +#include "gtk2xtbin.h" + +/* Xlib/Xt stuff */ +#include +#include +#include +#include +#include + +/* uncomment this if you want debugging information about widget + creation and destruction */ +#define DEBUG_XTBIN 1 + +static void gtk_xtbin_class_init (GtkXtBinClass *klass); +static void gtk_xtbin_init (GtkXtBin *xtbin); +static void gtk_xtbin_realize (GtkWidget *widget); +static void gtk_xtbin_unrealize (GtkWidget *widget); +static void gtk_xtbin_destroy (GtkObject *object); +static void gtk_xtbin_shutdown (GtkObject *object); + +/* Xt aware XEmbed */ +static void xt_client_init (XtClient * xtclient, + Display *xtdisplay, + Visual *xtvisual, + Colormap xtcolormap, + int xtdepth); +static void xt_client_create (XtClient * xtclient, + Window embeder, + int height, + int width ); +static void xt_client_unrealize (XtClient* xtclient); +static void xt_client_destroy (XtClient* xtclient); +static void xt_client_set_info (Widget xtplug, + unsigned long flags); +static void xt_client_event_handler (Widget w, + XtPointer client_data, + XEvent *event); +static void xt_client_handle_xembed_message (Widget w, + XtPointer client_data, + XEvent *event); +static void xt_client_focus_listener (Widget w, + XtPointer user_data, + XEvent *event); +static void xt_add_focus_listener( Widget w, XtPointer user_data ); +static void xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data); +static void xt_remove_focus_listener(Widget w, XtPointer user_data); +static void send_xembed_message (XtClient *xtclient, + long message, + long detail, + long data1, + long data2, + long time); +static int error_handler (Display *display, + XErrorEvent *error); +/* For error trap of XEmbed */ +static void trap_errors(void); +static int untrap_errors(void); +static int (*old_error_handler) (Display *, XErrorEvent *); +static int trapped_error_code = 0; + +static GtkWidgetClass *parent_class = NULL; + +GtkType +gtk_xtbin_get_type (void) +{ + static GtkType xtbin_type = 0; + + if (!xtbin_type) { + static const GtkTypeInfo xtbin_info = + { + "GtkXtBin", + sizeof (GtkXtBin), + sizeof (GtkXtBinClass), + (GtkClassInitFunc) gtk_xtbin_class_init, + (GtkObjectInitFunc) gtk_xtbin_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL + }; + xtbin_type = gtk_type_unique (GTK_TYPE_SOCKET, &xtbin_info); + } + return xtbin_type; +} + +static void +gtk_xtbin_class_init (GtkXtBinClass *klass) +{ + GtkWidgetClass *widget_class; + GtkObjectClass *object_class; + + parent_class = gtk_type_class (GTK_TYPE_SOCKET); + + widget_class = GTK_WIDGET_CLASS (klass); + widget_class->realize = gtk_xtbin_realize; + widget_class->unrealize = gtk_xtbin_unrealize; + + object_class = GTK_OBJECT_CLASS (klass); + object_class->destroy = gtk_xtbin_destroy; +} + +static void +gtk_xtbin_init (GtkXtBin *xtbin) +{ + xtbin->xtdisplay = NULL; + xtbin->parent_window = NULL; + xtbin->xtwindow = 0; + xtbin->x = 0; + xtbin->y = 0; +} + +static void +gtk_xtbin_realize (GtkWidget *widget) +{ + GtkXtBin *xtbin; + GtkAllocation allocation = { 0, 0, 200, 200 }; + gint x, y, w, h, d; /* geometry of window */ + +#ifdef DEBUG_XTBIN + printf("gtk_xtbin_realize()\n"); +#endif + + g_return_if_fail (GTK_IS_XTBIN (widget)); + + xtbin = GTK_XTBIN (widget); + + /* caculate the allocation before realize */ + gdk_window_get_geometry(xtbin->parent_window, &x, &y, &w, &h, &d); + allocation.width = w; + allocation.height = h; + gtk_widget_size_allocate (widget, &allocation); + +#ifdef DEBUG_XTBIN + printf("initial allocation %d %d %d %d\n", x, y, w, h); +#endif + + xtbin->width = widget->allocation.width; + xtbin->height = widget->allocation.height; + + /* use GtkSocket's realize */ + (*GTK_WIDGET_CLASS(parent_class)->realize)(widget); + + /* create the Xt client widget */ + xt_client_create(&(xtbin->xtclient), + gtk_socket_get_id(GTK_SOCKET(xtbin)), + xtbin->height, + xtbin->width); + xtbin->xtwindow = XtWindow(xtbin->xtclient.child_widget); + + gdk_flush(); + + /* now that we have created the xt client, add it to the socket. */ + gtk_socket_add_id(GTK_SOCKET(widget), xtbin->xtwindow); +} + + + +GtkWidget* +gtk_xtbin_new (GdkWindow *parent_window, + Display *xtdisplay, + Visual *xtvisual, + Colormap xtcolormap, + int xtdepth) +{ + GtkXtBin *xtbin; + gpointer user_data; + + assert(parent_window != NULL); + xtbin = gtk_type_new (GTK_TYPE_XTBIN); + + if (!xtbin) + return (GtkWidget*)NULL; + + /* Initialize the Xt toolkit */ + xtbin->parent_window = parent_window; + + xt_client_init(&(xtbin->xtclient), xtdisplay, xtvisual, xtcolormap, xtdepth); + + /* Build the hierachy */ + assert(xtbin->xtclient.xtdisplay != NULL); + xtbin->xtdisplay = xtbin->xtclient.xtdisplay; + gtk_widget_set_parent_window(GTK_WIDGET(xtbin), parent_window); + gdk_window_get_user_data(xtbin->parent_window, &user_data); + if (user_data) + gtk_container_add(GTK_CONTAINER(user_data), GTK_WIDGET(xtbin)); + + return GTK_WIDGET (xtbin); +} + +void +gtk_xtbin_set_position (GtkXtBin *xtbin, + gint x, + gint y) +{ + xtbin->x = x; + xtbin->y = y; + + if (GTK_WIDGET_REALIZED (xtbin)) + gdk_window_move (GTK_WIDGET (xtbin)->window, x, y); +} + +void +gtk_xtbin_resize (GtkWidget *widget, + gint width, + gint height) +{ + Arg args[2]; + GtkXtBin *xtbin = GTK_XTBIN (widget); + GtkAllocation allocation; + +#ifdef DEBUG_XTBIN + printf("gtk_xtbin_resize %p %d %d\n", (void *)widget, width, height); +#endif + + XtSetArg(args[0], XtNheight, height); + XtSetArg(args[1], XtNwidth, width); + XtSetValues(xtbin->xtclient.top_widget, args, 2); + xtbin->height = height; + xtbin->width = width; + + /* we need to send a size allocate so the socket knows about the + size changes */ + allocation.x = xtbin->x; + allocation.y = xtbin->y; + allocation.width = xtbin->width; + allocation.height = xtbin->height; + + gtk_widget_size_allocate(widget, &allocation); +} + +static void +gtk_xtbin_unrealize (GtkWidget *object) +{ + GtkXtBin *xtbin; + GtkWidget *widget; + +#ifdef DEBUG_XTBIN + printf("gtk_xtbin_unrealize()\n"); +#endif + + /* gtk_object_destroy() will already hold a refcount on object + */ + xtbin = GTK_XTBIN(object); + widget = GTK_WIDGET(object); + + GTK_WIDGET_UNSET_FLAGS (widget, GTK_VISIBLE); + if (GTK_WIDGET_REALIZED (widget)) { + xt_client_unrealize(&(xtbin->xtclient)); + } + + (*GTK_WIDGET_CLASS (parent_class)->unrealize)(widget); +} + +static void +gtk_xtbin_destroy (GtkObject *object) +{ + GtkXtBin *xtbin; + +#ifdef DEBUG_XTBIN + printf("gtk_xtbin_destroy()\n"); +#endif + + g_return_if_fail (object != NULL); + g_return_if_fail (GTK_IS_XTBIN (object)); + + xtbin = GTK_XTBIN (object); + + if(xtbin->xtwindow) { + /* remove the event handler */ + xt_client_destroy(&(xtbin->xtclient)); + xtbin->xtwindow = 0; + } + + GTK_OBJECT_CLASS(parent_class)->destroy(object); +} + +/* +* Following is the implementation of Xt XEmbedded for client side +*/ + +/* Initial Xt plugin */ +static void +xt_client_init( XtClient * xtclient, + Display *xtdisplay, + Visual *xtvisual, + Colormap xtcolormap, + int xtdepth) +{ + xtclient->top_widget = NULL; + xtclient->child_widget = NULL; + xtclient->xtdisplay = xtdisplay; + xtclient->xtvisual = xtvisual; + xtclient->xtcolormap = xtcolormap; + xtclient->xtdepth = xtdepth; +} + +/* Create the Xt client widgets +* */ +static void +xt_client_create ( XtClient* xtclient , + Window embedderid, + int height, + int width ) +{ + int n; + Arg args[6]; + Widget child_widget; + Widget top_widget; + +#ifdef DEBUG_XTBIN + printf("xt_client_create() \n"); +#endif + String app_name, app_class; + XtGetApplicationNameAndClass(xtclient->xtdisplay, &app_name, &app_class); + top_widget = XtAppCreateShell("drawingArea", app_class, + applicationShellWidgetClass, + xtclient->xtdisplay, + NULL, 0); + xtclient->top_widget = top_widget; + + /* set size of Xt window */ + n = 0; + XtSetArg(args[n], XtNheight, height);n++; + XtSetArg(args[n], XtNwidth, width);n++; + XtSetValues(top_widget, args, n); + + child_widget = XtVaCreateWidget("form", + compositeWidgetClass, + top_widget, NULL); + + n = 0; + XtSetArg(args[n], XtNheight, height);n++; + XtSetArg(args[n], XtNwidth, width);n++; + XtSetArg(args[n], XtNvisual, xtclient->xtvisual ); n++; + XtSetArg(args[n], XtNdepth, xtclient->xtdepth ); n++; + XtSetArg(args[n], XtNcolormap, xtclient->xtcolormap ); n++; + XtSetArg(args[n], XtNborderWidth, 0); n++; + XtSetValues(child_widget, args, n); + + XSync(xtclient->xtdisplay, FALSE); + xtclient->oldwindow = top_widget->core.window; + top_widget->core.window = embedderid; + + /* this little trick seems to finish initializing the widget */ +#if XlibSpecificationRelease >= 6 + XtRegisterDrawable(xtclient->xtdisplay, + embedderid, + top_widget); +#else + _XtRegisterWindow( embedderid, + top_widget); +#endif + XtRealizeWidget(child_widget); + + /* listen to all Xt events */ + XSelectInput(xtclient->xtdisplay, + XtWindow(top_widget), + 0x0FFFFF); + xt_client_set_info (child_widget, 0); + + XtManageChild(child_widget); + xtclient->child_widget = child_widget; + + /* set the event handler */ +#if 0 + XtAddEventHandler(child_widget, + 0x0FFFFF & ~ResizeRedirectMask, + TRUE, + (XtEventHandler)xt_client_event_handler, xtclient); + XtAddEventHandler(child_widget, + SubstructureNotifyMask | ButtonReleaseMask, + TRUE, + (XtEventHandler)xt_client_focus_listener, + xtclient); +#else + XtAddEventHandler(child_widget, + (SubstructureNotifyMask | ButtonReleaseMask), + TRUE, + (XtEventHandler)xt_client_event_handler, xtclient); +#endif + XSync(xtclient->xtdisplay, FALSE); +} + +static void +xt_client_unrealize ( XtClient* xtclient ) +{ +#if XlibSpecificationRelease >= 6 + XtUnregisterDrawable(xtclient->xtdisplay, + xtclient->top_widget->core.window); +#else + _XtUnregisterWindow(xtclient->top_widget->core.window, + xtclient->top_widget); +#endif + + /* flush the queue before we returning origin top_widget->core.window + or we can get X error since the window is gone */ + XSync(xtclient->xtdisplay, False); + + xtclient->top_widget->core.window = xtclient->oldwindow; + XtUnrealizeWidget(xtclient->top_widget); +} + +static void +xt_client_destroy (XtClient* xtclient) +{ + if(xtclient->top_widget) { + XtRemoveEventHandler(xtclient->child_widget, 0x0FFFFF, TRUE, + (XtEventHandler)xt_client_event_handler, xtclient); + XtDestroyWidget(xtclient->top_widget); + xtclient->top_widget = NULL; + } +} + +static void +xt_client_set_info (Widget xtplug, unsigned long flags) +{ + unsigned long buffer[2]; + + Atom infoAtom = XInternAtom(XtDisplay(xtplug), "_XEMBED_INFO", False); + + buffer[1] = 0; /* Protocol version */ + buffer[1] = flags; + + XChangeProperty (XtDisplay(xtplug), XtWindow(xtplug), + infoAtom, infoAtom, 32, + PropModeReplace, + (unsigned char *)buffer, 2); +} + +static void +xt_client_handle_xembed_message(Widget w, XtPointer client_data, XEvent *event) +{ + XtClient *xtplug = (XtClient*)client_data; + switch (event->xclient.data.l[1]) + { + case XEMBED_EMBEDDED_NOTIFY: + break; + case XEMBED_WINDOW_ACTIVATE: +#ifdef DEBUG_XTBIN + printf("Xt client get XEMBED_WINDOW_ACTIVATE\n"); +#endif + break; + case XEMBED_WINDOW_DEACTIVATE: +#ifdef DEBUG_XTBIN + printf("Xt client get XEMBED_WINDOW_DEACTIVATE\n"); +#endif + break; + case XEMBED_MODALITY_ON: +#ifdef DEBUG_XTBIN + printf("Xt client get XEMBED_MODALITY_ON\n"); +#endif + break; + case XEMBED_MODALITY_OFF: +#ifdef DEBUG_XTBIN + printf("Xt client get XEMBED_MODALITY_OFF\n"); +#endif + break; + case XEMBED_FOCUS_IN: + case XEMBED_FOCUS_OUT: + { + XEvent xevent; + memset(&xevent, 0, sizeof(xevent)); + + if(event->xclient.data.l[1] == XEMBED_FOCUS_IN) { +#ifdef DEBUG_XTBIN + printf("XTEMBED got focus in\n"); +#endif + xevent.xfocus.type = FocusIn; + } + else { +#ifdef DEBUG_XTBIN + printf("XTEMBED got focus out\n"); +#endif + xevent.xfocus.type = FocusOut; + } + + xevent.xfocus.window = XtWindow(xtplug->child_widget); + xevent.xfocus.display = XtDisplay(xtplug->child_widget); + XSendEvent(XtDisplay(xtplug->child_widget), + xevent.xfocus.window, + False, NoEventMask, + &xevent ); + XSync( XtDisplay(xtplug->child_widget), False); + } + break; + default: + break; + } /* End of XEmbed Message */ +} + +static void +xt_client_event_handler( Widget w, XtPointer client_data, XEvent *event) +{ + XtClient *xtplug = (XtClient*)client_data; + + switch(event->type) + { + case ClientMessage: + /* Handle xembed message */ + if (event->xclient.message_type== + XInternAtom (XtDisplay(xtplug->child_widget), + "_XEMBED", False)) { + xt_client_handle_xembed_message(w, client_data, event); + } + break; + case ReparentNotify: + break; + case MappingNotify: + xt_client_set_info (w, XEMBED_MAPPED); + break; + case UnmapNotify: + xt_client_set_info (w, 0); + break; + case FocusIn: + send_xembed_message ( xtplug, + XEMBED_REQUEST_FOCUS, 0, 0, 0, 0); + break; + case FocusOut: + break; + case KeyPress: +#ifdef DEBUG_XTBIN + printf("Key Press Got!\n"); +#endif + break; + default: + break; + } /* End of switch(event->type) */ +} + +static void +send_xembed_message (XtClient *xtclient, + long message, + long detail, + long data1, + long data2, + long time) +{ + XEvent xevent; + Window w=XtWindow(xtclient->top_widget); + Display* dpy=xtclient->xtdisplay; + int errorcode; + + memset(&xevent,0,sizeof(xevent)); + xevent.xclient.window = w; + xevent.xclient.type = ClientMessage; + xevent.xclient.message_type = XInternAtom(dpy,"_XEMBED",False); + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = time; + xevent.xclient.data.l[1] = message; + xevent.xclient.data.l[2] = detail; + xevent.xclient.data.l[3] = data1; + xevent.xclient.data.l[4] = data2; + + trap_errors (); + XSendEvent (dpy, w, False, NoEventMask, &xevent); + XSync (dpy,False); + + if((errorcode = untrap_errors())) { +#ifdef DEBUG_XTBIN + printf("send_xembed_message error(%d)!!!\n",errorcode); +#endif + } +} + +static int +error_handler(Display *display, XErrorEvent *error) +{ + trapped_error_code = error->error_code; + return 0; +} + +static void +trap_errors(void) +{ + trapped_error_code =0; + old_error_handler = XSetErrorHandler(error_handler); +} + +static int +untrap_errors(void) +{ + XSetErrorHandler(old_error_handler); + if(trapped_error_code) { +#ifdef DEBUG_XTBIN + printf("Get X Window Error = %d\n", trapped_error_code); +#endif + } + return trapped_error_code; +} + +static void +xt_client_focus_listener( Widget w, XtPointer user_data, XEvent *event) +{ + Display *dpy = XtDisplay(w); + XtClient *xtclient = user_data; + Window win = XtWindow(w); + + switch(event->type) + { + case CreateNotify: + if(event->xcreatewindow.parent == win) { + Widget child=XtWindowToWidget( dpy, event->xcreatewindow.window); + if (child) + xt_add_focus_listener_tree(child, user_data); + } + break; + case DestroyNotify: + xt_remove_focus_listener( w, user_data); + break; + case ReparentNotify: + if(event->xreparent.parent == win) { + /* I am the new parent */ + Widget child=XtWindowToWidget(dpy, event->xreparent.window); + if (child) + xt_add_focus_listener_tree( child, user_data); + } + else if(event->xreparent.window == win) { + /* I am the new child */ + } + else { + /* I am the old parent */ + } + break; + case ButtonRelease: +#if 0 + XSetInputFocus(dpy, XtWindow(xtclient->child_widget), RevertToParent, event->xbutton.time); +#endif + send_xembed_message ( xtclient, + XEMBED_REQUEST_FOCUS, 0, 0, 0, 0); + break; + default: + break; + } /* End of switch(event->type) */ +} + +static void +xt_add_focus_listener( Widget w, XtPointer user_data) +{ + XWindowAttributes attr; + long eventmask; + XtClient *xtclient = user_data; + int errorcode; + + trap_errors (); + XGetWindowAttributes(XtDisplay(w), XtWindow(w), &attr); + eventmask = attr.your_event_mask | SubstructureNotifyMask | ButtonReleaseMask; + XSelectInput(XtDisplay(w), + XtWindow(w), + eventmask); + + XtAddEventHandler(w, + SubstructureNotifyMask | ButtonReleaseMask, + TRUE, + (XtEventHandler)xt_client_focus_listener, + xtclient); + untrap_errors(); +} + +static void +xt_remove_focus_listener(Widget w, XtPointer user_data) +{ + int errorcode; + + trap_errors (); + XtRemoveEventHandler(w, SubstructureNotifyMask | ButtonReleaseMask, TRUE, + (XtEventHandler)xt_client_focus_listener, user_data); + + untrap_errors(); +} + +static void +xt_add_focus_listener_tree ( Widget treeroot, XtPointer user_data) +{ + Window win = XtWindow(treeroot); + Window *children; + Window root, parent; + Display *dpy = XtDisplay(treeroot); + unsigned int i, nchildren; + + /* ensure we don't add more than once */ + xt_remove_focus_listener( treeroot, user_data); + xt_add_focus_listener( treeroot, user_data); + trap_errors(); + if(!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) { + untrap_errors(); + return; + } + + if(untrap_errors()) + return; + + for(i=0; i +#include +#include +#include + +#define GTKXTBIN_API(type) type + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct _GtkXtBin GtkXtBin; +typedef struct _GtkXtBinClass GtkXtBinClass; + +#define GTK_TYPE_XTBIN (gtk_xtbin_get_type ()) +#define GTK_XTBIN(obj) (GTK_CHECK_CAST ((obj), \ + GTK_TYPE_XTBIN, GtkXtBin)) +#define GTK_XTBIN_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), \ + GTK_TYPE_XTBIN, GtkXtBinClass)) +#define GTK_IS_XTBIN(obj) (GTK_CHECK_TYPE ((obj), \ + GTK_TYPE_XTBIN)) +#define GTK_IS_XTBIN_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), \ + GTK_TYPE_XTBIN)) +typedef struct _XtClient XtClient; + +struct _XtClient { + Display *xtdisplay; + Widget top_widget; /* The toplevel widget */ + Widget child_widget; /* The embedded widget */ + Visual *xtvisual; + int xtdepth; + Colormap xtcolormap; + Window oldwindow; +}; + +struct _GtkXtBin +{ + GtkSocket gsocket; + GdkWindow *parent_window; + Display *xtdisplay; /* Xt Toolkit Display */ + + Window xtwindow; /* Xt Toolkit XWindow */ + gint x, y; + gint width, height; + XtClient xtclient; /* Xt Client for XEmbed */ +}; + +struct _GtkXtBinClass +{ + GtkSocketClass widget_class; +}; + +GTKXTBIN_API(GtkType) gtk_xtbin_get_type (void); +GTKXTBIN_API(GtkWidget *) gtk_xtbin_new (GdkWindow *parent_window, + Display *xtdisplay, + Visual *xtvisual, + Colormap xtcolormap, + int xtdepth); +GTKXTBIN_API(void) gtk_xtbin_set_position (GtkXtBin *xtbin, + gint x, + gint y); +GTKXTBIN_API(void) gtk_xtbin_resize (GtkWidget *widget, + gint width, + gint height); + +typedef struct _XtTMRec { + XtTranslations translations; /* private to Translation Manager */ + XtBoundActions proc_table; /* procedure bindings for actions */ + struct _XtStateRec *current_state; /* Translation Manager state ptr */ + unsigned long lastEventTime; +} XtTMRec, *XtTM; + +typedef struct _CorePart { + Widget self; /* pointer to widget itself */ + WidgetClass widget_class; /* pointer to Widget's ClassRec */ + Widget parent; /* parent widget */ + XrmName xrm_name; /* widget resource name quarkified */ + Boolean being_destroyed; /* marked for destroy */ + XtCallbackList destroy_callbacks; /* who to call when widget destroyed */ + XtPointer constraints; /* constraint record */ + Position x, y; /* window position */ + Dimension width, height; /* window dimensions */ + Dimension border_width; /* window border width */ + Boolean managed; /* is widget geometry managed? */ + Boolean sensitive; /* is widget sensitive to user events*/ + Boolean ancestor_sensitive; /* are all ancestors sensitive? */ + XtEventTable event_table; /* private to event dispatcher */ + XtTMRec tm; /* translation management */ + XtTranslations accelerators; /* accelerator translations */ + Pixel border_pixel; /* window border pixel */ + Pixmap border_pixmap; /* window border pixmap or NULL */ + WidgetList popup_list; /* list of popups */ + Cardinal num_popups; /* how many popups */ + String name; /* widget resource name */ + Screen *screen; /* window's screen */ + Colormap colormap; /* colormap */ + Window window; /* window ID */ + Cardinal depth; /* number of planes in window */ + Pixel background_pixel; /* window background pixel */ + Pixmap background_pixmap; /* window background pixmap or NULL */ + Boolean visible; /* is window mapped and not occluded?*/ + Boolean mapped_when_managed;/* map window if it's managed? */ +} CorePart; + +typedef struct _WidgetRec { + CorePart core; + } WidgetRec, CoreRec; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* __GTK_XTBIN_H__ */ + diff --git a/src/npruntime.c b/src/npruntime.c index 2e91a22..9b636bb 100644 --- a/src/npruntime.c +++ b/src/npruntime.c @@ -605,8 +605,10 @@ bool npobject_bridge_new(void) void npobject_bridge_destroy(void) { - g_hash_table_destroy(g_npobject_ids); - g_hash_table_destroy(g_npobjects); + if (g_npobject_ids) + g_hash_table_destroy(g_npobject_ids); + if (g_npobjects) + g_hash_table_destroy(g_npobjects); } void npobject_hash_table_insert(NPObject *npobj, NPObjectInfo *npobj_info) @@ -636,71 +638,3 @@ NPObject *npobject_lookup(uint32_t npobj_id) { return g_hash_table_lookup(g_npobject_ids, (void *)(uintptr_t)npobj_id); } - - -/* ====================================================================== */ -/* === Dummy npruntime accessors === */ -/* ====================================================================== */ - -static void dummy_function_do_nothing(void) -{ -} - -static int dummy_function_return_zero(void) -{ - return 0; -} - -static bool dummy_function_return_void_variant(NPVariant *variant) -{ - if (variant) - VOID_TO_NPVARIANT(*variant); - return false; -} - -static bool dummy_NPN_Invoke(NPP npp, NPObject* obj, NPIdentifier methodName, - const NPVariant *args, uint32_t argCount, NPVariant *result) -{ - return dummy_function_return_void_variant(result); -} - -static bool dummy_NPN_InvokeDefault(NPP npp, NPObject* obj, const NPVariant *args, - uint32_t argCount, NPVariant *result) -{ - return dummy_function_return_void_variant(result); -} - -static bool dummy_NPN_Evaluate(NPP npp, NPObject* obj, NPString *script, - NPVariant *result) -{ - return dummy_function_return_void_variant(result); -} - -static bool dummy_NPN_GetProperty(NPP npp, NPObject* obj, NPIdentifier propertyName, - NPVariant *result) -{ - return dummy_function_return_void_variant(result); -} - -void npruntime_init_callbacks(NPNetscapeFuncs *mozilla_funcs) -{ - mozilla_funcs->getstringidentifier = NewNPN_GetStringIdentifierProc(dummy_function_return_zero); - mozilla_funcs->getstringidentifiers = NewNPN_GetStringIdentifiersProc(dummy_function_do_nothing); - mozilla_funcs->getintidentifier = NewNPN_GetIntIdentifierProc(dummy_function_return_zero); - mozilla_funcs->identifierisstring = NewNPN_IdentifierIsStringProc(dummy_function_return_zero); - mozilla_funcs->utf8fromidentifier = NewNPN_UTF8FromIdentifierProc(dummy_function_return_zero); - mozilla_funcs->intfromidentifier = NewNPN_IntFromIdentifierProc(dummy_function_return_zero); - mozilla_funcs->createobject = NewNPN_CreateObjectProc(dummy_function_return_zero); - mozilla_funcs->retainobject = NewNPN_RetainObjectProc(dummy_function_return_zero); - mozilla_funcs->releaseobject = NewNPN_ReleaseObjectProc(dummy_function_do_nothing); - mozilla_funcs->invoke = NewNPN_InvokeProc(dummy_NPN_Invoke); - mozilla_funcs->invokeDefault = NewNPN_InvokeDefaultProc(dummy_NPN_InvokeDefault); - mozilla_funcs->evaluate = NewNPN_EvaluateProc(dummy_NPN_Evaluate); - mozilla_funcs->getproperty = NewNPN_GetPropertyProc(dummy_NPN_GetProperty); - mozilla_funcs->setproperty = NewNPN_SetPropertyProc(dummy_function_return_zero); - mozilla_funcs->removeproperty = NewNPN_RemovePropertyProc(dummy_function_return_zero); - mozilla_funcs->hasproperty = NewNPN_HasPropertyProc(dummy_function_return_zero); - mozilla_funcs->hasmethod = NewNPN_HasMethodProc(dummy_function_return_zero); - mozilla_funcs->releasevariantvalue = NewNPN_ReleaseVariantValueProc(dummy_function_do_nothing); - mozilla_funcs->setexception = NewNPN_SetExceptionProc(dummy_function_do_nothing); -} diff --git a/src/npw-config-template.h b/src/npw-config-template.h new file mode 100644 index 0000000..c7b82a2 --- /dev/null +++ b/src/npw-config-template.h @@ -0,0 +1,178 @@ +/* + * npw-config-template.h - nspluginwrapper configuration tool, template defs + * + * nspluginwrapper (C) 2005-2007 Gwenole Beauchesne + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef ELF_CLASS +#ifndef ElfW +# if ELF_CLASS == ELFCLASS32 +# define ElfW(x) Elf32_ ## x +# define ELFW(x) ELF32_ ## x +# define FUNC(x) x ## _32 +# define bswap_ad(x) bswap_32(x) +# else +# define ElfW(x) Elf64_ ## x +# define ELFW(x) ELF64_ ## x +# define FUNC(x) x ## _64 +# define bswap_ad(x) bswap_64(x) +# endif +#endif + +static void FUNC(elf_swap_ehdr)(ElfW(Ehdr) *hdr) +{ + hdr->e_type = bswap_16(hdr->e_type); + hdr->e_machine = bswap_16(hdr->e_machine); + hdr->e_version = bswap_32(hdr->e_version); + hdr->e_entry = bswap_ad(hdr->e_entry); + hdr->e_phoff = bswap_ad(hdr->e_phoff); + hdr->e_shoff = bswap_ad(hdr->e_shoff); + hdr->e_flags = bswap_32(hdr->e_flags); + hdr->e_ehsize = bswap_16(hdr->e_ehsize); + hdr->e_phentsize = bswap_16(hdr->e_phentsize); + hdr->e_phnum = bswap_16(hdr->e_phnum); + hdr->e_shentsize = bswap_16(hdr->e_shentsize); + hdr->e_shnum = bswap_16(hdr->e_shnum); + hdr->e_shstrndx = bswap_16(hdr->e_shstrndx); +} + +static void FUNC(elf_swap_shdr)(ElfW(Shdr) *shdr) +{ + shdr->sh_name = bswap_32(shdr->sh_name); + shdr->sh_type = bswap_32(shdr->sh_type); + shdr->sh_flags = bswap_ad(shdr->sh_flags); + shdr->sh_addr = bswap_ad(shdr->sh_addr); + shdr->sh_offset = bswap_ad(shdr->sh_offset); + shdr->sh_size = bswap_ad(shdr->sh_size); + shdr->sh_link = bswap_32(shdr->sh_link); + shdr->sh_info = bswap_32(shdr->sh_info); + shdr->sh_addralign = bswap_ad(shdr->sh_addralign); + shdr->sh_entsize = bswap_ad(shdr->sh_entsize); +} + +static void FUNC(elf_swap_sym)(ElfW(Sym) *sym) +{ + sym->st_name = bswap_32(sym->st_name); + sym->st_value = bswap_ad(sym->st_value); + sym->st_size = bswap_ad(sym->st_size); + sym->st_shndx = bswap_16(sym->st_shndx); +} + +static bool FUNC(is_elf_plugin_fd)(int fd, NPW_PluginInfo *out_plugin_info) +{ + int i; + bool ret = false; + + ElfW(Ehdr) ehdr; + if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) + return false; + + bool do_swap = (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) && !is_little_endian(); + if (do_swap) + FUNC(elf_swap_ehdr)(&ehdr); + + if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) + return false; + if (ehdr.e_type != ET_DYN) + return false; + if (ehdr.e_version != EV_CURRENT) + return false; + + if (out_plugin_info) { + const char *target_arch = ""; + switch (ehdr.e_machine) { + case EM_386: target_arch = "i386"; break; + case EM_X86_64: target_arch = "x86_64"; break; + case EM_SPARC: target_arch = "sparc"; break; + case EM_PPC: target_arch = "ppc"; break; + case EM_PPC64: target_arch = "ppc64"; break; + } + strcpy(out_plugin_info->target_arch, target_arch); + const char *target_os = ""; + switch (ehdr.e_ident[EI_OSABI]) { + case ELFOSABI_LINUX: target_os = "linux"; break; + case ELFOSABI_SOLARIS: target_os = "solaris"; break; + case ELFOSABI_FREEBSD: target_os = "freebsd"; break; + } + strcpy(out_plugin_info->target_os, target_os); + } + + ElfW(Shdr) *shdr = (ElfW(Shdr) *)load_data(fd, ehdr.e_shoff, ehdr.e_shnum * sizeof(*shdr)); + if (do_swap) { + for (i = 0; i < ehdr.e_shnum; i++) + FUNC(elf_swap_shdr)(&shdr[i]); + } + + char **sdata = (char **)calloc(ehdr.e_shnum, sizeof(*sdata)); + for (i = 0; i < ehdr.e_shnum; i++) { + ElfW(Shdr) *sec = &shdr[i]; + if (sec->sh_type != SHT_NOBITS) + sdata[i] = (char *)load_data(fd, sec->sh_offset, sec->sh_size); + } + + ElfW(Shdr) *symtab_sec = NULL; + for (i = 0; i < ehdr.e_shnum; i++) { + ElfW(Shdr) *sec = &shdr[i]; + if (sec->sh_type == SHT_DYNSYM + && strcmp(sdata[ehdr.e_shstrndx] + sec->sh_name, ".dynsym") == 0) { + symtab_sec = sec; + break; + } + } + if (symtab_sec == NULL) + goto done; + ElfW(Sym) *symtab = (ElfW(Sym) *)sdata[symtab_sec - shdr]; + char *strtab = sdata[symtab_sec->sh_link]; + + int nb_syms = symtab_sec->sh_size / sizeof(*symtab); + if (do_swap) { + for (i = 0; i < nb_syms; i++) + FUNC(elf_swap_sym)(&symtab[i]); + } + + int nb_np_syms; + int is_wrapper_plugin = 0; + for (i = 0, nb_np_syms = 0; i < nb_syms; i++) { + ElfW(Sym) *sym = &symtab[i]; + const char *name = strtab + sym->st_name; + if (ELFW(ST_BIND)(sym->st_info) != STB_GLOBAL) + continue; + if (ELFW(ST_TYPE)(sym->st_info) == STT_OBJECT && strcmp(name, "NPW_Plugin") == 0) + is_wrapper_plugin = 1; + if (ELFW(ST_TYPE)(sym->st_info) != STT_FUNC) + continue; + if (!strcmp(name, "NP_GetMIMEDescription") || + !strcmp(name, "NP_Initialize") || + !strcmp(name, "NP_Shutdown")) + nb_np_syms++; + } + ret = (nb_np_syms == 3) && !is_wrapper_plugin; + + done: + for (i = 0; i < ehdr.e_shnum; i++) + free(sdata[i]); + free(sdata); + free(shdr); + return ret; +} + +#undef bswap_ad +#undef ElfW +#undef ELFW +#undef FUNC +#undef ELF_CLASS +#endif diff --git a/src/npw-config.c b/src/npw-config.c index 3539eff..3375026 100644 --- a/src/npw-config.c +++ b/src/npw-config.c @@ -254,11 +254,31 @@ static const char **get_mozilla_plugin_dirs(void) ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) -/* 32-bit ELF base types. */ -typedef uint16_t Elf32_Half; -typedef uint32_t Elf32_Word; -typedef uint32_t Elf32_Addr; -typedef uint32_t Elf32_Off; +#undef bswap_64 +#define bswap_64(x) \ + ( \ + (((x) & 0xff00000000000000) >> 56) | (((x) & 0x00ff000000000000) >> 40) | \ + (((x) & 0x00000000000000ff) << 56) | (((x) & 0x000000000000ff00) << 40) | \ + (((x) & 0x0000ff0000000000) >> 24) | (((x) & 0x000000ff00000000) >> 8) | \ + (((x) & 0x0000000000ff0000) << 24) | (((x) & 0x00000000ff000000) << 8) \ + ) + +/* 32-bit ELF base types. */ +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Off; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Word; + +/* 64-bit ELF base types. */ +typedef uint64_t Elf64_Addr; +typedef uint16_t Elf64_Half; +typedef int16_t Elf64_SHalf; +typedef uint64_t Elf64_Off; +typedef int32_t Elf64_Sword; +typedef uint32_t Elf64_Word; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; /* The ELF file header. This appears at the start of every ELF file. */ #define EI_NIDENT (16) @@ -281,6 +301,30 @@ typedef struct Elf32_Half e_shstrndx; /* Section header string table index */ } Elf32_Ehdr; +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */ + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +/* Base structure - used to distinguish between 32/64 bit version */ +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ +} Elf_hdr_base; + #define EI_MAG0 0 /* File identification byte 0 index */ #define ELFMAG0 0x7f /* Magic number byte 0 */ #define EI_MAG1 1 /* File identification byte 1 index */ @@ -326,6 +370,21 @@ typedef struct Elf32_Word sh_entsize; /* Entry size if section holds table */ } Elf32_Shdr; +typedef struct +{ + Elf64_Word sh_name; /* Section name, index in string tbl */ + Elf64_Word sh_type; /* Type of section */ + Elf64_Xword sh_flags; /* Miscellaneous section attributes */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Size of section in bytes */ + Elf64_Word sh_link; /* Index of another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + + #define SHT_NOBITS 8 /* Program space with no data (bss) */ #define SHT_DYNSYM 11 /* Dynamic linker symbol table */ @@ -340,18 +399,27 @@ typedef struct Elf32_Half st_shndx; /* Section index */ } Elf32_Sym; -#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -#define ELF32_ST_TYPE(val) ((val) & 0xf) +typedef struct +{ + Elf64_Word st_name; /* Symbol name, index in string tbl */ + unsigned char st_info; /* Type and binding attributes */ + unsigned char st_other; /* No defined meaning, 0 */ + Elf64_Half st_shndx; /* Associated section index */ + Elf64_Addr st_value; /* Value of the symbol */ + Elf64_Xword st_size; /* Associated symbol size */ +} Elf64_Sym; + +#define ELF_ST_BIND(x) ((x) >> 4) +#define ELF_ST_TYPE(x) (((unsigned int) x) & 0xf) +#define ELF32_ST_BIND(x) ELF_ST_BIND(x) +#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x) +#define ELF64_ST_BIND(x) ELF_ST_BIND(x) +#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x) + #define STB_GLOBAL 1 /* Global symbol */ #define STT_OBJECT 1 /* Symbol is a data object */ #define STT_FUNC 2 /* Symbol is a code object */ -/* We handle 32-bit ELF plugins only */ -#undef ELF_CLASS -#define ELF_CLASS ELFCLASS32 -#define ElfW(x) Elf32_ ## x -#define ELFW(x) ELF32_ ## x - void *load_data(int fd, long offset, unsigned int size) { char *data = (char *)malloc(size); @@ -374,51 +442,16 @@ static bool is_little_endian(void) return x.b[0] == 0x04; } -static void elf_swap_ehdr(ElfW(Ehdr) *hdr) -{ - hdr->e_type = bswap_16(hdr->e_type); - hdr->e_machine = bswap_16(hdr->e_machine); - hdr->e_version = bswap_32(hdr->e_version); - hdr->e_entry = bswap_32(hdr->e_entry); - hdr->e_phoff = bswap_32(hdr->e_phoff); - hdr->e_shoff = bswap_32(hdr->e_shoff); - hdr->e_flags = bswap_32(hdr->e_flags); - hdr->e_ehsize = bswap_16(hdr->e_ehsize); - hdr->e_phentsize = bswap_16(hdr->e_phentsize); - hdr->e_phnum = bswap_16(hdr->e_phnum); - hdr->e_shentsize = bswap_16(hdr->e_shentsize); - hdr->e_shnum = bswap_16(hdr->e_shnum); - hdr->e_shstrndx = bswap_16(hdr->e_shstrndx); -} +#define ELF_CLASS ELFCLASS32 +#include "npw-config-template.h" -static void elf_swap_shdr(ElfW(Shdr) *shdr) -{ - shdr->sh_name = bswap_32(shdr->sh_name); - shdr->sh_type = bswap_32(shdr->sh_type); - shdr->sh_flags = bswap_32(shdr->sh_flags); - shdr->sh_addr = bswap_32(shdr->sh_addr); - shdr->sh_offset = bswap_32(shdr->sh_offset); - shdr->sh_size = bswap_32(shdr->sh_size); - shdr->sh_link = bswap_32(shdr->sh_link); - shdr->sh_info = bswap_32(shdr->sh_info); - shdr->sh_addralign = bswap_32(shdr->sh_addralign); - shdr->sh_entsize = bswap_32(shdr->sh_entsize); -} - -static void elf_swap_sym(ElfW(Sym) *sym) -{ - sym->st_name = bswap_32(sym->st_name); - sym->st_value = bswap_32(sym->st_value); - sym->st_size = bswap_32(sym->st_size); - sym->st_shndx = bswap_32(sym->st_shndx); -} +#define ELF_CLASS ELFCLASS64 +#include "npw-config-template.h" static bool is_plugin_fd(int fd, NPW_PluginInfo *out_plugin_info) { - int i; - bool ret = false; - - ElfW(Ehdr) ehdr; + Elf_hdr_base ehdr; + if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) return false; @@ -428,91 +461,13 @@ static bool is_plugin_fd(int fd, NPW_PluginInfo *out_plugin_info) || ehdr.e_ident[EI_MAG3] != ELFMAG3) return false; - bool do_swap = (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) && !is_little_endian(); - if (do_swap) - elf_swap_ehdr(&ehdr); + lseek(fd, 0, SEEK_SET); - if (ehdr.e_ident[EI_CLASS] != ELF_CLASS) - return false; - if (ehdr.e_type != ET_DYN) - return false; - if (ehdr.e_version != EV_CURRENT) - return false; - - if (out_plugin_info) { - const char *target_arch = ""; - switch (ehdr.e_machine) { - case EM_386: target_arch = "i386"; break; - case EM_SPARC: target_arch = "sparc"; break; - case EM_PPC: target_arch = "ppc"; break; - } - strcpy(out_plugin_info->target_arch, target_arch); - const char *target_os = ""; - switch (ehdr.e_ident[EI_OSABI]) { - case ELFOSABI_LINUX: target_os = "linux"; break; - case ELFOSABI_SOLARIS: target_os = "solaris"; break; - case ELFOSABI_FREEBSD: target_os = "freebsd"; break; - } - strcpy(out_plugin_info->target_os, target_os); + switch (ehdr.e_ident[EI_CLASS]) { + case ELFCLASS32: return is_elf_plugin_fd_32(fd, out_plugin_info); + case ELFCLASS64: return is_elf_plugin_fd_64(fd, out_plugin_info); } - - ElfW(Shdr) *shdr = (ElfW(Shdr) *)load_data(fd, ehdr.e_shoff, ehdr.e_shnum * sizeof(*shdr)); - if (do_swap) { - for (i = 0; i < ehdr.e_shnum; i++) - elf_swap_shdr(&shdr[i]); - } - - char **sdata = (char **)calloc(ehdr.e_shnum, sizeof(*sdata)); - for (i = 0; i < ehdr.e_shnum; i++) { - ElfW(Shdr) *sec = &shdr[i]; - if (sec->sh_type != SHT_NOBITS) - sdata[i] = (char *)load_data(fd, sec->sh_offset, sec->sh_size); - } - - ElfW(Shdr) *symtab_sec = NULL; - for (i = 0; i < ehdr.e_shnum; i++) { - ElfW(Shdr) *sec = &shdr[i]; - if (sec->sh_type == SHT_DYNSYM - && strcmp(sdata[ehdr.e_shstrndx] + sec->sh_name, ".dynsym") == 0) { - symtab_sec = sec; - break; - } - } - if (symtab_sec == NULL) - goto done; - ElfW(Sym) *symtab = (ElfW(Sym) *)sdata[symtab_sec - shdr]; - char *strtab = sdata[symtab_sec->sh_link]; - - int nb_syms = symtab_sec->sh_size / sizeof(*symtab); - if (do_swap) { - for (i = 0; i < nb_syms; i++) - elf_swap_sym(&symtab[i]); - } - - int nb_np_syms; - int is_wrapper_plugin = 0; - for (i = 0, nb_np_syms = 0; i < nb_syms; i++) { - ElfW(Sym) *sym = &symtab[i]; - const char *name = strtab + sym->st_name; - if (ELFW(ST_BIND)(sym->st_info) != STB_GLOBAL) - continue; - if (ELFW(ST_TYPE)(sym->st_info) == STT_OBJECT && strcmp(name, "NPW_Plugin") == 0) - is_wrapper_plugin = 1; - if (ELFW(ST_TYPE)(sym->st_info) != STT_FUNC) - continue; - if (!strcmp(name, "NP_GetMIMEDescription") || - !strcmp(name, "NP_Initialize") || - !strcmp(name, "NP_Shutdown")) - nb_np_syms++; - } - ret = (nb_np_syms == 3) && !is_wrapper_plugin; - - done: - for (i = 0; i < ehdr.e_shnum; i++) - free(sdata[i]); - free(sdata); - free(shdr); - return ret; + return false; } enum { @@ -548,6 +503,12 @@ static int detect_plugin_viewer(const char *filename, NPW_PluginInfo *out_plugin else target_os_table[0] = NULL; + // don't wrap plugins for host OS/ARCH + if (out_plugin_info + && out_plugin_info->target_arch && strcmp(out_plugin_info->target_arch, HOST_ARCH) == 0 + && out_plugin_info->target_os && strcmp(out_plugin_info->target_os, HOST_OS) == 0) + return EXIT_VIEWER_NATIVE; + for (int i = 0; i < target_arch_table_size; i++) { const char *target_arch = target_arch_table[i]; if (target_arch == NULL) @@ -562,8 +523,6 @@ static int detect_plugin_viewer(const char *filename, NPW_PluginInfo *out_plugin const char *target_os = target_os_table[j]; if (target_os == NULL) continue; - if (strcmp(target_arch, HOST_ARCH) == 0 && strcmp(target_os, HOST_OS) == 0) - return EXIT_VIEWER_NATIVE; // don't wrap plugins for host OS/ARCH char viewer_path[PATH_MAX]; sprintf(viewer_path, "%s/%s/%s", viewer_arch_path, target_os, NPW_VIEWER); if (access(viewer_path, F_OK) != 0) diff --git a/src/npw-rpc.c b/src/npw-rpc.c index fbf0e6d..ec5bbc0 100644 --- a/src/npw-rpc.c +++ b/src/npw-rpc.c @@ -66,6 +66,7 @@ int rpc_type_of_NPPVariable(int variable) switch (variable) { case NPPVpluginNameString: case NPPVpluginDescriptionString: + case NPPVformValue: // byte values of 0 does not appear in the UTF-8 encoding but for U+0000 type = RPC_TYPE_STRING; break; case NPPVpluginWindowBool: diff --git a/src/npw-rpc.h b/src/npw-rpc.h index faddd62..491f376 100644 --- a/src/npw-rpc.h +++ b/src/npw-rpc.h @@ -37,6 +37,11 @@ enum { RPC_METHOD_NPN_STATUS, RPC_METHOD_NPN_PRINT_DATA, RPC_METHOD_NPN_REQUEST_READ, + RPC_METHOD_NPN_NEW_STREAM, + RPC_METHOD_NPN_DESTROY_STREAM, + RPC_METHOD_NPN_WRITE, + RPC_METHOD_NPN_PUSH_POPUPS_ENABLED_STATE, + RPC_METHOD_NPN_POP_POPUPS_ENABLED_STATE, RPC_METHOD_NPP_NEW, RPC_METHOD_NPP_DESTROY, diff --git a/src/npw-viewer.c b/src/npw-viewer.c index 83b2abf..cee7e79 100644 --- a/src/npw-viewer.c +++ b/src/npw-viewer.c @@ -43,6 +43,7 @@ #include "rpc.h" #include "npw-rpc.h" #include "utils.h" +#include "xembed.h" #define XP_UNIX 1 #define MOZ_X11 1 @@ -60,10 +61,6 @@ // Define to use XEMBED #define USE_XEMBED 1 -// XXX define which widget to use -#define USE_GTK_TOOLKIT 1 -#define USE_X11_TOOLKIT 0 - // XXX unimplemented functions #define UNIMPLEMENTED() npw_printf("WARNING: Unimplemented function %s at line %d\n", __func__, __LINE__) @@ -78,24 +75,29 @@ typedef struct _PluginInstance { bool use_xembed; NPWindow window; uint32_t width, height; - Window old_window; - Window focus_window; - Window old_focus_window; -#if USE_GTK_TOOLKIT - GtkWidget *top_window; -#else - Window top_window; -#endif - Widget top_widget; - Widget form; + void *toolkit_data; } PluginInstance; // Browser side data for an NPStream instance typedef struct _StreamInstance { NPStream *stream; uint32_t stream_id; + int is_plugin_stream; } StreamInstance; +// Xt wrapper data +typedef struct _XtData { + Window browser_window; + Widget top_widget; + Widget form; +} XtData; + +// Gtk wrapper data +typedef struct _GtkData { + GtkWidget *container; + GtkWidget *socket; +} GtkData; + /* ====================================================================== */ /* === X Toolkit glue === */ @@ -154,27 +156,161 @@ extern void XtResizeWidget( _XtDimension /* border_width */ ); -// Update focus to plugin window -// XXX I am not convinced this is the really corrent... -static void xt_client_focus_listener(Widget w, XtPointer user_data, XEvent *event, Boolean *cont) +// Dummy X error handler +static int trapped_error_code; +static int (*old_error_handler)(Display *, XErrorEvent *); + +static int error_handler(Display *display, XErrorEvent *error) { - PluginInstance *plugin = (PluginInstance *)user_data; + trapped_error_code = error->error_code; + return 0; +} + +static void trap_errors(void) +{ + trapped_error_code = 0; + old_error_handler = XSetErrorHandler(error_handler); +} + +static int untrap_errors(void) +{ + XSetErrorHandler(old_error_handler); + return trapped_error_code; +} + +// Install the _XEMBED_INFO property +static void xt_client_set_info(Widget w, unsigned long flags) +{ + Atom atom_XEMBED_INFO = XInternAtom(x_display, "_XEMBED_INFO", False); + + unsigned long buffer[2]; + buffer[1] = 0; /* Protocol version */ + buffer[1] = flags; + XChangeProperty(XtDisplay(w), XtWindow(w), + atom_XEMBED_INFO, + atom_XEMBED_INFO, + 32, PropModeReplace, (unsigned char *)buffer, 2); +} + +// Send an XEMBED message to the specified window +static void send_xembed_message(Display *display, + Window window, + long message, + long detail, + long data1, + long data2) +{ + XEvent xevent; + memset(&xevent, 0, sizeof(xevent)); + xevent.xclient.window = window; + xevent.xclient.type = ClientMessage; + xevent.xclient.message_type = XInternAtom(display, "_XEMBED", False); + xevent.xclient.format = 32; + xevent.xclient.data.l[0] = CurrentTime; // XXX: evil? + xevent.xclient.data.l[1] = message; + xevent.xclient.data.l[2] = detail; + xevent.xclient.data.l[3] = data1; + xevent.xclient.data.l[4] = data2; + + trap_errors(); + XSendEvent(display, xevent.xclient.window, False, NoEventMask, &xevent); + XSync(display, False); + untrap_errors(); +} + +/* + * NSPluginWrapper strategy to handle input focus. + * + * - XEMBED must be enabled with NPPVpluginNeedsXEmbed set to + * PR_TRUE. This causes Firefox to pass a plain GtkSocket ID into + * NPWindow::window. i.e. the GtkXtBin window ID is NOT used. + * + * - A click into the plugin window sends an XEMBED_REQUEST_FOCUS + * event to the parent (socket) window. + * + * - An XFocusEvent is simulated when XEMBED_FOCUS_IN and + * XEMBED_FOCUS_OUT messages arrive to the plugin window. + * + * - Key events are forwarded from the top widget (which window was + * reparented to the socket window) to the actual canvas (form). + * + * Reference checkpoints, i.e. check the following test cases still + * work if you want to change the policy. + * + * [1] Debian bug #435912 + * + * + * Goto to stage 1, use arrow keys to move the block. Now, click + * outside of the game window, use arrow keys to try to move the + * block: it should NOT move. + * + * [2] User reported bug + * + * + * Choose a language and then a mirror. Do NOT move the cursor out of + * the plugin window. Now, click into the "Login" input field and try + * to type in something, you should have the input focus. + * + * [3] Additional test that came in during debugging + * + * Go to either [1] or [2], double-click the browser URL entry to + * select it completely. Now, click into the Flash plugin area. The + * URL selection MUST now be unselected AND using the Tab key selects + * various fields in the Flash window (e.g. menu items for [1]). + */ + +// Simulate client focus +static void xt_client_simulate_focus(Widget w, int type) +{ + XEvent xevent; + memset(&xevent, 0, sizeof(xevent)); + xevent.xfocus.type = type; + xevent.xfocus.window = XtWindow(w); + xevent.xfocus.display = XtDisplay(w); + xevent.xfocus.mode = NotifyNormal; + xevent.xfocus.detail = NotifyAncestor; + + trap_errors(); + XSendEvent(XtDisplay(w), xevent.xfocus.window, False, NoEventMask, &xevent); + XSync(XtDisplay(w), False); + untrap_errors(); +} + +// Various hacks for decent events filtery +static void xt_client_event_handler(Widget w, XtPointer client_data, XEvent *event, Boolean *cont) +{ + XtData *toolkit = (XtData *)client_data; switch (event->type) { - case ButtonRelease: - if (plugin->focus_window != XtWindow(plugin->form)) { - XSetInputFocus(x_display, XtWindow(plugin->form), RevertToParent, event->xbutton.time); - plugin->focus_window = XtWindow(plugin->form); - XSync(x_display, False); + case ClientMessage: + // Handle XEMBED messages, in particular focus changes + if (event->xclient.message_type == XInternAtom(x_display, "_XEMBED", False)) { + switch (event->xclient.data.l[1]) { + case XEMBED_FOCUS_IN: + xt_client_simulate_focus(toolkit->form, FocusIn); + break; + case XEMBED_FOCUS_OUT: + xt_client_simulate_focus(toolkit->form, FocusOut); + break; + } } break; - case LeaveNotify: - if (plugin->old_focus_window && plugin->focus_window != plugin->old_focus_window) { - XSetInputFocus(x_display, plugin->old_focus_window, RevertToParent, event->xcrossing.time); - plugin->focus_window = plugin->old_focus_window; - XSync(x_display, False); + case KeyPress: + case KeyRelease: + // Propagate key events down to the actual window + if (event->xkey.window == XtWindow(toolkit->top_widget)) { + event->xkey.window = XtWindow(toolkit->form); + trap_errors(); + XSendEvent(XtDisplay(toolkit->form), event->xfocus.window, False, NoEventMask, event); + XSync(XtDisplay(toolkit->form), False); + untrap_errors(); + *cont = False; } break; + case ButtonRelease: + // Notify the embedder that we want the input focus + send_xembed_message(XtDisplay(w), toolkit->browser_window, XEMBED_REQUEST_FOCUS, 0, 0, 0); + break; } } @@ -282,86 +418,65 @@ static int create_window(PluginInstance *plugin, NPWindow *window) NPSetWindowCallbackStruct *ws_info = window->ws_info; // create the new window - if (plugin->use_xembed) + if (plugin->use_xembed) { + // XXX it's my understanding that the following plug-forwarding + // machinery should not be necessary but it turns out it is for + // Flash Player 9 Update 3 beta + GtkData *toolkit = calloc(1, sizeof(*toolkit)); + if (toolkit == NULL) + return -1; + toolkit->container = gtk_plug_new((GdkNativeWindow)window->window); + if (toolkit->container == NULL) + return -1; + gtk_widget_show(toolkit->container); + toolkit->socket = gtk_socket_new(); + if (toolkit->socket == NULL) + return -1; + gtk_widget_show(toolkit->socket); + gtk_container_add(GTK_CONTAINER(toolkit->container), toolkit->socket); + gtk_widget_show_all(toolkit->container); + window->window = (void *)gtk_socket_get_id(GTK_SOCKET(toolkit->socket)); + plugin->toolkit_data = toolkit; return 0; -#if USE_GTK_TOOLKIT - plugin->top_window = gtk_plug_new(0); - gtk_window_resize(GTK_WINDOW(plugin->top_window), plugin->width, plugin->height); - gtk_widget_set_size_request(plugin->top_window, window->width, window->height); - gtk_widget_show(plugin->top_window); - { - GdkDrawable *win = GDK_DRAWABLE(plugin->top_window->window); - XReparentWindow(GDK_DRAWABLE_XDISPLAY(win), - GDK_DRAWABLE_XID(win), - (Window)window->window, 0, 0); - - XMapWindow(GDK_DRAWABLE_XDISPLAY(win), - GDK_DRAWABLE_XID(win)); } -#else - XSetWindowAttributes attr; - attr.bit_gravity = NorthWestGravity; - attr.colormap = ws_info->colormap; - attr.event_mask = - ButtonMotionMask | - ButtonPressMask | - ButtonReleaseMask | - KeyPressMask | - KeyReleaseMask | - EnterWindowMask | - LeaveWindowMask | - PointerMotionMask | - StructureNotifyMask | - VisibilityChangeMask | - FocusChangeMask | - ExposureMask; - unsigned long mask = CWBitGravity | CWEventMask; - if (attr.colormap) - mask |= CWColormap; + XtData *toolkit = malloc(sizeof(*toolkit)); + if (toolkit == NULL) + return -1; - plugin->top_window = XCreateWindow(x_display, (Window)window->window, - 0, 0, window->width, window->height, - 0, ws_info->depth, InputOutput, ws_info->visual, mask, &attr); + String app_name, app_class; + XtGetApplicationNameAndClass(x_display, &app_name, &app_class); + Widget top_widget = XtVaAppCreateShell("drawingArea", app_class, topLevelShellWidgetClass, x_display, + XtNoverrideRedirect, True, + XtNborderWidth, 0, + XtNbackgroundPixmap, None, + XtNwidth, window->width, + XtNheight, window->height, + NULL); - XSelectInput(x_display, plugin->top_window, ExposureMask); - - XMapWindow(x_display, plugin->top_window); - XFlush(x_display); -#endif - - Widget top_widget = XtAppCreateShell("drawingArea", "npw-viewer", applicationShellWidgetClass, x_display, NULL, 0); - plugin->top_widget = top_widget; - XtResizeWidget(top_widget, window->width, window->height, 0); - - Widget form = XtVaCreateWidget("form", compositeWidgetClass, top_widget, - XtNdepth, ws_info->depth, - XtNvisual, ws_info->visual, - XtNcolormap, ws_info->colormap, - NULL); - plugin->form = form; - XtResizeWidget(form, window->width, window->height, 0); - -#if USE_GTK_TOOLKIT - plugin->old_window = top_widget->core.window; - top_widget->core.window = GDK_WINDOW_XWINDOW(plugin->top_window->window); - XtRegisterDrawable(x_display, GDK_WINDOW_XWINDOW(plugin->top_window->window), top_widget); -#else - plugin->old_window = top_widget->core.window; - top_widget->core.window = plugin->top_window; - XtRegisterDrawable(x_display, plugin->top_window, top_widget); -#endif + Widget form = XtVaCreateManagedWidget("form", compositeWidgetClass, top_widget, + XtNdepth, ws_info->depth, + XtNvisual, ws_info->visual, + XtNcolormap, ws_info->colormap, + XtNborderWidth, 0, + XtNbackgroundPixmap, None, + XtNwidth, window->width, + XtNheight, window->height, + NULL); + XtRealizeWidget(top_widget); + XReparentWindow(x_display, XtWindow(top_widget), (Window)window->window, 0, 0); XtRealizeWidget(form); - XtManageChild(form); - int revert_to; - plugin->old_focus_window = None; - XGetInputFocus(x_display, &plugin->old_focus_window, &revert_to); - XtAddEventHandler(form, (LeaveWindowMask | ButtonReleaseMask), True, xt_client_focus_listener, plugin); - plugin->focus_window = plugin->old_focus_window; - XSync(x_display, False); + XSelectInput(x_display, XtWindow(top_widget), 0x0fffff); + XtAddEventHandler(top_widget, (SubstructureNotifyMask|KeyPress|KeyRelease), True, xt_client_event_handler, toolkit); + XtAddEventHandler(form, (ButtonReleaseMask), True, xt_client_event_handler, toolkit); + xt_client_set_info(form, 0); + plugin->toolkit_data = toolkit; + toolkit->top_widget = top_widget; + toolkit->form = form; + toolkit->browser_window = (Window)window->window; window->window = (void *)XtWindow(form); return 0; } @@ -372,10 +487,18 @@ static int update_window(PluginInstance *plugin, NPWindow *window) if (plugin->window.width != window->width || plugin->window.height != window->height) { plugin->window.width = window->width; plugin->window.height = window->height; - if (plugin->form) - XtResizeWidget(plugin->form, plugin->window.width, plugin->window.height, 0); - if (plugin->top_widget) - XtResizeWidget(plugin->top_widget, plugin->window.width, plugin->window.height, 0); + if (plugin->toolkit_data) { + if (plugin->use_xembed) { + // XXX check window size changes are already caught per the XEMBED protocol + } + else { + XtData *toolkit = (XtData *)plugin->toolkit_data; + if (toolkit->form) + XtResizeWidget(toolkit->form, plugin->window.width, plugin->window.height, 0); + if (toolkit->top_widget) + XtResizeWidget(toolkit->top_widget, plugin->window.width, plugin->window.height, 0); + } + } } return 0; } @@ -383,35 +506,23 @@ static int update_window(PluginInstance *plugin, NPWindow *window) // Destroy window static void destroy_window(PluginInstance *plugin) { - if (plugin->old_focus_window) { - XtRemoveEventHandler(plugin->form, (LeaveWindowMask | ButtonReleaseMask), True, xt_client_focus_listener, plugin); - plugin->old_focus_window = None; - } - - if (plugin->top_widget) { - XSync(x_display, False); -#if USE_GTK_TOOLKIT - XtUnregisterDrawable(x_display, GDK_WINDOW_XWINDOW(plugin->top_window->window)); -#else - XtUnregisterDrawable(x_display, plugin->top_window); -#endif - XSync(x_display, False); - plugin->top_widget->core.window = plugin->old_window; - XtUnrealizeWidget(plugin->top_widget); - XtDestroyWidget(plugin->top_widget); - XSync(x_display, False); - plugin->top_widget = NULL; - } - - if (plugin->top_window) { -#if USE_GTK_TOOLKIT - gtk_widget_destroy((GtkWidget *)plugin->top_window); - plugin->top_window = NULL; -#else - XDestroyWindow(x_display, plugin->top_window); - plugin->top_window = None; -#endif - XSync(x_display, False); + if (plugin->toolkit_data) { + if (plugin->use_xembed) { + GtkData *toolkit = (GtkData *)plugin->toolkit_data; + if (toolkit->container) + gtk_widget_destroy(toolkit->container); + } + else { + XtData *toolkit = (XtData *)plugin->toolkit_data; + if (toolkit->top_widget) { + XSync(x_display, False); + XtUnrealizeWidget(toolkit->top_widget); + XtDestroyWidget(toolkit->top_widget); + XSync(x_display, False); + } + } + free(plugin->toolkit_data); + plugin->toolkit_data = NULL; } destroy_window_attributes(&plugin->window); @@ -424,20 +535,12 @@ static void destroy_window(PluginInstance *plugin) static char *g_user_agent = NULL; +// Does browser have specified feature? +#define NPN_HAS_FEATURE(FEATURE) ((mozilla_funcs.version & 0xff) >= NPVERS_HAS_##FEATURE) + // Netscape exported functions static NPNetscapeFuncs mozilla_funcs; -// Closes and deletes a stream -static NPError -g_NPN_DestroyStream(NPP instance, NPStream *stream, NPError reason) -{ - D(bug("NPN_DestroyStream instance=%p\n", instance)); - - UNIMPLEMENTED(); - - return NPERR_GENERIC_ERROR; -} - // Forces a repaint message for a windowless plug-in static void g_NPN_ForceRedraw(NPP instance) @@ -660,17 +763,6 @@ g_NPN_MemFree(void *ptr) free(ptr); } -// Requests the creation of a new data stream produced by the plug-in and consumed by the browser -static NPError -g_NPN_NewStream(NPP instance, NPMIMEType type, const char *target, NPStream **stream) -{ - D(bug("NPN_NewStream instance=%p\n", instance)); - - UNIMPLEMENTED(); - - return NPERR_GENERIC_ERROR; -} - // Posts data to a URL static NPError invoke_NPN_PostURL(NPP instance, const char *url, const char *target, uint32 len, const char *buf, NPBool file) @@ -891,15 +983,256 @@ g_NPN_UserAgent(NPP instance) return g_user_agent; } +// Requests the creation of a new data stream produced by the plug-in and consumed by the browser +static NPError +invoke_NPN_NewStream(NPP instance, NPMIMEType type, const char *target, NPStream **pstream) +{ + int error = rpc_method_invoke(g_rpc_connection, + RPC_METHOD_NPN_NEW_STREAM, + RPC_TYPE_NPP, instance, + RPC_TYPE_STRING, type, + RPC_TYPE_STRING, target, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_NewStream() invoke", error); + return NPERR_OUT_OF_MEMORY_ERROR; + } + + int32_t ret; + uint32_t stream_id; + char *url; + uint32_t end; + uint32_t lastmodified; + void *notifyData; + char *headers; + error = rpc_method_wait_for_reply(g_rpc_connection, + RPC_TYPE_INT32, &ret, + RPC_TYPE_UINT32, &stream_id, + RPC_TYPE_STRING, &url, + RPC_TYPE_UINT32, &end, + RPC_TYPE_UINT32, &lastmodified, + RPC_TYPE_NP_NOTIFY_DATA, ¬ifyData, + RPC_TYPE_STRING, &headers, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_NewStream() wait for reply", error); + return NPERR_GENERIC_ERROR; + } + + NPStream *stream = NULL; + if (ret == NPERR_NO_ERROR) { + if ((stream = malloc(sizeof(*stream))) == NULL) + return NPERR_OUT_OF_MEMORY_ERROR; + memset(stream, 0, sizeof(*stream)); + + StreamInstance *stream_ndata; + if ((stream_ndata = malloc(sizeof(*stream_ndata))) == NULL) { + free(stream); + return NPERR_OUT_OF_MEMORY_ERROR; + } + stream->ndata = stream_ndata; + stream->url = url; + stream->end = end; + stream->lastmodified = lastmodified; + stream->notifyData = notifyData; + stream->headers = headers; + memset(stream_ndata, 0, sizeof(*stream_ndata)); + stream_ndata->stream_id = stream_id; + id_link(stream_id, stream_ndata); + stream_ndata->stream = stream; + stream_ndata->is_plugin_stream = 1; + } + else { + if (url) + free(url); + if (headers) + free(headers); + } + *pstream = stream; + + return ret; +} + +static NPError +g_NPN_NewStream(NPP instance, NPMIMEType type, const char *target, NPStream **stream) +{ + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + if (stream == NULL) + return NPERR_INVALID_PARAM; + *stream = NULL; + + D(bug("NPN_NewStream instance=%p\n", instance)); + NPError ret = invoke_NPN_NewStream(instance, type, target, stream); + D(bug(" return: %d [%s]\n", ret, string_of_NPError(ret))); + return ret; +} + +// Closes and deletes a stream +static NPError +invoke_NPN_DestroyStream(NPP instance, NPStream *stream, NPError reason) +{ + int error = rpc_method_invoke(g_rpc_connection, + RPC_METHOD_NPN_DESTROY_STREAM, + RPC_TYPE_NPP, instance, + RPC_TYPE_NP_STREAM, stream, + RPC_TYPE_INT32, (int32_t)reason, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_DestroyStream() invoke", error); + return NPERR_GENERIC_ERROR; + } + + int32_t ret; + error = rpc_method_wait_for_reply(g_rpc_connection, + RPC_TYPE_INT32, &ret, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_DestroyStream() wait for reply", error); + return NPERR_GENERIC_ERROR; + } + + return ret; +} + +static NPError +g_NPN_DestroyStream(NPP instance, NPStream *stream, NPError reason) +{ + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + if (stream == NULL) + return NPERR_INVALID_PARAM; + + D(bug("NPN_DestroyStream instance=%p, stream=%p, reason=%s\n", + instance, stream, string_of_NPReason(reason))); + NPError ret = invoke_NPN_DestroyStream(instance, stream, reason); + D(bug(" return: %d [%s]\n", ret, string_of_NPError(ret))); + + // Mozilla calls NPP_DestroyStream() for its streams, keep stream + // info in that case + StreamInstance *stream_ndata = stream->ndata; + if (stream_ndata && stream_ndata->is_plugin_stream) { + id_remove(stream_ndata->stream_id); + free(stream_ndata); + free((char *)stream->url); + free((char *)stream->headers); + free(stream); + } + + return ret; +} + // Pushes data into a stream produced by the plug-in and consumed by the browser +static int +invoke_NPN_Write(NPP instance, NPStream *stream, int32 len, void *buf) +{ + int error = rpc_method_invoke(g_rpc_connection, + RPC_METHOD_NPN_WRITE, + RPC_TYPE_NPP, instance, + RPC_TYPE_NP_STREAM, stream, + RPC_TYPE_ARRAY, RPC_TYPE_CHAR, len, buf, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_Write() invoke", error); + return -1; + } + + int32_t ret; + error = rpc_method_wait_for_reply(g_rpc_connection, + RPC_TYPE_INT32, &ret, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_Write() wait for reply", error); + return -1; + } + + return ret; +} + static int32 g_NPN_Write(NPP instance, NPStream *stream, int32 len, void *buf) { - D(bug("NPN_Write instance=%d\n", instance)); + if (instance == NULL) + return -1; - UNIMPLEMENTED(); + if (stream == NULL) + return -1; - return -1; + D(bug("NPN_Write instance=%p, stream=%p, len=%d, buf=%p\n", instance, stream, len, buf)); + int32 ret = invoke_NPN_Write(instance, stream, len, buf); + D(bug(" return: %d\n", ret)); + return ret; +} + +// Enable popups while executing code where popups should be enabled +static void +invoke_NPN_PushPopupsEnabledState(NPP instance, NPBool enabled) +{ + int error = rpc_method_invoke(g_rpc_connection, + RPC_METHOD_NPN_PUSH_POPUPS_ENABLED_STATE, + RPC_TYPE_NPP, instance, + RPC_TYPE_UINT32, (uint32_t)enabled, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_PushPopupsEnabledState() invoke", error); + return; + } + + error = rpc_method_wait_for_reply(g_rpc_connection, RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) + npw_perror("NPN_PushPopupsEnabledState() wait for reply", error); +} + +static void +g_NPN_PushPopupsEnabledState(NPP instance, NPBool enabled) +{ + if (instance == NULL) + return; + + D(bug("NPN_PushPopupsEnabledState instance=%p, enabled=%d\n", instance, enabled)); + invoke_NPN_PushPopupsEnabledState(instance, enabled); + D(bug(" done\n")); +} + +// Restore popups state +static void +invoke_NPN_PopPopupsEnabledState(NPP instance) +{ + int error = rpc_method_invoke(g_rpc_connection, + RPC_METHOD_NPN_POP_POPUPS_ENABLED_STATE, + RPC_TYPE_NPP, instance, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_PopPopupsEnabledState() invoke", error); + return; + } + + error = rpc_method_wait_for_reply(g_rpc_connection, RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) + npw_perror("NPN_PopPopupsEnabledState() wait for reply", error); +} + +static void +g_NPN_PopPopupsEnabledState(NPP instance) +{ + if (instance == NULL) + return; + + D(bug("NPN_PopPopupsEnabledState instance=%p\n", instance)); + invoke_NPN_PopPopupsEnabledState(instance); + D(bug(" done\n")); } @@ -1673,19 +2006,19 @@ static NPPluginFuncs plugin_funcs; // Allows the browser to query the plug-in supported formats typedef char * (*NP_GetMIMEDescriptionUPP)(void); -static NP_GetMIMEDescriptionUPP g_NP_GetMIMEDescription = NULL; +static NP_GetMIMEDescriptionUPP g_plugin_NP_GetMIMEDescription = NULL; // Allows the browser to query the plug-in for information typedef NPError (*NP_GetValueUPP)(void *instance, NPPVariable variable, void *value); -static NP_GetValueUPP g_NP_GetValue = NULL; +static NP_GetValueUPP g_plugin_NP_GetValue = NULL; // Provides global initialization for a plug-in typedef NPError (*NP_InitializeUPP)(NPNetscapeFuncs *moz_funcs, NPPluginFuncs *plugin_funcs); -static NP_InitializeUPP g_NP_Initialize = NULL; +static NP_InitializeUPP g_plugin_NP_Initialize = NULL; // Provides global deinitialization for a plug-in typedef NPError (*NP_ShutdownUPP)(void); -static NP_ShutdownUPP g_NP_Shutdown = NULL; +static NP_ShutdownUPP g_plugin_NP_Shutdown = NULL; /* ====================================================================== */ @@ -1693,6 +2026,18 @@ static NP_ShutdownUPP g_NP_Shutdown = NULL; /* ====================================================================== */ // NP_GetMIMEDescription +static char * +g_NP_GetMIMEDescription(void) +{ + if (g_plugin_NP_GetMIMEDescription == NULL) + return NULL; + + D(bug("NP_GetMIMEDescription\n")); + char *str = g_plugin_NP_GetMIMEDescription(); + D(bug(" return: %s\n", str ? str : "")); + return str; +} + static int handle_NP_GetMIMEDescription(rpc_connection_t *connection) { D(bug("handle_NP_GetMIMEDescription\n")); @@ -1702,6 +2047,18 @@ static int handle_NP_GetMIMEDescription(rpc_connection_t *connection) } // NP_GetValue +static NPError +g_NP_GetValue(NPPVariable variable, void *value) +{ + if (g_plugin_NP_GetValue == NULL) + return NPERR_INVALID_FUNCTABLE_ERROR; + + D(bug("NP_GetValue variable=%d\n", variable)); + NPError ret = g_plugin_NP_GetValue(NULL, variable, value); + D(bug(" return: %d\n", ret)); + return ret; +} + static int handle_NP_GetValue(rpc_connection_t *connection) { D(bug("handle_NP_GetValue\n")); @@ -1713,32 +2070,47 @@ static int handle_NP_GetValue(rpc_connection_t *connection) return error; } - char *str = NULL; - NPError ret = g_NP_GetValue ? g_NP_GetValue(NULL, variable, (void *)&str) : NPERR_GENERIC_ERROR; - return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_STRING, str, RPC_TYPE_INVALID); + NPError ret = NPERR_GENERIC_ERROR; + int variable_type = rpc_type_of_NPPVariable(variable); + + switch (variable_type) { + case RPC_TYPE_STRING: + { + char *str = NULL; + ret = g_NP_GetValue(variable, (void *)&str); + return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_STRING, str, RPC_TYPE_INVALID); + } + case RPC_TYPE_INT32: + { + uint32_t n = 0; + ret = g_NP_GetValue(variable, (void *)&n); + return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INT32, n, RPC_TYPE_INVALID); + } + case RPC_TYPE_BOOLEAN: + { + PRBool b = PR_FALSE; + ret = g_NP_GetValue(variable, (void *)&b); + return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_BOOLEAN, b, RPC_TYPE_INVALID); + } + } + + npw_printf("ERROR: only basic types are supported in NP_GetValue()\n"); + abort(); } // NP_Initialize -static int handle_NP_Initialize(rpc_connection_t *connection) +static NPError +g_NP_Initialize(uint32_t version) { - D(bug("handle_NP_Initialize\n")); - - uint32_t has_npruntime = 0; - int error = rpc_method_get_args(connection, - RPC_TYPE_UINT32, &has_npruntime, - RPC_TYPE_INVALID); - - if (error != RPC_ERROR_NO_ERROR) { - npw_perror("NP_Initialize() get args", error); - return error; - } + if (g_plugin_NP_Initialize == NULL) + return NPERR_INVALID_FUNCTABLE_ERROR; memset(&plugin_funcs, 0, sizeof(plugin_funcs)); plugin_funcs.size = sizeof(plugin_funcs); memset(&mozilla_funcs, 0, sizeof(mozilla_funcs)); mozilla_funcs.size = sizeof(mozilla_funcs); - mozilla_funcs.version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; + mozilla_funcs.version = version; mozilla_funcs.geturl = NewNPN_GetURLProc(g_NPN_GetURL); mozilla_funcs.posturl = NewNPN_PostURLProc(g_NPN_PostURL); mozilla_funcs.requestread = NewNPN_RequestReadProc(g_NPN_RequestRead); @@ -1760,15 +2132,10 @@ static int handle_NP_Initialize(rpc_connection_t *connection) mozilla_funcs.invalidaterect = NewNPN_InvalidateRectProc(g_NPN_InvalidateRect); mozilla_funcs.invalidateregion = NewNPN_InvalidateRegionProc(g_NPN_InvalidateRegion); mozilla_funcs.forceredraw = NewNPN_ForceRedrawProc(g_NPN_ForceRedraw); + mozilla_funcs.pushpopupsenabledstate = NewNPN_PushPopupsEnabledStateProc(g_NPN_PushPopupsEnabledState); + mozilla_funcs.poppopupsenabledstate = NewNPN_PopPopupsEnabledStateProc(g_NPN_PopPopupsEnabledState); - npruntime_init_callbacks(&mozilla_funcs); - - if (has_npruntime && getenv("NPW_DISABLE_NPRUNTIME")) { - D(bug(" user disabled npruntime support\n")); - has_npruntime = false; - } - - if (has_npruntime) { + if (NPN_HAS_FEATURE(NPRUNTIME_SCRIPTING)) { D(bug(" browser supports scripting through npruntime\n")); mozilla_funcs.getstringidentifier = NewNPN_GetStringIdentifierProc(g_NPN_GetStringIdentifier); mozilla_funcs.getstringidentifiers = NewNPN_GetStringIdentifiersProc(g_NPN_GetStringIdentifiers); @@ -1789,29 +2156,59 @@ static int handle_NP_Initialize(rpc_connection_t *connection) mozilla_funcs.hasmethod = NewNPN_HasMethodProc(g_NPN_HasMethod); mozilla_funcs.releasevariantvalue = NewNPN_ReleaseVariantValueProc(g_NPN_ReleaseVariantValue); mozilla_funcs.setexception = NewNPN_SetExceptionProc(g_NPN_SetException); + + if (!npobject_bridge_new()) + return NPERR_OUT_OF_MEMORY_ERROR; } - NPError ret = NPERR_NO_ERROR; + D(bug("NP_Initialize\n")); + NPError ret = g_plugin_NP_Initialize(&mozilla_funcs, &plugin_funcs); + D(bug(" return: %d\n", ret)); + return ret; +} - if (!npobject_bridge_new()) - ret = NPERR_OUT_OF_MEMORY_ERROR; +static int handle_NP_Initialize(rpc_connection_t *connection) +{ + D(bug("handle_NP_Initialize\n")); - if (ret == NPERR_NO_ERROR) - ret = g_NP_Initialize(&mozilla_funcs, &plugin_funcs); + uint32_t version; + int error = rpc_method_get_args(connection, + RPC_TYPE_UINT32, &version, + RPC_TYPE_INVALID); + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NP_Initialize() get args", error); + return error; + } + + NPError ret = g_NP_Initialize(version); return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INVALID); } // NP_Shutdown +static NPError +g_NP_Shutdown(void) +{ + if (g_plugin_NP_Shutdown == NULL) + return NPERR_INVALID_FUNCTABLE_ERROR; + + D(bug("NP_Shutdown\n")); + NPError ret = g_plugin_NP_Shutdown(); + D(bug(" done\n")); + + if (NPN_HAS_FEATURE(NPRUNTIME_SCRIPTING)) + npobject_bridge_destroy(); + + gtk_main_quit(); + + return ret; +} + static int handle_NP_Shutdown(rpc_connection_t *connection) { D(bug("handle_NP_Shutdown\n")); NPError ret = g_NP_Shutdown(); - - npobject_bridge_destroy(); - gtk_main_quit(); - return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INVALID); } @@ -1966,7 +2363,7 @@ g_NPP_SetWindow(NPP instance, NPWindow *np_window) NPWindow *window = np_window; if (window) { - if (plugin->top_window) { + if (plugin->toolkit_data) { if (update_window(plugin, window) < 0) return NPERR_GENERIC_ERROR; } @@ -2021,6 +2418,16 @@ g_NPP_GetValue(NPP instance, NPPVariable variable, void *value) if (plugin_funcs.getvalue == NULL) return NPERR_INVALID_FUNCTABLE_ERROR; + switch (variable) { +#if USE_XEMBED + case NPPVpluginNeedsXEmbed: + *((PRBool *)value) = PR_TRUE; + return NPERR_NO_ERROR; +#endif + default: + break; + } + D(bug("NPP_GetValue instance=%p, variable=%d\n", instance, variable)); NPError ret = plugin_funcs.getvalue(instance, variable, value); D(bug(" return: %d [%s]\n", ret, string_of_NPError(ret))); @@ -2158,6 +2565,7 @@ static int handle_NPP_NewStream(rpc_connection_t *connection) RPC_TYPE_UINT32, &stream->end, RPC_TYPE_UINT32, &stream->lastmodified, RPC_TYPE_NP_NOTIFY_DATA, &stream->notifyData, + RPC_TYPE_STRING, &stream->headers, RPC_TYPE_BOOLEAN, &seekable, RPC_TYPE_INVALID); @@ -2174,6 +2582,7 @@ static int handle_NPP_NewStream(rpc_connection_t *connection) stream_ndata->stream_id = stream_id; id_link(stream_id, stream_ndata); stream_ndata->stream = stream; + stream_ndata->is_plugin_stream = 0; uint16 stype = NP_NORMAL; NPError ret = g_NPP_NewStream(instance, type, stream, seekable, &stype); @@ -2212,6 +2621,7 @@ g_NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason) free(stream_ndata); } free((char *)stream->url); + free((char *)stream->headers); free(stream); return ret; @@ -2686,24 +3096,22 @@ static int do_main(int argc, char **argv, const char *connection_path) // freeze on NP_Shutdown when multiple Flash movies are active static int is_flash_player9_beta1(void) { - if (g_NP_GetValue) { - const char *plugin_desc = NULL; - if (g_NP_GetValue(NULL, NPPVpluginDescriptionString, &plugin_desc) == NPERR_NO_ERROR - && plugin_desc && strcmp(plugin_desc, "Shockwave Flash 9.0 d55") == 0) { - npw_printf("WARNING: Flash Player 9 beta 1 detected and rejected\n"); - return 1; - } + const char *plugin_desc = NULL; + if (g_NP_GetValue(NPPVpluginDescriptionString, &plugin_desc) == NPERR_NO_ERROR + && plugin_desc && strcmp(plugin_desc, "Shockwave Flash 9.0 d55") == 0) { + npw_printf("WARNING: Flash Player 9 beta 1 detected and rejected\n"); + return 1; } return 0; } static int do_test(void) { - if (g_NP_GetMIMEDescription == NULL) + if (g_plugin_NP_GetMIMEDescription == NULL) return 1; - if (g_NP_Initialize == NULL) + if (g_plugin_NP_Initialize == NULL) return 2; - if (g_NP_Shutdown == NULL) + if (g_plugin_NP_Shutdown == NULL) return 3; if (is_flash_player9_beta1()) return 4; @@ -2714,25 +3122,15 @@ static int do_info(void) { if (do_test() != 0) return 1; - if (g_NP_GetValue == NULL) - printf("0\n\n0\n\n"); - else { - const char *plugin_name = NULL; - if (g_NP_GetValue(NULL, NPPVpluginNameString, &plugin_name) == NPERR_NO_ERROR && plugin_name) - printf("%zd\n%s\n", strlen(plugin_name) + 1, plugin_name); - else - printf("0\n\n"); - const char *plugin_desc = NULL; - if (g_NP_GetValue(NULL, NPPVpluginDescriptionString, &plugin_desc) == NPERR_NO_ERROR && plugin_desc) - printf("%zd\n%s\n", strlen(plugin_desc) + 1, plugin_desc); - else - printf("0\n\n"); - } + const char *plugin_name = NULL; + if (g_NP_GetValue(NPPVpluginNameString, &plugin_name) == NPERR_NO_ERROR && plugin_name) + printf("PLUGIN_NAME %zd\n%s\n", strlen(plugin_name), plugin_name); + const char *plugin_desc = NULL; + if (g_NP_GetValue(NPPVpluginDescriptionString, &plugin_desc) == NPERR_NO_ERROR && plugin_desc) + printf("PLUGIN_DESC %zd\n%s\n", strlen(plugin_desc), plugin_desc); const char *mime_info = g_NP_GetMIMEDescription(); if (mime_info) - printf("%zd\n%s\n", strlen(mime_info) + 1, mime_info); - else - printf("0\n\n"); + printf("PLUGIN_MIME %zd\n%s\n", strlen(mime_info), mime_info); return 0; } @@ -2813,22 +3211,22 @@ int main(int argc, char **argv) return 1; } dlerror(); - g_NP_GetMIMEDescription = (NP_GetMIMEDescriptionUPP)dlsym(handle, "NP_GetMIMEDescription"); + g_plugin_NP_GetMIMEDescription = (NP_GetMIMEDescriptionUPP)dlsym(handle, "NP_GetMIMEDescription"); if ((error = dlerror()) != NULL) { npw_printf("ERROR: %s\n", error); return 1; } - g_NP_Initialize = (NP_InitializeUPP)dlsym(handle, "NP_Initialize"); + g_plugin_NP_Initialize = (NP_InitializeUPP)dlsym(handle, "NP_Initialize"); if ((error = dlerror()) != NULL) { npw_printf("ERROR: %s\n", error); return 1; } - g_NP_Shutdown = (NP_ShutdownUPP)dlsym(handle, "NP_Shutdown"); + g_plugin_NP_Shutdown = (NP_ShutdownUPP)dlsym(handle, "NP_Shutdown"); if ((error = dlerror()) != NULL) { npw_printf("ERROR: %s\n", error); return 1; } - g_NP_GetValue = (NP_GetValueUPP)dlsym(handle, "NP_GetValue"); + g_plugin_NP_GetValue = (NP_GetValueUPP)dlsym(handle, "NP_GetValue"); } int ret = 1; diff --git a/src/npw-viewer.sh b/src/npw-viewer.sh index a627947..a681ea4 100644 --- a/src/npw-viewer.sh +++ b/src/npw-viewer.sh @@ -71,15 +71,6 @@ if test "$ARCH" != "$TARGET_ARCH"; then fi fi -# Don't wrap host plugins -case " $@ " in -*" --test "*|*" -t "*) - if test "$TARGET_OS" = "$OS" -a "$TARGET_ARCH" = "$ARCH"; then - exit 20 # EXIT_VIEWER_NATIVE - fi - ;; -esac - # Expand PATH for RealPlayer package on NetBSD (realplay) if test "$OS" = "NetBSD"; then REALPLAYER_HOME="/usr/pkg/lib/RealPlayer" diff --git a/src/npw-wrapper.c b/src/npw-wrapper.c index ec79c1d..ebf9701 100644 --- a/src/npw-wrapper.c +++ b/src/npw-wrapper.c @@ -18,16 +18,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define _GNU_SOURCE 1 /* RTLD_DEFAULT */ #include "sysdeps.h" #include #include #include #include +#include #include #include #include -#include #include #include @@ -94,12 +95,12 @@ typedef struct _PluginInstance { typedef struct _StreamInstance { NPStream *stream; uint32_t stream_id; + int is_plugin_stream; } StreamInstance; // Prototypes static void plugin_init(int is_NP_Initialize); static void plugin_exit(void); -static pthread_mutex_t plugin_init_lock = PTHREAD_MUTEX_INITIALIZER; // Helpers #ifndef min @@ -140,12 +141,40 @@ static gboolean rpc_event_dispatch(GSource *source, GSourceFunc callback, gpoint /* === Browser side plug-in API === */ /* ====================================================================== */ +// Does browser have specified feature? +#define NPN_HAS_FEATURE(FEATURE) ((mozilla_funcs.version & 0xff) >= NPVERS_HAS_##FEATURE) + +// NPN_MemAlloc +static inline void *g_NPN_MemAlloc(uint32 size) +{ + return CallNPN_MemAllocProc(mozilla_funcs.memalloc, size); +} + +// NPN_MemFree +static inline void g_NPN_MemFree(void* ptr) +{ + CallNPN_MemFreeProc(mozilla_funcs.memfree, ptr); +} + +// NPN_MemFlush +static inline uint32 g_NPN_MemFlush(uint32 size) +{ + return CallNPN_MemFlushProc(mozilla_funcs.memflush, size); +} + // NPN_UserAgent +static const char *g_NPN_UserAgent(NPP instance) +{ + if (mozilla_funcs.uagent == NULL) + return NULL; + return mozilla_funcs.uagent(instance); +} + static int handle_NPN_UserAgent(rpc_connection_t *connection) { D(bug("handle_NPN_UserAgent\n")); - const char *user_agent = mozilla_funcs.uagent ? mozilla_funcs.uagent(NULL) : NULL; + const char *user_agent = g_NPN_UserAgent(NULL); return rpc_method_send_reply(connection, RPC_TYPE_STRING, user_agent, RPC_TYPE_INVALID); } @@ -463,6 +492,237 @@ static int handle_NPN_RequestRead(rpc_connection_t *connection) return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INVALID); } +// NPN_NewStream +static NPError g_NPN_NewStream(NPP instance, NPMIMEType type, const char *target, NPStream **stream) +{ + if (mozilla_funcs.newstream == NULL) + return NPERR_INVALID_FUNCTABLE_ERROR; + + if (stream == NULL) + return NPERR_INVALID_PARAM; + + D(bug("NPN_NewStream instance=%p, type='%s', target='%s'\n", instance, type, target)); + NPError ret = CallNPN_NewStreamProc(mozilla_funcs.newstream, instance, type, target, stream); + D(bug(" return: %d [%s]\n", ret, string_of_NPError(ret))); + + if (ret == NPERR_NO_ERROR) { + StreamInstance *stream_pdata = malloc(sizeof(*stream_pdata)); + if (stream_pdata == NULL) + return NPERR_OUT_OF_MEMORY_ERROR; + memset(stream_pdata, 0, sizeof(*stream_pdata)); + stream_pdata->stream = *stream; + stream_pdata->stream_id = id_create(stream_pdata); + stream_pdata->is_plugin_stream = 1; + (*stream)->pdata = stream_pdata; + } + else { + static const StreamInstance fake_StreamInstance = { + .stream = NULL, + .stream_id = 0 + }; + static const NPStream fake_NPStream = { + .pdata = (void *)&fake_StreamInstance, + .url = NULL, + .end = 0, + .lastmodified = 0, + .notifyData = NULL + }; + *stream = (void *)&fake_NPStream; + } + + return ret; +} + +static int handle_NPN_NewStream(rpc_connection_t *connection) +{ + D(bug("handle_NPN_NewStream\n")); + + NPP instance; + char *type; + char *target; + int error = rpc_method_get_args(connection, + RPC_TYPE_NPP, &instance, + RPC_TYPE_STRING, &type, + RPC_TYPE_STRING, &target, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_NewStream() get args", error); + return error; + } + + NPStream *stream; + NPError ret = g_NPN_NewStream(instance, type, target, &stream); + + if (type) + free(type); + if (target) + free(target); + + return rpc_method_send_reply(connection, + RPC_TYPE_INT32, ret, + RPC_TYPE_UINT32, ((StreamInstance *)stream->pdata)->stream_id, + RPC_TYPE_STRING, stream->url, + RPC_TYPE_UINT32, stream->end, + RPC_TYPE_UINT32, stream->lastmodified, + RPC_TYPE_NP_NOTIFY_DATA, stream->notifyData, + RPC_TYPE_STRING, NPN_HAS_FEATURE(RESPONSE_HEADERS) ? stream->headers : NULL, + RPC_TYPE_INVALID); +} + +// NPN_DestroySream +static NPError +g_NPN_DestroyStream(NPP instance, NPStream *stream, NPReason reason) +{ + if (mozilla_funcs.destroystream == NULL) + return NPERR_INVALID_FUNCTABLE_ERROR; + + if (stream == NULL) + return NPERR_INVALID_PARAM; + + // Mozilla calls NPP_DestroyStream() for its streams, keep stream + // info in that case + StreamInstance *stream_pdata = stream->pdata; + if (stream_pdata && stream_pdata->is_plugin_stream) { + id_remove(stream_pdata->stream_id); + free(stream->pdata); + stream->pdata = NULL; + } + + D(bug("NPN_DestroyStream instance=%p, stream=%p, reason=%s\n", + instance, stream, string_of_NPReason(reason))); + NPError ret = CallNPN_DestroyStreamProc(mozilla_funcs.destroystream, instance, stream, reason); + D(bug(" return: %d [%s]\n", ret, string_of_NPError(ret))); + + return ret; +} + +static int handle_NPN_DestroyStream(rpc_connection_t *connection) +{ + D(bug("handle_NPN_DestroyStream\n")); + + NPP instance; + NPStream *stream; + int32_t reason; + int error = rpc_method_get_args(connection, + RPC_TYPE_NPP, &instance, + RPC_TYPE_NP_STREAM, &stream, + RPC_TYPE_INT32, &reason, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_DestroyStream() get args", error); + return error; + } + + NPError ret = g_NPN_DestroyStream(instance, stream, reason); + return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INVALID); +} + +// NPN_Write +static int32 g_NPN_Write(NPP instance, NPStream *stream, int32 len, void *buf) +{ + if (mozilla_funcs.write == NULL) + return -1; + + if (stream == NULL) + return -1; + + D(bug("NPP_Write instance=%p\n", instance)); + int32 ret = CallNPN_WriteProc(mozilla_funcs.write, instance, stream, len, buf); + D(bug(" return: %d\n", ret)); + return ret; +} + +static int handle_NPN_Write(rpc_connection_t *connection) +{ + D(bug("handle_NPN_Write\n")); + + NPP instance; + NPStream *stream; + unsigned char *buf; + int32_t len; + int error = rpc_method_get_args(connection, + RPC_TYPE_NPP, &instance, + RPC_TYPE_NP_STREAM, &stream, + RPC_TYPE_ARRAY, RPC_TYPE_CHAR, &len, &buf, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_Write() get args", error); + return error; + } + + int32 ret = g_NPN_Write(instance, stream, len, buf); + + if (buf) + free(buf); + + return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INVALID); +} + +// NPN_PushPopupsEnabledState +static void g_NPN_PushPopupsEnabledState(NPP instance, NPBool enabled) +{ + if (mozilla_funcs.pushpopupsenabledstate == NULL) + return; + + D(bug("NPN_PushPopupsEnabledState instance=%p, enabled=%d\n", instance, enabled)); + CallNPN_PushPopupsEnabledStateProc(mozilla_funcs.pushpopupsenabledstate, instance, enabled); + D(bug(" done\n")); +} + +static int handle_NPN_PushPopupsEnabledState(rpc_connection_t *connection) +{ + D(bug("handle_NPN_PushPopupsEnabledState\n")); + + NPP instance; + uint32_t enabled; + int error = rpc_method_get_args(connection, + RPC_TYPE_NPP, &instance, + RPC_TYPE_UINT32, &enabled, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_PushPopupsEnabledState() get args", error); + return error; + } + + g_NPN_PushPopupsEnabledState(instance, enabled); + + return RPC_ERROR_NO_ERROR; +} + +// NPN_PopPopupsEnabledState +static void g_NPN_PopPopupsEnabledState(NPP instance) +{ + if (mozilla_funcs.poppopupsenabledstate == NULL) + return; + + D(bug("NPN_PopPopupsEnabledState instance=%p\n", instance)); + CallNPN_PopPopupsEnabledStateProc(mozilla_funcs.poppopupsenabledstate, instance); + D(bug(" done\n")); +} + +static int handle_NPN_PopPopupsEnabledState(rpc_connection_t *connection) +{ + D(bug("handle_NPN_PopPopupsEnabledState\n")); + + NPP instance; + int error = rpc_method_get_args(connection, + RPC_TYPE_NPP, &instance, + RPC_TYPE_INVALID); + + if (error != RPC_ERROR_NO_ERROR) { + npw_perror("NPN_PopPopupsEnabledState() get args", error); + return error; + } + + g_NPN_PopPopupsEnabledState(instance); + + return RPC_ERROR_NO_ERROR; +} + // NPN_CreateObject static int handle_NPN_CreateObject(rpc_connection_t *connection) { @@ -982,7 +1242,6 @@ g_NPP_New(NPMIMEType mime_type, NPP instance, static NPError invoke_NPP_Destroy(NPP instance, NPSavedData **save) { - // XXX handle save area (transfer raw bytes but keep size information somewhere) int error = rpc_method_invoke(g_rpc_connection, RPC_METHOD_NPP_DESTROY, RPC_TYPE_NPP, instance, @@ -1007,6 +1266,11 @@ invoke_NPP_Destroy(NPP instance, NPSavedData **save) if (save) *save = save_area; + else if (save_area) { + if (save_area->len > 0 && save_area->buf) + free(save_area->buf); + free(save_area); + } return ret; } @@ -1097,7 +1361,24 @@ invoke_NPP_GetValue(NPP instance, NPPVariable variable, void *value) ret = NPERR_GENERIC_ERROR; } D(bug(" value: %s\n", str)); - *((char **)value) = str; // XXX memory leak + switch (variable) { + case NPPVformValue: + // this is a '\0'-terminated UTF-8 string data allocated by NPN_MemAlloc() + if (ret == NPERR_NO_ERROR && str) { + char *utf8_str = g_NPN_MemAlloc(strlen(str) + 1); + if (utf8_str == NULL) + ret = NPERR_OUT_OF_MEMORY_ERROR; + else + strcpy(utf8_str, str); + free(str); + str = utf8_str; + } + break; + default: + // XXX memory leak (add to a deallocation pool?) + break; + } + *((char **)value) = str; break; } case RPC_TYPE_INT32: @@ -1232,6 +1513,7 @@ invoke_NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream, NPBool see RPC_TYPE_UINT32, stream->end, RPC_TYPE_UINT32, stream->lastmodified, RPC_TYPE_NP_NOTIFY_DATA, stream->notifyData, + RPC_TYPE_STRING, NPN_HAS_FEATURE(RESPONSE_HEADERS) ? stream->headers : NULL, RPC_TYPE_BOOLEAN, seekable, RPC_TYPE_INVALID); @@ -1269,6 +1551,7 @@ g_NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream, NPBool seekable memset(stream_pdata, 0, sizeof(*stream_pdata)); stream_pdata->stream = stream; stream_pdata->stream_id = id_create(stream_pdata); + stream_pdata->is_plugin_stream = 0; stream->pdata = stream_pdata; D(bug("NPP_NewStream instance=%p\n", instance)); @@ -1391,7 +1674,7 @@ static int32 g_NPP_WriteReady(NPP instance, NPStream *stream) { if (instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; + return -1; D(bug("NPP_WriteReady instance=%p\n", instance)); int32 ret = invoke_NPP_WriteReady(instance, stream); @@ -1434,7 +1717,7 @@ static int32 g_NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, void *buf) { if (instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; + return -1; D(bug("NPP_Write instance=%p\n", instance)); int32 ret = invoke_NPP_Write(instance, stream, offset, len, buf); @@ -1590,6 +1873,442 @@ NP_GetMIMEDescription(void) return g_plugin.formats; } + +/* ====================================================================== */ +/* === LONG64 NPAPI support === */ +/* ====================================================================== */ + +/* + * Dependent on NPSavedData + * NPP_New + * NPP_Destroy + * NOTE: the browsers don't seem to care about NPSavedData + * + * Dependent on NPWindow / NPSetWindowCallbackStruct + * NPP_SetWindow + * + * Dependent on NPStream (plug-in side) + * NPP_NewStream + * NPP_DestroyStream + * NPP_WriteReady + * NPP_Write + * NPP_StreamAsFile + * + * Dependent on NPStream (browser-side) + * NPN_RequestRead + * NPN_NewStream + * NPN_DestroyStream + * NPN_Write + * NOTE: Konqueror does not implement those + * + * Dependent on NPPrintCallbackStruct + * NPP_Print + */ + +// Check if another thunking layer is necessary +static int g_use_long64_thunks = -1; + +static void set_use_long64_thunks(bool enabled) +{ + g_use_long64_thunks = enabled; + + if (g_use_long64_thunks) { + // XXX update mozilla_funcs with g_LONG64_*() variants + } +} + +#define NP_CVT32(VAL) ptr32->VAL = ptr64->VAL +#define NP_CVT64(VAL) ptr64->VAL = ptr32->VAL + +// Check display is valid +static bool is_browser_display(Display *display) +{ + Display *browser_display = NULL; + if (mozilla_funcs.getvalue == NULL) + return 0; + if (mozilla_funcs.getvalue(NULL, NPNVxDisplay, (void *)&browser_display) != NPERR_NO_ERROR) + return 0; + return display == browser_display; +} + +// NPStream +typedef struct _LONG64_NPStream { + void* pdata; + void* ndata; + const char* url; + uint64 end; + uint64 lastmodified; + void* notifyData; + const char* headers; +} LONG64_NPStream; + +static void convert_from_LONG64_NPStream(NPStream *ptr32, const LONG64_NPStream *ptr64) +{ + NP_CVT32(pdata); + NP_CVT32(ndata); + NP_CVT32(url); + NP_CVT32(end); + NP_CVT32(lastmodified); + NP_CVT32(notifyData); + NP_CVT32(headers); +} + +#define NP_STREAM32(STREAM) get_stream32(STREAM) + +static inline NPStream *get_stream32(LONG64_NPStream *stream64) +{ + NPStream *stream32 = stream64->pdata; + if (stream32 && stream32->ndata == stream64) + return stream32; + return (NPStream *)stream64; +} + +// NPByteRange +typedef struct _LONG64_NPByteRange { + int64 offset; + uint64 length; + struct _LONG64_NPByteRange* next; +} LONG64_NPByteRange; + +// NPSavedData +typedef struct _LONG64_NPSavedData { + int64 len; + void* buf; +} LONG64_NPSavedData; + +static void convert_from_LONG64_NPSavedData(NPSavedData *ptr32, const LONG64_NPSavedData *ptr64) +{ + NP_CVT32(len); + NP_CVT32(buf); +} + +static void convert_from_NPSavedData(LONG64_NPSavedData *ptr64, const NPSavedData *ptr32) +{ + NP_CVT64(len); + NP_CVT64(buf); +} + +// NPSetWindowCallbackStruct +typedef struct { + int64 type; +#ifdef MOZ_X11 + Display* display; + Visual* visual; + Colormap colormap; + unsigned int depth; +#endif +} LONG64_NPSetWindowCallbackStruct; + +static bool is_LONG64_NPSetWindowCallbackStruct(void *ws_info) +{ + LONG64_NPSetWindowCallbackStruct *ws_info64 = (LONG64_NPSetWindowCallbackStruct *)ws_info; + + return (/* LONG64_NPSetWindowCallbacStruct.type valid? */ + (ws_info64->type == 0 || ws_info64->type == NP_SETWINDOW) && +#ifdef MOZ_X11 + /* LONG64_NPSetWindowCallbacStruct.display valid? */ + is_browser_display(ws_info64->display) && +#endif + 1); +} + +static void convert_from_LONG64_NPSetWindowCallbackStruct(NPSetWindowCallbackStruct *ptr32, + const LONG64_NPSetWindowCallbackStruct *ptr64) +{ + NP_CVT32(type); +#ifdef MOZ_X11 + NP_CVT32(display); + NP_CVT32(visual); + NP_CVT32(colormap); + NP_CVT32(depth); +#endif +} + +// NPPrintCallbackStruct +typedef struct { + int64 type; + FILE* fp; +} LONG64_NPPrintCallbackStruct; + +static bool is_LONG64_NPPrintCallbackStruct(void *platformPrint) +{ + LONG64_NPPrintCallbackStruct *platformPrint64 = (LONG64_NPPrintCallbackStruct *)platformPrint; + + return (/* LONG64_NPPrintCallbackStruct.type valid? */ + platformPrint64->type == NP_PRINT && + /* LONG64_NPPrintCallbackStruct.file valid? */ + platformPrint64->fp != NULL); +} + +static void convert_from_LONG64_NPPrintCallbackStruct(NPPrintCallbackStruct *ptr32, + const LONG64_NPPrintCallbackStruct *ptr64) +{ + NP_CVT32(type); + NP_CVT32(fp); +} + +// NPWindow +typedef struct _LONG64_NPWindow { + void* window; + int64 x; + int64 y; + uint64 width; + uint64 height; + NPRect clipRect; +#if defined(XP_UNIX) && !defined(XP_MACOSX) + void * ws_info; +#endif /* XP_UNIX */ + NPWindowType type; +} LONG64_NPWindow; + +static bool is_LONG64_NPWindow(void *window) +{ + NPWindow *window32 = (NPWindow *)window; + LONG64_NPWindow *window64 = (LONG64_NPWindow *)window; + + return (/* MSW32(LONG64_NPWindow.x) */ + (window32->x == 0 || window32->x == 0xffffffff) && + /* MSW32(LONG64_NPWindow.y) */ + (window32->width == 0 || window32->width == 0xffffffff) && + /* LONG64_NPWindow.clipRect.top, LONG64_NPWindow.clipRect.left */ + (window32->type != NPWindowTypeWindow && window32->type != NPWindowTypeDrawable) && + /* LONG64_NPWindow.type valid? */ + (window64->type == NPWindowTypeWindow || window64->type == NPWindowTypeDrawable) && + /* LONG64_NPWindow.ws_info valid? */ + is_LONG64_NPSetWindowCallbackStruct(window64->ws_info)); +} + +static void convert_from_LONG64_NPWindow(NPWindow *ptr32, const LONG64_NPWindow *ptr64) +{ + NP_CVT32(type); + NP_CVT32(window); + NP_CVT32(x); + NP_CVT32(y); + NP_CVT32(width); + NP_CVT32(height); + NP_CVT32(clipRect.top); + NP_CVT32(clipRect.left); + NP_CVT32(clipRect.bottom); + NP_CVT32(clipRect.right); + convert_from_LONG64_NPSetWindowCallbackStruct(ptr32->ws_info, ptr64->ws_info); +} + +// NPP_SetWindow (LONG64) +static NPError +g_LONG64_NPP_SetWindow(NPP instance, void *window) +{ + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + // Detect broken 64-bit NPAPI + if (g_use_long64_thunks < 0) + set_use_long64_thunks(is_LONG64_NPWindow(window)); + + NPWindow window32; + NPSetWindowCallbackStruct ws_info32; + if (g_use_long64_thunks) { + window32.ws_info = &ws_info32; + convert_from_LONG64_NPWindow(&window32, window); + window = &window32; + } + + return g_NPP_SetWindow(instance, window); +} + +// NPP_New (LONG64) +static NPError +g_LONG64_NPP_New(NPMIMEType mime_type, NPP instance, + uint16_t mode, int16_t argc, char *argn[], char *argv[], + void *saved) +{ + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPSavedData saved32; + if (saved && g_use_long64_thunks > 0) { + convert_from_LONG64_NPSavedData(&saved32, saved); + saved = &saved32; + } + + return g_NPP_New(mime_type, instance, mode, argc, argn, argv, saved); +} + +// NPP_Destroy (LONG64) +static NPError +g_LONG64_NPP_Destroy(NPP instance, void **save) +{ + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPSavedData *save_area32 = NULL; + NPError ret = g_NPP_Destroy(instance, &save_area32); + + if (save && g_use_long64_thunks > 0) { + LONG64_NPSavedData *save_area64 = NULL; + if (ret == NPERR_NO_ERROR && save_area32) { + if ((save_area64 = g_NPN_MemAlloc(save_area32->len)) != NULL) + convert_from_NPSavedData(save_area64, save_area32); + free(save_area32); + } + *save = save_area64; + } + + return ret; +} + +// NPP_NewStream (LONG64) +static NPError +g_LONG64_NPP_NewStream(NPP instance, NPMIMEType type, void *stream, NPBool seekable, uint16 *stype) +{ + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + // Detect broken 64-bit NPAPI + if (g_use_long64_thunks < 0) { + npw_printf("WARNING: function using an NPStream was called too early, could not determine LONG64 data structure\n"); + set_use_long64_thunks(false); + } + + if (g_use_long64_thunks) { + NPStream *stream32; + if ((stream32 = malloc(sizeof(*stream32))) == NULL) + return NPERR_OUT_OF_MEMORY_ERROR; + convert_from_LONG64_NPStream(stream32, stream); + stream32->ndata = stream; + ((NPStream *)stream)->pdata = stream32; + } + + return g_NPP_NewStream(instance, type, NP_STREAM32(stream), seekable, stype); +} + +// NPP_DestroyStream (LONG64) +static NPError +g_LONG64_NPP_DestroyStream(NPP instance, void *stream, NPReason reason) +{ + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + if (stream == NULL) + return NPERR_INVALID_PARAM; + + NPError ret = g_NPP_DestroyStream(instance, NP_STREAM32(stream), reason); + + if (g_use_long64_thunks) { + free(((NPStream *)stream)->pdata); + ((NPStream *)stream)->pdata = NULL; + } + + return ret; +} + +// NPP_WriteReady (LONG64) +static int64 +g_LONG64_NPP_WriteReady(NPP instance, void *stream) +{ + if (instance == NULL) + return -1L; + + if (stream == NULL) + return -1L; + + return (int64)(int32)g_NPP_WriteReady(instance, NP_STREAM32(stream)); +} + +// NPP_Write (LONG64) +static int64 +g_LONG64_NPP_Write(NPP instance, void *stream, int64 offset, int64 len, void *buf) +{ + if (instance == NULL) + return -1L; + + if (stream == NULL) + return -1L; + + return (int64)(int32)g_NPP_Write(instance, NP_STREAM32(stream), offset, len, buf); +} + +// NPP_StreamAsFile (LONG64) +static void +g_LONG64_NPP_StreamAsFile(NPP instance, void *stream, const char *fname) +{ + if (instance == NULL) + return; + + if (stream == NULL) + return; + + g_NPP_StreamAsFile(instance, NP_STREAM32(stream), fname); +} + +// NPP_Print (LONG64) +static void g_LONG64_NPP_Print(NPP instance, void *PrintInfo) +{ + if (instance == NULL) + return; + + if (PrintInfo == NULL) + return; + + // Detect broken 64-bit NPAPI + if (g_use_long64_thunks < 0) + set_use_long64_thunks(is_LONG64_NPPrintCallbackStruct(PrintInfo)); + + NPPrint PrintInfo32; + NPPrintCallbackStruct platformPrint32; + if (g_use_long64_thunks) { + memcpy(&PrintInfo32, PrintInfo, sizeof(PrintInfo32)); + void *platformPrint; + switch (((NPPrint *)PrintInfo)->mode) { + case NP_FULL: + platformPrint = ((NPPrint *)PrintInfo)->print.fullPrint.platformPrint; + convert_from_LONG64_NPPrintCallbackStruct(&platformPrint32, platformPrint); + PrintInfo32.print.fullPrint.platformPrint = &platformPrint32; + break; + case NP_EMBED: + platformPrint = ((NPPrint *)PrintInfo)->print.embedPrint.platformPrint; + convert_from_LONG64_NPPrintCallbackStruct(&platformPrint32, platformPrint); + PrintInfo32.print.embedPrint.platformPrint = &platformPrint32; + break; + } + PrintInfo = &PrintInfo32; + } + + g_NPP_Print(instance, PrintInfo); +} + + +/* ====================================================================== */ +/* === Plug-in initialization === */ +/* ====================================================================== */ + +// Detect Konqueror +static bool is_konqueror(void) +{ + if (dlsym(RTLD_DEFAULT, "qApp") == NULL) + return false; + if (mozilla_funcs.getvalue == NULL) + return false; + Display *x_display = NULL; + if (mozilla_funcs.getvalue(NULL, NPNVxDisplay, (void *)&x_display) != NPERR_NO_ERROR) + return false; + XtAppContext x_app_context = NULL; + if (mozilla_funcs.getvalue(NULL, NPNVxtAppContext, (void *)&x_app_context) != NPERR_NO_ERROR) + return false; + if (x_display == NULL || x_app_context == NULL) + return false; + String name, class; + XtGetApplicationNameAndClass(x_display, &name, &class); + if (strcmp(name, "nspluginviewer") == 0) + return true; + // XXX user-agent string can be changed, but it's still an heuristic + const char *user_agent = g_NPN_UserAgent(NULL); + if (user_agent == NULL) + return false; + if (strstr(user_agent, "Konqueror") != NULL) + return true; + return false; +} + // Provides global initialization for a plug-in NPError NP_Initialize(NPNetscapeFuncs *moz_funcs, NPPluginFuncs *plugin_funcs) @@ -1630,6 +2349,21 @@ NP_Initialize(NPNetscapeFuncs *moz_funcs, NPPluginFuncs *plugin_funcs) plugin_funcs->getvalue = NewNPP_GetValueProc(g_NPP_GetValue); plugin_funcs->setvalue = NewNPP_SetValueProc(g_NPP_SetValue); + // override function table with an additional thunking layer for + // possibly broken 64-bit Konqueror versions (NPAPI 0.11) + if (is_konqueror() && sizeof(void *) == 8 && ! NPN_HAS_FEATURE(NPRUNTIME_SCRIPTING)) { + D(bug("Installing Konqueror workarounds\n")); + plugin_funcs->setwindow = NewNPP_SetWindowProc(g_LONG64_NPP_SetWindow); + plugin_funcs->newstream = NewNPP_NewStreamProc(g_LONG64_NPP_NewStream); + plugin_funcs->destroystream = NewNPP_DestroyStreamProc(g_LONG64_NPP_DestroyStream); + plugin_funcs->asfile = NewNPP_StreamAsFileProc(g_LONG64_NPP_StreamAsFile); + plugin_funcs->writeready = NewNPP_WriteReadyProc(g_LONG64_NPP_WriteReady); + plugin_funcs->write = NewNPP_WriteProc(g_LONG64_NPP_Write); + plugin_funcs->print = NewNPP_PrintProc(g_LONG64_NPP_Print); + plugin_funcs->newp = NewNPP_NewProc(g_LONG64_NPP_New); + plugin_funcs->destroy = NewNPP_DestroyProc(g_LONG64_NPP_Destroy); + } + if (g_plugin.initialized == 0 || g_plugin.initialized == 1) plugin_init(1); if (g_plugin.initialized <= 0) @@ -1638,17 +2372,13 @@ NP_Initialize(NPNetscapeFuncs *moz_funcs, NPPluginFuncs *plugin_funcs) if (!npobject_bridge_new()) return NPERR_MODULE_LOAD_FAILED_ERROR; - // NPRuntime appeared in NPAPI >= 0.14 - bool has_npruntime = true; - if ((moz_funcs->version >> 8) == 0 && (moz_funcs->version & 0xff) < 14) - has_npruntime = false; - // check that the browser doesn't lie - if (moz_funcs->size < (offsetof(NPNetscapeFuncs, setexception) + sizeof(NPN_SetExceptionUPP))) - has_npruntime = false; + // pass down common NPAPI version supported by both the underlying + // browser and the thunking capabilities of nspluginwrapper + uint32_t version = min(moz_funcs->version, plugin_funcs->version); int error = rpc_method_invoke(g_rpc_connection, RPC_METHOD_NP_INITIALIZE, - RPC_TYPE_UINT32, (uint32_t)has_npruntime, + RPC_TYPE_UINT32, (uint32_t)version, RPC_TYPE_INVALID); if (error != RPC_ERROR_NO_ERROR) { @@ -1707,13 +2437,8 @@ NP_Shutdown(void) return ret; } - -/* ====================================================================== */ -/* === Plug initialization === */ -/* ====================================================================== */ - // Initialize wrapper plugin and execute viewer -static void do_plugin_init(int is_NP_Initialize) +static void plugin_init(int is_NP_Initialize) { if (g_plugin.initialized < 0) return; @@ -1754,28 +2479,30 @@ static void do_plugin_init(int is_NP_Initialize) FILE *viewer_fp = popen(command, "r"); if (viewer_fp == NULL) return; - char **strings[] = { &g_plugin.name, &g_plugin.description, &g_plugin.formats, NULL }; - int i, error = 0; - for (i = 0; strings[i] != NULL; i++) { - int len; - if (fscanf(viewer_fp, "%d\n", &len) != 1) { - error = 1; - break; - } - char *str = malloc(len + 1); + char line[256]; + while (fgets(line, sizeof(line), viewer_fp)) { + // Read line + int len = strlen(line); if (len == 0) - str[0] = '\0'; - else { - if (fgets(str, len, viewer_fp) == NULL) { - error = 1; - break; + continue; + line[len - 1] = '\0'; + + // Parse line + char tag[sizeof(line)]; + if (sscanf(line, "%s %d", tag, &len) == 2) { + char *str = malloc(++len); + if (str && fgets(str, len, viewer_fp)) { + if (strcmp(tag, "PLUGIN_NAME") == 0) + g_plugin.name = str; + else if (strcmp(tag, "PLUGIN_DESC") == 0) + g_plugin.description = str; + else if (strcmp(tag, "PLUGIN_MIME") == 0) + g_plugin.formats = str; } } - *(strings[i]) = str; } pclose(viewer_fp); - if (error == 0) - g_plugin.initialized = 1; + g_plugin.initialized = 1; } if (!is_NP_Initialize) @@ -1817,6 +2544,11 @@ static void do_plugin_init(int is_NP_Initialize) { RPC_METHOD_NPN_STATUS, handle_NPN_Status }, { RPC_METHOD_NPN_PRINT_DATA, handle_NPN_PrintData }, { RPC_METHOD_NPN_REQUEST_READ, handle_NPN_RequestRead }, + { RPC_METHOD_NPN_NEW_STREAM, handle_NPN_NewStream }, + { RPC_METHOD_NPN_DESTROY_STREAM, handle_NPN_DestroyStream }, + { RPC_METHOD_NPN_WRITE, handle_NPN_Write }, + { RPC_METHOD_NPN_PUSH_POPUPS_ENABLED_STATE, handle_NPN_PushPopupsEnabledState }, + { RPC_METHOD_NPN_POP_POPUPS_ENABLED_STATE, handle_NPN_PopPopupsEnabledState }, { RPC_METHOD_NPN_CREATE_OBJECT, handle_NPN_CreateObject }, { RPC_METHOD_NPN_RETAIN_OBJECT, handle_NPN_RetainObject }, { RPC_METHOD_NPN_RELEASE_OBJECT, handle_NPN_ReleaseObject }, @@ -1909,15 +2641,8 @@ static void do_plugin_init(int is_NP_Initialize) D(bug("--- INIT ---\n")); } -static void plugin_init(int is_NP_Initialize) -{ - pthread_mutex_lock(&plugin_init_lock); - do_plugin_init(is_NP_Initialize); - pthread_mutex_unlock(&plugin_init_lock); -} - // Kill NSPlugin Viewer process -static void do_plugin_exit(void) +static void plugin_exit(void) { D(bug("plugin_exit\n")); @@ -1964,13 +2689,6 @@ static void do_plugin_exit(void) g_plugin.initialized = 0; } -static void plugin_exit(void) -{ - pthread_mutex_lock(&plugin_init_lock); - do_plugin_exit(); - pthread_mutex_unlock(&plugin_init_lock); -} - static void __attribute__((destructor)) plugin_exit_sentinel(void) { plugin_exit(); diff --git a/src/xembed.h b/src/xembed.h new file mode 100644 index 0000000..11b3cce --- /dev/null +++ b/src/xembed.h @@ -0,0 +1,50 @@ +/* + * xembed.h - XEMBED definitions + * + * nspluginwrapper (C) 2005-2007 Gwenole Beauchesne + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEMBED_H +#define XEMBED_H + +// XEMBED messages +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_WINDOW_DEACTIVATE 2 +#define XEMBED_REQUEST_FOCUS 3 +#define XEMBED_FOCUS_IN 4 +#define XEMBED_FOCUS_OUT 5 +#define XEMBED_FOCUS_NEXT 6 +#define XEMBED_FOCUS_PREV 7 +#define XEMBED_GRAB_KEY 8 +#define XEMBED_UNGRAB_KEY 9 +#define XEMBED_MODALITY_ON 10 +#define XEMBED_MODALITY_OFF 11 + +// Non-standard messages +#define XEMBED_GTK_GRAB_KEY 108 +#define XEMBED_GTK_UNGRAB_KEY 109 + +// Details for XEMBED_FOCUS_IN +#define XEMBED_FOCUS_CURRENT 0 +#define XEMBED_FOCUS_FIRST 1 +#define XEMBED_FOCUS_LAST 2 + +// Flags for _XEMBED_INFO +#define XEMBED_MAPPED (1 << 0) + +#endif /* XEMBED_H */ diff --git a/tests/test-plugins.html b/tests/test-plugins.html index 335601b..b14ea2e 100644 --- a/tests/test-plugins.html +++ b/tests/test-plugins.html @@ -30,5 +30,12 @@ Various sample data to exercise the NPAPI plugins compatibility layer
  • Citrix ICA Client demo
    http://www.rentonlinesoftware.com/store/tryademo.asp + +Individual testcases: +

    diff --git a/utils/xtrace.c b/utils/xtrace.c index e592fa5..6569a3a 100644 --- a/utils/xtrace.c +++ b/utils/xtrace.c @@ -1,11 +1,13 @@ #define _GNU_SOURCE 1 /* RTLD_NEXT */ +#include #include #include #include #include #include #include +#include #include #include @@ -15,11 +17,12 @@ #include -static int g_debug_level = -1; +static int g_debug_level = 1; static FILE *g_log_file = NULL; void npw_printf(const char *format, ...) { +#if 0 if (g_debug_level < 0) { g_debug_level = 0; const char *debug_str = getenv("NPW_DEBUG"); @@ -38,6 +41,9 @@ void npw_printf(const char *format, ...) if (log_file == NULL) g_log_file = stderr; } +#else + g_log_file = stderr; +#endif if (g_debug_level > 0) { va_list args; @@ -47,7 +53,102 @@ void npw_printf(const char *format, ...) } } +#define PREPARE_FUNC(NAME, RET, ARGS) \ + static RET (*lib_##NAME) ARGS; \ + if (lib_##NAME == NULL) \ + lib_##NAME = dlsym(RTLD_NEXT, #NAME); \ + assert(lib_##NAME != NULL) +#if 0 +char *getenv(const char *name) +{ + PREPARE_FUNC(getenv, char *, (const char *)); + char *str = lib_getenv(name); + npw_printf("getenv '%s' => '%s'\n", name ? name : "", str ? str : ""); + return str; +} + +int putenv(char *string) +{ + PREPARE_FUNC(putenv, int, (char *)); + npw_printf("putenv '%s'\n", string ? string : ""); + return lib_putenv(string); +} +#endif + +int XSetClassHints(Display *display, Window w, XClassHint *class_hints) +{ + PREPARE_FUNC(XSetClassHints, int, (Display *, Window, XClassHint *)); + npw_printf("XSetClassHints(window %p, class_hints { %s, '%s' }\n", w, class_hints->res_name, class_hints->res_class); + return lib_XSetClassHints(display, w, class_hints); +} + +static void print_backtrace(void) +{ +#if 0 +#define N_LEVELS 64 + void *trace[N_LEVELS]; + int n_levels = backtrace(trace, sizeof(trace)/sizeof(trace[0])); + backtrace_symbols_fd(trace, n_levels, STDERR_FILENO); +#endif +} + +Status XSendEvent(Display *display, Window w, Bool propagate, long event_mask, XEvent *event_send) +{ + PREPARE_FUNC(XSendEvent, Status, (Display *, Window, Bool, long, XEvent *)); + if (event_send && (1 || ((XAnyEvent *)event_send)->send_event)) { + npw_printf("XSendEvent(window %p, propagate %d, event_mask %08x, event type %d, SendEvent %x)\n", + w, propagate, event_mask, event_send->type, event_send->xany.send_event); + } + print_backtrace(); + if (event_send->xclient.message_type == XInternAtom(display, "_XEMBED", False)) + npw_printf("[X11] Handle XEMBED message %d for window %p\n", event_send->xclient.data.l[1], event_send->xany.window); + return lib_XSendEvent(display, w, propagate, event_mask, event_send); +} + +#if 0 +Window XCreateWindow(Display *display, Window parent, int x, int y, + unsigned int width, unsigned int height, + unsigned int border_width, int depth, unsigned int class, Visual *visual, + unsigned long valuemask, XSetWindowAttributes *attributes) +{ + PREPARE_FUNC(XCreateWindow, Window, (Display *, Window, int, int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, Visual *, unsigned long, XSetWindowAttributes *)); + Window ret = lib_XCreateWindow(display, parent, x, y, width, height, + border_width, depth, class, visual, + valuemask, attributes); + npw_printf("[X11] XCreateWindow() -> %p\n", ret); + return ret; +} +#endif + +#if 0 +int XChangeProperty(Display *display, Window w, Atom property, Atom type, + int format, int mode, const unsigned char *data, int nelements) +{ + PREPARE_FUNC(XChangeProperty, int, (Display *, Window, Atom, Atom, int, int, const unsigned char *, int)); + const char *property_str = XGetAtomName(display, property); + const char *type_str = XGetAtomName(display, type); + npw_printf("XChangeProperty(window %p, property %s, type %s, format %d, mode %d)\n", + w, property_str, type_str, format, mode); + int ret = lib_XChangeProperty(display, w, property, type, format, mode, data, nelements); + if (property == XInternAtom(display, "WM_HINTS", False) && format == 32) { + XWMHints *hints = XGetWMHints(display, w); + if (hints) { + npw_printf(" InputHint %d\n", hints->input); + XFree((void *)hints); + } + } + return ret; +} +#endif + +void XtConfigureWidget(Widget w, Position x, Position y, Dimension width, Dimension height, Dimension border_width) +{ + PREPARE_FUNC(XtConfigureWidget, void, (Widget, Position, Position, Dimension, Dimension, Dimension)); + lib_XtConfigureWidget(w, x, y, width, height, border_width); +} + +#if 0 Atom XInternAtom(Display *display, const char *atom_name, Bool only_if_exists) { static Atom (*x_XInternAtom)(Display *, const char *, Bool) = NULL; @@ -58,21 +159,38 @@ Atom XInternAtom(Display *display, const char *atom_name, Bool only_if_exists) Atom ret = x_XInternAtom(display, atom_name, only_if_exists); return ret; } +#endif -static XtEventHandler app_event_handler = NULL; +typedef struct { + void *client_data; + XtEventHandler event_handler; +} fake_event_handler_data; static void fake_event_handler(Widget w, XtPointer client_data, XEvent *event, Boolean *cont) { + fake_event_handler_data *pdata = (fake_event_handler_data *)client_data; + switch (event->type) { + case ButtonPress: + npw_printf("ButtonPress\n"); + break; + case ButtonRelease: + npw_printf("ButtonRelease\n"); + break; case KeyPress: npw_printf("KeyPress\n"); break; - case ButtonPress: - npw_printf("ButtonPress\n"); - // asm volatile ("int3"); + case KeyRelease: + npw_printf("KeyRelease\n"); + break; + case FocusIn: + npw_printf("[X11] FocusIn for window %p\n", event->xfocus.window); + break; + case FocusOut: + npw_printf("[X11] FocusOut for window %p\n", event->xfocus.window); break; } - app_event_handler(w, client_data, event, cont); + pdata->event_handler(w, pdata->client_data, event, cont); } static const char *dlname(void *addr) @@ -83,6 +201,37 @@ static const char *dlname(void *addr) return dlinfo.dli_sname; } +static void print_event_mask(EventMask event_mask) +{ +#define P(EVENT) if (event_mask & (EVENT##Mask)) npw_printf(" " #EVENT "\n") + P(KeyPress); + P(KeyRelease); + P(ButtonPress); + P(ButtonRelease); + P(EnterWindow); + P(LeaveWindow); + P(PointerMotion); + P(PointerMotionHint); + P(Button1Motion); + P(Button2Motion); + P(Button3Motion); + P(Button4Motion); + P(Button5Motion); + P(ButtonMotion); + P(KeymapState); + P(Exposure); + P(VisibilityChange); + P(StructureNotify); + P(ResizeRedirect); + P(SubstructureNotify); + P(SubstructureRedirect); + P(FocusChange); + P(PropertyChange); + P(ColormapChange); + P(OwnerGrabButton); +} + +#if 1 void XtAddEventHandler(Widget w, EventMask event_mask, Bool nonmaskable, XtEventHandler proc, XtPointer client_data) { @@ -90,15 +239,67 @@ void XtAddEventHandler(Widget w, EventMask event_mask, Bool nonmaskable, if (x_XtAddEventHandler == NULL) x_XtAddEventHandler = dlsym(RTLD_NEXT, "XtAddEventHandler"); - npw_printf("[X11] XtAddEventHandler(Widget=%p[0x%08x], event_mask=%x, nonmaskable=%d, proc=%p[%s])\n", - w, XtWindow(w), event_mask, nonmaskable, proc, dlname(proc)); - if (event_mask & ButtonPressMask) { - app_event_handler = proc; - proc = fake_event_handler; + if (0 || (event_mask & (KeyPressMask|KeyReleaseMask))) { + npw_printf("[X11] XtAddEventHandler(Widget=%p[0x%08x], event_mask=%x, nonmaskable=%d, proc=%p[%s])\n", + w, XtWindow(w), event_mask, nonmaskable, proc, dlname(proc)); + print_event_mask(event_mask); } +#if 1 + if (event_mask & (KeyPressMask|KeyReleaseMask)) { + fake_event_handler_data *pdata = malloc(sizeof(*pdata)); + pdata->client_data = client_data; + pdata->event_handler = proc; + proc = fake_event_handler; + client_data = pdata; + } +#endif x_XtAddEventHandler(w, event_mask, nonmaskable, proc, client_data); +#if 0 + if (event_mask & (KeyPressMask|KeyReleaseMask)) + x_XtAddEventHandler(w, event_mask, nonmaskable, fake_event_handler, client_data); +#endif +} +#endif + +#if 0 +void XtAddCallback(Widget w, _Xconst _XtString callback_name, XtCallbackProc callback, XtPointer client_data) +{ + static void (*x_XtAddCallback)(Widget, _Xconst _XtString, XtCallbackProc, XtPointer); + if (x_XtAddCallback == NULL) + x_XtAddCallback = dlsym(RTLD_NEXT, "XtAddCallback"); + + npw_printf("[X11] XtAddCallback(widget %p[0x%08x], callback_name '%s', callback %p[%s], client_data %p)\n", + w, XtWindow(w), callback_name, callback, dlname(callback), client_data); + x_XtAddCallback(w, callback_name, callback, client_data); +} +#endif + +#if 0 +int XtGrabKeyboard(Widget w, _XtBoolean owner_events, int pointer_mode, int keyboard_mode, Time time) +{ + static int (*x_XtGrabKeyboard)(Widget, _XtBoolean, int, int, Time); + if (x_XtGrabKeyboard == NULL) + x_XtGrabKeyboard = dlsym(RTLD_NEXT, "XtGrabKeyboard"); + + npw_printf("[X11] XtGrabKeyboard(widget %p[0x%08x], owner_events %08x, pointer_mode %08x, keyboard_mode %08x, time %d)\n", + w, XtWindow(w), owner_events, pointer_mode, keyboard_mode, time); + int rc = x_XtGrabKeyboard(w, owner_events, pointer_mode, keyboard_mode, time); + npw_printf(" returns %d\n", rc); + return rc; } +void XtUngrabKeyboard(Widget w, Time time) +{ + static void (*x_XtUngrabKeyboard)(Widget, Time); + if (x_XtUngrabKeyboard == NULL) + x_XtUngrabKeyboard = dlsym(RTLD_NEXT, "XtUngrabKeyboard"); + + npw_printf("[X11] XtUngrabKeyboard(widget %p[%08x], time %d)\n", w, XtWindow(w), time); + x_XtUngrabKeyboard(w, time); +} +#endif + +#if 0 XtIntervalId XtAppAddTimeOut(XtAppContext app_context, unsigned long interval, XtTimerCallbackProc proc, XtPointer client_data) { @@ -143,7 +344,9 @@ Status XGetWindowAttributes(Display *display, Window w, XWindowAttributes *attr) attr->x, attr->y, attr->width, attr->height, attr->border_width); return ret; } +#endif +#if 1 Widget XtCreatePopupShell(const char *name, WidgetClass widget_class, Widget parent, ArgList args, Cardinal num_args) { @@ -159,6 +362,7 @@ Widget XtCreatePopupShell(const char *name, WidgetClass widget_class, Widget npw_printf(" return: %p [0x%08x]\n", ret, XtWindow(ret)); return ret; } +#endif #if 0 Status XQueryTree(Display *display, Window w, Window *root_return, Window *parent_return, @@ -180,7 +384,6 @@ Status XQueryTree(Display *display, Window w, Window *root_return, Window *paren } return ret; } -#endif Bool XQueryPointer(Display *display, Window w, Window *root_return, Window *child_return, int *root_x_return, int *root_y_return, @@ -201,6 +404,7 @@ Bool XQueryPointer(Display *display, Window w, Window *root_return, } return ret; } +#endif #if 0 Widget XtWindowToWidget(Display *display, Window window) @@ -214,7 +418,6 @@ Widget XtWindowToWidget(Display *display, Window window) npw_printf(" return: %p\n", ret); return ret; } -#endif Widget XtParent(Widget w) { @@ -227,7 +430,9 @@ Widget XtParent(Widget w) npw_printf(" return: 0x%08x\n", ret); return ret; } +#endif +#if 0 static XErrorHandler app_error_handler = NULL; static int fake_error_handler(Display *display, XErrorEvent *error_event) @@ -247,7 +452,9 @@ XErrorHandler XSetErrorHandler(XErrorHandler handler) XErrorHandler ret = fn(fake_error_handler); return ret; } +#endif +#if 0 int XPutImage(Display *display, Drawable d, GC gc, XImage *image, int src_x, int src_y, int dest_x, int dest_y, unsigned int width, unsigned int height) @@ -282,3 +489,4 @@ Status XShmPutImage(Display *display, Drawable d, GC gc, XImage *image, npw_printf(" return: %d\n", ret); return ret; } +#endif