nspluginwrapper/src/npw-wrapper.c

4163 lines
116 KiB
C

/*
* npw-wrapper.c - Host Mozilla plugin (loads the actual viewer)
*
* nspluginwrapper (C) 2005-2009 Gwenole Beauchesne
* (C) 2011 David Benjamin
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _GNU_SOURCE 1 /* RTLD_DEFAULT */
#include "sysdeps.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <dlfcn.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <glib.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include "utils.h"
#include "npw-common.h"
#include "npw-malloc.h"
#define DEBUG 1
#include "debug.h"
// Define to enable direct execution of native plugins
// (i.e. not running through npviewer.bin)
#define ALLOW_DIRECT_EXECUTION 1
// Globally exported plugin ident, used by the "npconfig" tool
const NPW_PluginInfo NPW_Plugin = {
NPW_PLUGIN_IDENT,
NPW_DEFAULT_PLUGIN_PATH,
0,
HOST_OS,
HOST_ARCH,
NPW_PLUGIN_INFO_VERSION,
""
};
// Path to plugin to use
static const char *plugin_path = NPW_Plugin.path;
// Path to associated plugin viewer
static const char *plugin_viewer_path = NPW_Plugin.viewer_path;
// Netscape exported functions
static NPNetscapeFuncs mozilla_funcs;
// NPAPI version nspluginwrapper supports
static int npapi_version = 0;
// Wrapper plugin data
typedef struct {
int initialized;
int viewer_pid;
int is_wrapper;
char *name;
char *description;
char *formats;
} Plugin;
static Plugin g_plugin = { 0, -1, 0, NULL, NULL, NULL };
// Instance state information about the plugin
typedef struct _PluginInstance {
NPW_DECL_PLUGIN_INSTANCE;
rpc_connection_t *connection;
NPP native_instance;
} PluginInstance;
#define PLUGIN_INSTANCE(instance) \
((PluginInstance *)NPW_PLUGIN_INSTANCE(instance))
#define PLUGIN_INSTANCE_NPP(plugin) \
NPW_PLUGIN_INSTANCE_NPP((NPW_PluginInstance *)(plugin))
// Plugin side data for an NPStream instance
typedef struct _StreamInstance {
NPW_DECL_STREAM_INSTANCE;
} StreamInstance;
// Prototypes
static void plugin_init(int is_NP_Initialize);
static void plugin_exit(void);
static void plugin_kill_cb(rpc_connection_t *connection, void *user_data);
static NPError plugin_start(void);
static NPError plugin_start_if_needed(void);
static int plugin_killed = 0;
/*
* Notes concerning NSPluginWrapper recovery model.
*
* NSPluginWrapper will restart the Viewer if it detected to be
* dead. It will not attempt to "replay" the plugin. This means that
* if a plugin crashed, its window will remain grayed: only new
* instances will start a new viewer.
*
* Each PlugInstance holds a reference to the RPC connection it was
* created with. g_rpc_connection can be seen as the "master"
* connection (used to initialize and shutdown things). The RPC
* connections are reference counted so that when the master
* connection is set to a new one, previous connections are still
* live. That way, old NPP instances are not passed down with the new
* connection and thus can fail early/gracefully in subsequent calls
* to NPP_*() functions.
*
* All active NPRuntime objects are marked as inactive and
* are no longer processed.
*/
// Minimal time between two plugin restarts in sec
#define MIN_RESTART_INTERVAL 1
// Consume as many bytes as possible when we are not NPP_WriteReady()
// XXX: move to a common place to Wrapper and Viewer
#define NPERR_STREAM_BUFSIZ 65536
/* ====================================================================== */
/* === Helpers === */
/* ====================================================================== */
// Flush the X output buffer
static void toolkit_flush(NPP instance)
{
// Always prefer gdk_flush() if the master binary is linked against Gtk
static void (*INVALID)(void) = (void (*)(void))(intptr_t)-1;
static void (*lib_gdk_flush)(void) = NULL;
if (lib_gdk_flush == NULL) {
if ((lib_gdk_flush = dlsym(RTLD_DEFAULT, "gdk_flush")) == NULL)
lib_gdk_flush = INVALID;
}
if (lib_gdk_flush != INVALID) {
lib_gdk_flush();
return;
}
// Try raw X11
Display *x_display = NULL;
int error = mozilla_funcs.getvalue(instance, NPNVxDisplay, (void *)&x_display);
if (error == NPERR_NO_ERROR && x_display) {
XSync(x_display, False);
return;
}
}
static void pointer_ungrab(NPP instance, Time time)
{
// Always prefer gdk_pointer_ungrab() if the master binary is linked against Gtk
static void (*INVALID)(uint32_t) = (void (*)(uint32_t))(intptr_t)-1;
static void (*lib_gdk_pointer_ungrab)(uint32_t) = NULL;
if (lib_gdk_pointer_ungrab == NULL) {
if ((lib_gdk_pointer_ungrab = dlsym(RTLD_DEFAULT, "gdk_pointer_ungrab")) == NULL)
lib_gdk_pointer_ungrab = INVALID;
}
if (lib_gdk_pointer_ungrab != INVALID) {
lib_gdk_pointer_ungrab(time);
return;
}
// Try raw X11
Display *x_display = NULL;
int error = mozilla_funcs.getvalue(instance, NPNVxDisplay, (void *)&x_display);
if (error == NPERR_NO_ERROR && x_display) {
XUngrabPointer(x_display, time);
return;
}
}
// PluginInstance vfuncs
static void *plugin_instance_allocate(void);
static void plugin_instance_deallocate(PluginInstance *plugin);
static void plugin_instance_finalize(PluginInstance *plugin);
static void plugin_instance_invalidate(PluginInstance *plugin);
static NPW_PluginInstanceClass PluginInstanceClass = {
(NPW_PluginInstanceAllocateFunctionPtr)plugin_instance_allocate,
(NPW_PluginInstanceDeallocateFunctionPtr)plugin_instance_deallocate,
(NPW_PluginInstanceFinalizeFunctionPtr)plugin_instance_finalize,
(NPW_PluginInstanceInvalidateFunctionPtr)plugin_instance_invalidate
};
static void *plugin_instance_allocate(void)
{
return NPW_MemNew0(PluginInstance, 1);
}
static void plugin_instance_deallocate(PluginInstance *plugin)
{
NPW_MemFree(plugin);
}
static void plugin_instance_finalize(PluginInstance *plugin)
{
id_remove(plugin->instance_id);
rpc_connection_unref(plugin->connection);
}
static void plugin_instance_invalidate(PluginInstance *plugin)
{
/* Browser's NPP instance is no longer valid beyond this point. So,
let's just break the link to nspluginwrapper's PluginInstance now. */
if (plugin->instance) {
plugin->instance->pdata = NULL;
plugin->instance = NULL;
}
/* We don't reset instance_id here because we still need the NPP ->
PluginInstance mapping for incoming RPC. However, the important
thing is plugin->instance to be NULL. */
}
/* ====================================================================== */
/* === Plug-in side data === */
/* ====================================================================== */
// Functions supplied by the plug-in
static NPPluginFuncs plugin_funcs;
// Allows the browser to query the plug-in supported formats
static NP_GetMIMEDescriptionFunc g_plugin_NP_GetMIMEDescription = NULL;
// Allows the browser to query the plug-in for information
static NP_GetValueFunc g_plugin_NP_GetValue = NULL;
// Provides global initialization for a plug-in
static NP_InitializeFunc g_plugin_NP_Initialize = NULL;
// Provides global deinitialization for a plug-in
static NP_ShutdownFunc g_plugin_NP_Shutdown = NULL;
// Plugin native library handle
static void *plugin_handle = NULL;
static bool plugin_load_native(void)
{
void *handle;
const char *error;
if ((handle = dlopen(plugin_path, RTLD_LOCAL|RTLD_LAZY)) == NULL) {
npw_printf("ERROR: %s\n", dlerror());
return false;
}
dlerror();
g_plugin_NP_GetMIMEDescription = (NP_GetMIMEDescriptionFunc)dlsym(handle, "NP_GetMIMEDescription");
if ((error = dlerror()) != NULL) {
npw_printf("ERROR: %s\n", error);
dlclose(handle);
return false;
}
g_plugin_NP_Initialize = (NP_InitializeFunc)dlsym(handle, "NP_Initialize");
if ((error = dlerror()) != NULL) {
npw_printf("ERROR: %s\n", error);
dlclose(handle);
return false;
}
g_plugin_NP_Shutdown = (NP_ShutdownFunc)dlsym(handle, "NP_Shutdown");
if ((error = dlerror()) != NULL) {
npw_printf("ERROR: %s\n", error);
dlclose(handle);
return false;
}
g_plugin_NP_GetValue = (NP_GetValueFunc)dlsym(handle, "NP_GetValue");
plugin_handle = handle;
return true;
}
// Check for direct execution of the plugin
static inline bool plugin_has_direct_exec_env(void)
{
if (getenv("NPW_DIRECT_EXEC"))
return true;
if (getenv("NPW_DIRECT_EXECUTION"))
return true;
return false;
}
static bool plugin_can_direct_exec(void)
{
if (!plugin_has_direct_exec_env())
return false;
if (!plugin_load_native())
return false;
// XXX: really check for same OS/ARCH
D(bug("Run plugin natively\n"));
return true;
}
static inline bool plugin_direct_exec(void)
{
#if ALLOW_DIRECT_EXECUTION
static int g_plugin_direct_exec = -1;
if (G_UNLIKELY(g_plugin_direct_exec < 0))
g_plugin_direct_exec = plugin_can_direct_exec();
return g_plugin_direct_exec;
#else
return false;
#endif
}
#define PLUGIN_DIRECT_EXEC plugin_direct_exec()
/* ====================================================================== */
/* === RPC communication === */
/* ====================================================================== */
static GSource *g_rpc_source;
static GSource *g_rpc_sync_source;
static XtInputId xt_rpc_source_id;
static XtBlockHookId xt_rpc_sync_id;
rpc_connection_t *g_rpc_connection attribute_hidden = NULL;
/* ====================================================================== */
/* === 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_t size)
{
return mozilla_funcs.memalloc(size);
}
// NPN_MemFree
static inline void g_NPN_MemFree(void* ptr)
{
mozilla_funcs.memfree(ptr);
}
// NPN_MemFlush
static inline uint32_t g_NPN_MemFlush(uint32_t size)
{
return mozilla_funcs.memflush(size);
}
// NPN_ReloadPlugins
static void
g_NPN_ReloadPlugins(NPBool reloadPages)
{
D(bug("NPN_ReloadPlugins reloadPages=%d\n", reloadPages));
NPW_UNIMPLEMENTED();
}
// NPN_GetJavaEnv
static void *
g_NPN_GetJavaEnv(void)
{
D(bug("NPN_GetJavaEnv\n"));
return NULL;
}
// NPN_GetJavaPeer
static void *
g_NPN_GetJavaPeer(NPP instance)
{
D(bug("NPN_GetJavaPeer instance=%p\n", instance));
return NULL;
}
// NPN_UserAgent
static const char *g_NPN_UserAgent(NPP instance)
{
if (mozilla_funcs.uagent == NULL)
return NULL;
D(bugiI("NPN_UserAgent instance=%p\n", instance));
const char *user_agent = mozilla_funcs.uagent(instance);
D(bugiD("NPN_UserAgent return: '%s'\n", user_agent));
return user_agent;
}
static int handle_NPN_UserAgent(rpc_connection_t *connection)
{
D(bug("handle_NPN_UserAgent\n"));
int error = rpc_method_get_args(connection, RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_UserAgent() get args", error);
return error;
}
const char *user_agent = g_NPN_UserAgent(NULL);
return rpc_method_send_reply(connection, RPC_TYPE_STRING, user_agent, RPC_TYPE_INVALID);
}
// NPN_Status
static void
g_NPN_Status(NPP instance, const char *message)
{
if (mozilla_funcs.status == NULL)
return;
D(bugiI("NPN_Status instance=%p, message='%s'\n", instance, message));
mozilla_funcs.status(instance, message);
D(bugiD("NPN_Status done\n"));
}
static int handle_NPN_Status(rpc_connection_t *connection)
{
D(bug("handle_NPN_Status\n"));
PluginInstance *plugin;
char *message;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_STRING, &message,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_Status() get args", error);
return error;
}
g_NPN_Status(PLUGIN_INSTANCE_NPP(plugin), message);
if (message)
free(message);
return rpc_method_send_reply (connection, RPC_TYPE_INVALID);
}
// NPN_GetValue
static NPError
g_NPN_GetValue(NPP instance, NPNVariable variable, void *value)
{
if (mozilla_funcs.getvalue == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
D(bugiI("NPN_GetValue instance=%p, variable=%d [%s]\n", instance, variable, string_of_NPNVariable(variable)));
NPError ret = mozilla_funcs.getvalue(instance, variable, value);
D(bugiD("NPN_GetValue return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
static int handle_NPN_GetValue(rpc_connection_t *connection)
{
D(bug("handle_NPN_GetValue\n"));
PluginInstance *plugin;
uint32_t variable;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_UINT32, &variable,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_GetValue() get args", error);
return error;
}
// Work around a Chromium sort-of bug and check for NULL NPPs. It is
// only sort of a bug in that we are doing something our plugin had
// never requested. Unfortunately, the existing RPC system makes
// detecting this very difficult. So, hack in the checks Chromium
// was missing.
//
// TODO: Either fix bug #13 or remove this when the fix in Chromium
// has trickled down to the stable channel.
bool valid_instance = true;
if (PLUGIN_INSTANCE_NPP(plugin) == NULL) {
switch (variable) {
case NPNVWindowNPObject:
case NPNVPluginElementNPObject:
case NPNVprivateModeBool:
case NPNVnetscapeWindow:
D(bug("Skipping NPN_GetValue on NULL instance to avoid possible crash.\n"));
valid_instance = false;
}
}
NPError ret = NPERR_GENERIC_ERROR;
switch (rpc_type_of_NPNVariable(variable)) {
case RPC_TYPE_UINT32:
{
uint32_t n = 0;
if (valid_instance)
ret = g_NPN_GetValue(PLUGIN_INSTANCE_NPP(plugin), variable, (void *)&n);
return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_UINT32, n, RPC_TYPE_INVALID);
}
case RPC_TYPE_BOOLEAN:
{
NPBool b = FALSE;
if (valid_instance)
ret = g_NPN_GetValue(PLUGIN_INSTANCE_NPP(plugin), variable, (void *)&b);
return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_BOOLEAN, b, RPC_TYPE_INVALID);
}
case RPC_TYPE_STRING:
{
char *str = NULL;
ret = g_NPN_GetValue(PLUGIN_INSTANCE_NPP(plugin), variable, (void *)&str);
error = rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_STRING, str, RPC_TYPE_INVALID);
if (str)
NPN_MemFree(str);
return error;
}
case RPC_TYPE_NP_OBJECT:
{
NPObject *npobj = NULL;
if (valid_instance)
ret = g_NPN_GetValue(PLUGIN_INSTANCE_NPP(plugin), variable, (void *)&npobj);
return rpc_method_send_reply(connection,
RPC_TYPE_INT32, ret,
RPC_TYPE_NP_OBJECT_PASS_REF, npobj,
RPC_TYPE_INVALID);
}
}
abort();
}
// NPN_SetValue
static NPError
g_NPN_SetValue(NPP instance, NPPVariable variable, void *value)
{
if (mozilla_funcs.setvalue == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
D(bugiI("NPN_SetValue instance=%p, variable=%d [%s]\n", instance, variable, string_of_NPPVariable(variable)));
NPError ret = mozilla_funcs.setvalue(instance, variable, value);
D(bugiD("NPN_SetValue return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
static int handle_NPN_SetValue(rpc_connection_t *connection)
{
D(bug("handle_NPN_SetValue\n"));
PluginInstance *plugin;
uint32_t variable, value;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_UINT32, &variable,
RPC_TYPE_BOOLEAN, &value,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_SetValue() get args", error);
return error;
}
NPError ret = NPERR_GENERIC_ERROR;
// Work around a Chromium sort-of bug and check for NULL NPPs. It is
// only sort of a bug in that we are doing something our plugin had
// never requested. Unfortunately, the existing RPC system makes
// detecting this very difficult. So, hack in the checks Chromium
// was missing.
//
// TODO: Either fix bug #13 or remove this when the fix in Chromium
// has trickled down to the stable channel.
if (PLUGIN_INSTANCE_NPP(plugin) == NULL) {
D(bug("Skipping NPN_SetValue on NULL instance to avoid possible crash.\n"));
ret = NPERR_INVALID_INSTANCE_ERROR;
} else {
ret = g_NPN_SetValue(PLUGIN_INSTANCE_NPP(plugin), variable, (void *)(uintptr_t)value);
}
return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INVALID);
}
// NPN_InvalidateRect
static void g_NPN_InvalidateRect(NPP instance, NPRect *invalidRect)
{
if (mozilla_funcs.invalidaterect == NULL)
return;
D(bugiI("NPN_InvalidateRect instance=%p "
"rect.top=%d rect.left=%d rect.bottom=%d rect.right=%d\n",
instance,
invalidRect->top, invalidRect->left,
invalidRect->bottom, invalidRect->right));
mozilla_funcs.invalidaterect(instance, invalidRect);
D(bugiD("NPN_InvalidateRect done\n"));
}
static int handle_NPN_InvalidateRect(rpc_connection_t *connection)
{
D(bug("handle_NPN_InvalidateRect\n"));
PluginInstance *plugin;
NPRect invalidRect;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_NP_RECT, &invalidRect,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_InvalidateRect() get args", error);
return error;
}
g_NPN_InvalidateRect(PLUGIN_INSTANCE_NPP(plugin), &invalidRect);
return rpc_method_send_reply (connection, RPC_TYPE_INVALID);
}
// NPN_InvalidateRegion
static void
g_NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion)
{
D(bug("NPN_InvalidateRegion instance=%p\n", instance));
NPW_UNIMPLEMENTED();
}
// NPN_ForceRedraw
static void
g_NPN_ForceRedraw(NPP instance)
{
D(bug("NPN_ForceRedraw instance=%p\n", instance));
NPW_UNIMPLEMENTED();
}
// NPN_GetURL
static NPError g_NPN_GetURL(NPP instance, const char *url, const char *target)
{
if (mozilla_funcs.geturl == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
D(bugiI("NPN_GetURL instance=%p, url='%s', target='%s'\n", instance, url, target));
NPError ret = mozilla_funcs.geturl(instance, url, target);
D(bugiD("NPN_GetURL return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
static int handle_NPN_GetURL(rpc_connection_t *connection)
{
D(bug("handle_NPN_GetURL\n"));
PluginInstance *plugin;
char *url, *target;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_STRING, &url,
RPC_TYPE_STRING, &target,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_GetURL() get args", error);
return error;
}
NPError ret = g_NPN_GetURL(PLUGIN_INSTANCE_NPP(plugin), url, target);
if (url)
free(url);
if (target)
free(target);
return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INVALID);
}
// NPN_GetURLNotify
static NPError g_NPN_GetURLNotify(NPP instance, const char *url, const char *target, void *notifyData)
{
if (mozilla_funcs.geturlnotify == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
D(bugiI("NPN_GetURLNotify instance=%p, url='%s', target='%s', notifyData=%p\n", instance, url, target, notifyData));
NPError ret = mozilla_funcs.geturlnotify(instance, url, target, notifyData);
D(bugiD("NPN_GetURLNotify return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
static int handle_NPN_GetURLNotify(rpc_connection_t *connection)
{
D(bug("handle_NPN_GetURLNotify\n"));
PluginInstance *plugin;
char *url, *target;
void *notifyData;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_STRING, &url,
RPC_TYPE_STRING, &target,
RPC_TYPE_NP_NOTIFY_DATA, &notifyData,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_GetURLNotify() get args", error);
return error;
}
NPError ret = g_NPN_GetURLNotify(PLUGIN_INSTANCE_NPP(plugin), url, target, notifyData);
if (url)
free(url);
if (target)
free(target);
return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INVALID);
}
// NPN_PostURL
static NPError g_NPN_PostURL(NPP instance, const char *url, const char *target, uint32_t len, const char *buf, NPBool file)
{
if (mozilla_funcs.posturl == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
D(bugiI("NPN_PostURL instance=%p, url='%s', target='%s', file='%s'\n", instance, url, target, file ? buf : "<raw-data>"));
NPError ret = mozilla_funcs.posturl(instance, url, target, len, buf, file);
D(bugiD("NPN_PostURL return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
static int handle_NPN_PostURL(rpc_connection_t *connection)
{
D(bug("handle_NPN_PostURL\n"));
PluginInstance *plugin;
char *url, *target;
uint32_t len;
char *buf;
uint32_t file;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_STRING, &url,
RPC_TYPE_STRING, &target,
RPC_TYPE_ARRAY, RPC_TYPE_CHAR, &len, &buf,
RPC_TYPE_BOOLEAN, &file,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_PostURL() get args", error);
return error;
}
NPError ret = g_NPN_PostURL(PLUGIN_INSTANCE_NPP(plugin), url, target, len, buf, file);
if (url)
free(url);
if (target)
free(target);
if (buf)
free(buf);
return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INVALID);
}
// NPN_PostURLNotify
static NPError g_NPN_PostURLNotify(NPP instance, const char *url, const char *target, uint32_t len, const char *buf, NPBool file, void *notifyData)
{
if (mozilla_funcs.posturlnotify == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
D(bugiI("NPN_PostURLNotify instance=%p, url='%s', target='%s', file='%s', notifyData=%p\n", instance, url, target, file ? buf : "<raw-data>", notifyData));
NPError ret = mozilla_funcs.posturlnotify(instance, url, target, len, buf, file, notifyData);
D(bugiD("NPN_PostURLNotify return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
static int handle_NPN_PostURLNotify(rpc_connection_t *connection)
{
D(bug("handle_NPN_PostURLNotify\n"));
PluginInstance *plugin;
char *url, *target;
int32_t len;
char *buf;
uint32_t file;
void *notifyData;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_STRING, &url,
RPC_TYPE_STRING, &target,
RPC_TYPE_ARRAY, RPC_TYPE_CHAR, &len, &buf,
RPC_TYPE_BOOLEAN, &file,
RPC_TYPE_NP_NOTIFY_DATA, &notifyData,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_PostURLNotify() get args", error);
return error;
}
NPError ret = g_NPN_PostURLNotify(PLUGIN_INSTANCE_NPP(plugin), url, target, len, buf, file, notifyData);
if (url)
free(url);
if (target)
free(target);
if (buf)
free(buf);
return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INVALID);
}
// NPN_PrintData
static int handle_NPN_PrintData(rpc_connection_t *connection)
{
D(bug("handle_NPN_PrintData\n"));
uint32_t platform_print_id;
NPPrintData printData;
int error = rpc_method_get_args(connection,
RPC_TYPE_UINT32, &platform_print_id,
RPC_TYPE_NP_PRINT_DATA, &printData,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_PrintData() get args", error);
return error;
}
NPPrintCallbackStruct *platformPrint = id_lookup(platform_print_id);
if (platformPrint == NULL)
return RPC_ERROR_GENERIC;
D(bug(" platformPrint=%p, printData.size=%d\n", platformPrint, printData.size));
if (fwrite(printData.data, printData.size, 1, platformPrint->fp) != 1)
return RPC_ERROR_ERRNO_SET;
return rpc_method_send_reply (connection, RPC_TYPE_INVALID);
}
// NPN_RequestRead
static NPError g_NPN_RequestRead(NPStream *stream, NPByteRange *rangeList)
{
if (mozilla_funcs.requestread == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
D(bugiI("NPN_RequestRead stream=%p, rangeList=%p\n", stream, rangeList));
NPError ret = mozilla_funcs.requestread(stream, rangeList);
D(bugiD("NPN_RequestRead return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
static int handle_NPN_RequestRead(rpc_connection_t *connection)
{
D(bug("handle_NPN_RequestRead\n"));
NPStream *stream;
NPByteRange *rangeList;
int error = rpc_method_get_args(connection,
RPC_TYPE_NP_STREAM, &stream,
RPC_TYPE_NP_BYTE_RANGE, &rangeList,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_RequestRead() get args", error);
return error;
}
NPError ret = g_NPN_RequestRead(stream, rangeList);
while (rangeList) {
NPByteRange *p = rangeList;
rangeList = rangeList->next;
free(p);
}
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(bugiI("NPN_NewStream instance=%p, type='%s', target='%s'\n", instance, type, target));
NPError ret = mozilla_funcs.newstream(instance, type, target, stream);
D(bugiD("NPN_NewStream return: %d [%s]\n", ret, string_of_NPError(ret)));
if (PLUGIN_DIRECT_EXEC)
return 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"));
PluginInstance *plugin;
char *type;
char *target;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
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(PLUGIN_INSTANCE_NPP(plugin), 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_DestroyStream
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
if (!PLUGIN_DIRECT_EXEC) {
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(bugiI("NPN_DestroyStream instance=%p, stream=%p, reason=%s\n",
instance, stream, string_of_NPReason(reason)));
NPError ret = mozilla_funcs.destroystream(instance, stream, reason);
D(bugiD("NPN_DestroyStream 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"));
PluginInstance *plugin;
NPStream *stream;
int32_t reason;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
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(PLUGIN_INSTANCE_NPP(plugin), stream, reason);
return rpc_method_send_reply(connection, RPC_TYPE_INT32, ret, RPC_TYPE_INVALID);
}
// NPN_Write
static int32_t g_NPN_Write(NPP instance, NPStream *stream, int32_t len, void *buf)
{
if (mozilla_funcs.write == NULL)
return -1;
if (stream == NULL)
return -1;
D(bugiI("NPN_Write instance=%p\n", instance));
int32_t ret = mozilla_funcs.write(instance, stream, len, buf);
D(bugiD("NPN_Write return: %d\n", ret));
return ret;
}
static int handle_NPN_Write(rpc_connection_t *connection)
{
D(bug("handle_NPN_Write\n"));
PluginInstance *plugin;
NPStream *stream;
unsigned char *buf;
int32_t len;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
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_t ret = g_NPN_Write(PLUGIN_INSTANCE_NPP(plugin), 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(bugiI("NPN_PushPopupsEnabledState instance=%p, enabled=%d\n", instance, enabled));
mozilla_funcs.pushpopupsenabledstate(instance, enabled);
D(bugiD("NPN_PushPopupsEnabledState done\n"));
}
static int handle_NPN_PushPopupsEnabledState(rpc_connection_t *connection)
{
D(bug("handle_NPN_PushPopupsEnabledState\n"));
PluginInstance *plugin;
uint32_t enabled;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
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(PLUGIN_INSTANCE_NPP(plugin), enabled);
return rpc_method_send_reply (connection, RPC_TYPE_INVALID);
}
// NPN_PopPopupsEnabledState
static void g_NPN_PopPopupsEnabledState(NPP instance)
{
if (mozilla_funcs.poppopupsenabledstate == NULL)
return;
D(bugiI("NPN_PopPopupsEnabledState instance=%p\n", instance));
mozilla_funcs.poppopupsenabledstate(instance);
D(bugiD("NPN_PopPopupsEnabledState done\n"));
}
static int handle_NPN_PopPopupsEnabledState(rpc_connection_t *connection)
{
D(bug("handle_NPN_PopPopupsEnabledState\n"));
PluginInstance *plugin;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_PopPopupsEnabledState() get args", error);
return error;
}
g_NPN_PopPopupsEnabledState(PLUGIN_INSTANCE_NPP(plugin));
return rpc_method_send_reply (connection, RPC_TYPE_INVALID);
}
/* ====================================================================== */
/* === NPRuntime glue === */
/* ====================================================================== */
// NPN_CreateObject
static NPObject *
g_NPN_CreateObject(NPP instance, NPClass *klass)
{
D(bugiI("NPN_CreateObject instance=%p, aClass=%p\n", instance, klass));
NPObject *npobj = mozilla_funcs.createobject(instance, klass);
D(bugiD("NPN_CreateObject return: %p\n", npobj));
return npobj;
}
// NPN_RetainObject
static NPObject *
g_NPN_RetainObject(NPObject *npobj)
{
D(bugiI("NPN_RetainObject npobj=%p\n", npobj));
NPObject *new_npobj = mozilla_funcs.retainobject(npobj);
D(bugiD("NPN_RetainObject return: %p (refcount: %d)\n", new_npobj, new_npobj->referenceCount));
return new_npobj;
}
// NPN_ReleaseObject
static void
g_NPN_ReleaseObject(NPObject *npobj)
{
D(bugiI("NPN_ReleaseObject npobj=%p\n", npobj));
uint32_t refcount = npobj->referenceCount - 1;
NPN_ReleaseObject(npobj);
D(bugiD("NPN_ReleaseObject done (refcount: %d)\n", refcount));
}
// NPN_Invoke
static bool
g_NPN_Invoke(NPP instance, NPObject *npobj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
D(bugiI("NPN_Invoke instance=%p, npobj=%p, methodName=%p\n", instance, npobj, methodName));
print_npvariant_args(args, argCount);
bool ret = mozilla_funcs.invoke(instance, npobj, methodName, args, argCount, result);
gchar *result_str = string_of_NPVariant(result);
D(bugiD("NPN_Invoke return: %d (%s)\n", ret, result_str));
g_free(result_str);
return ret;
}
static int handle_NPN_Invoke(rpc_connection_t *connection)
{
D(bug("handle_NPN_Invoke()\n"));
PluginInstance *plugin;
NPObject *npobj;
NPIdentifier methodName;
NPVariant *args;
uint32_t argCount;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_NP_OBJECT, &npobj,
RPC_TYPE_NP_IDENTIFIER, &methodName,
RPC_TYPE_ARRAY, RPC_TYPE_NP_VARIANT, &argCount, &args,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_Invoke() get args", error);
return error;
}
NPVariant result;
VOID_TO_NPVARIANT(result);
bool ret = g_NPN_Invoke(PLUGIN_INSTANCE_NPP(plugin), npobj, methodName, args, argCount, &result);
if (npobj)
NPN_ReleaseObject(npobj);
if (args) {
for (int i = 0; i < argCount; i++)
NPN_ReleaseVariantValue(&args[i]);
free(args);
}
return rpc_method_send_reply(connection,
RPC_TYPE_UINT32, ret,
RPC_TYPE_NP_VARIANT_PASS_REF, &result,
RPC_TYPE_INVALID);
}
// NPN_InvokeDefault
static bool
g_NPN_InvokeDefault(NPP instance, NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
D(bugiI("NPN_InvokeDefault instance=%p, npobj=%p\n", instance, npobj));
print_npvariant_args(args, argCount);
bool ret = mozilla_funcs.invokeDefault(instance, npobj, args, argCount, result);
gchar *result_str = string_of_NPVariant(result);
D(bugiD("NPN_InvokeDefault return: %d (%s)\n", ret, result_str));
g_free(result_str);
return ret;
}
static int handle_NPN_InvokeDefault(rpc_connection_t *connection)
{
D(bug("handle_NPN_InvokeDefault\n"));
PluginInstance *plugin;
NPObject *npobj;
NPVariant *args;
uint32_t argCount;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_NP_OBJECT, &npobj,
RPC_TYPE_ARRAY, RPC_TYPE_NP_VARIANT, &argCount, &args,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_InvokeDefault() get args", error);
return error;
}
NPVariant result;
VOID_TO_NPVARIANT(result);
bool ret = g_NPN_InvokeDefault(PLUGIN_INSTANCE_NPP(plugin), npobj, args, argCount, &result);
if (npobj)
NPN_ReleaseObject(npobj);
if (args) {
for (int i = 0; i < argCount; i++)
NPN_ReleaseVariantValue(&args[i]);
free(args);
}
return rpc_method_send_reply(connection,
RPC_TYPE_UINT32, ret,
RPC_TYPE_NP_VARIANT_PASS_REF, &result,
RPC_TYPE_INVALID);
}
// NPN_Evaluate
static bool
g_NPN_Evaluate(NPP instance, NPObject *npobj, NPString *script, NPVariant *result)
{
D(bugiI("NPN_Evaluate instance=%p, npobj=%p\n", instance, npobj));
D(bug("script = '%.*s'\n", script->UTF8Length, script->UTF8Characters));
bool ret = mozilla_funcs.evaluate(instance, npobj, script, result);
gchar *result_str = string_of_NPVariant(result);
D(bugiD("NPN_Evaluate return: %d (%s)\n", ret, result_str));
g_free(result_str);
return ret;
}
static int handle_NPN_Evaluate(rpc_connection_t *connection)
{
D(bug("handle_NPN_Evaluate\n"));
PluginInstance *plugin;
NPObject *npobj;
NPString script;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_NP_OBJECT, &npobj,
RPC_TYPE_NP_STRING, &script,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_Evaluate() get args", error);
return error;
}
NPVariant result;
VOID_TO_NPVARIANT(result);
bool ret = g_NPN_Evaluate(PLUGIN_INSTANCE_NPP(plugin), npobj, &script, &result);
if (npobj)
NPN_ReleaseObject(npobj);
if (script.UTF8Characters)
NPN_MemFree((void *)script.UTF8Characters);
return rpc_method_send_reply(connection,
RPC_TYPE_UINT32, ret,
RPC_TYPE_NP_VARIANT_PASS_REF, &result,
RPC_TYPE_INVALID);
}
// NPN_GetProperty
static bool
g_NPN_GetProperty(NPP instance, NPObject *npobj, NPIdentifier propertyName, NPVariant *result)
{
D(bugiI("NPN_GetProperty instance=%p, npobj=%p, propertyName=%p\n", instance, npobj, propertyName));
bool ret = mozilla_funcs.getproperty(instance, npobj, propertyName, result);
gchar *result_str = string_of_NPVariant(result);
D(bugiD("NPN_GetProperty return: %d (%s)\n", ret, result_str));
g_free(result_str);
return ret;
}
static int handle_NPN_GetProperty(rpc_connection_t *connection)
{
D(bug("handle_NPN_GetProperty\n"));
PluginInstance *plugin;
NPObject *npobj;
NPIdentifier propertyName;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_NP_OBJECT, &npobj,
RPC_TYPE_NP_IDENTIFIER, &propertyName,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_GetProperty() get args", error);
return error;
}
NPVariant result;
VOID_TO_NPVARIANT(result);
bool ret = g_NPN_GetProperty(PLUGIN_INSTANCE_NPP(plugin), npobj, propertyName, &result);
if (npobj)
NPN_ReleaseObject(npobj);
return rpc_method_send_reply(connection,
RPC_TYPE_UINT32, ret,
RPC_TYPE_NP_VARIANT_PASS_REF, &result,
RPC_TYPE_INVALID);
}
// NPN_SetProperty
static bool
g_NPN_SetProperty(NPP instance, NPObject *npobj, NPIdentifier propertyName, const NPVariant *value)
{
D(bugiI("NPN_SetProperty instance=%p, npobj=%p, propertyName=%p\n", instance, npobj, propertyName));
bool ret = mozilla_funcs.setproperty(instance, npobj, propertyName, value);
D(bugiD("NPN_SetProperty return: %d\n", ret));
return ret;
}
static int handle_NPN_SetProperty(rpc_connection_t *connection)
{
D(bug("handle_NPN_SetProperty\n"));
PluginInstance *plugin;
NPObject *npobj;
NPIdentifier propertyName;
NPVariant value;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_NP_OBJECT, &npobj,
RPC_TYPE_NP_IDENTIFIER, &propertyName,
RPC_TYPE_NP_VARIANT, &value,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_SetProperty() get args", error);
return error;
}
bool ret = g_NPN_SetProperty(PLUGIN_INSTANCE_NPP(plugin), npobj, propertyName, &value);
if (npobj)
NPN_ReleaseObject(npobj);
NPN_ReleaseVariantValue(&value);
return rpc_method_send_reply(connection,
RPC_TYPE_UINT32, ret,
RPC_TYPE_INVALID);
}
// NPN_RemoveProperty
static bool
g_NPN_RemoveProperty(NPP instance, NPObject *npobj, NPIdentifier propertyName)
{
D(bugiI("NPN_RemoveProperty instance=%p, npobj=%p, propertyName=%p\n", instance, npobj, propertyName));
bool ret = mozilla_funcs.removeproperty(instance, npobj, propertyName);
D(bugiD("NPN_RemoveProperty return: %d\n", ret));
return ret;
}
static int handle_NPN_RemoveProperty(rpc_connection_t *connection)
{
D(bug("handle_NPN_RemoveProperty\n"));
PluginInstance *plugin;
NPObject *npobj;
NPIdentifier propertyName;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_NP_OBJECT, &npobj,
RPC_TYPE_NP_IDENTIFIER, &propertyName,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_RemoveProperty() get args", error);
return error;
}
bool ret = g_NPN_RemoveProperty(PLUGIN_INSTANCE_NPP(plugin), npobj, propertyName);
if (npobj)
NPN_ReleaseObject(npobj);
return rpc_method_send_reply(connection,
RPC_TYPE_UINT32, ret,
RPC_TYPE_INVALID);
}
// NPN_HasProperty
static bool
g_NPN_HasProperty(NPP instance, NPObject *npobj, NPIdentifier propertyName)
{
D(bugiI("NPN_HasProperty instance=%p, npobj=%p, propertyName=%p\n", instance, npobj, propertyName));
bool ret = mozilla_funcs.hasproperty(instance, npobj, propertyName);
D(bugiD("NPN_HasProperty return: %d\n", ret));
return ret;
}
static int handle_NPN_HasProperty(rpc_connection_t *connection)
{
D(bug("handle_NPN_HasProperty\n"));
PluginInstance *plugin;
NPObject *npobj;
NPIdentifier propertyName;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_NP_OBJECT, &npobj,
RPC_TYPE_NP_IDENTIFIER, &propertyName,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_HasProperty() get args", error);
return error;
}
bool ret = g_NPN_HasProperty(PLUGIN_INSTANCE_NPP(plugin), npobj, propertyName);
if (npobj)
NPN_ReleaseObject(npobj);
return rpc_method_send_reply(connection,
RPC_TYPE_UINT32, ret,
RPC_TYPE_INVALID);
}
// NPN_HasMethod
static bool
g_NPN_HasMethod(NPP instance, NPObject *npobj, NPIdentifier methodName)
{
D(bugiI("NPN_HasMethod instance=%p, npobj=%p, methodName=%p\n", instance, npobj, methodName));
bool ret = mozilla_funcs.hasmethod(instance, npobj, methodName);
D(bugiD("NPN_HasMethod return: %d\n", ret));
return ret;
}
static int handle_NPN_HasMethod(rpc_connection_t *connection)
{
D(bug("handle_NPN_HasMethod\n"));
PluginInstance *plugin;
NPObject *npobj;
NPIdentifier methodName;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_NP_OBJECT, &npobj,
RPC_TYPE_NP_IDENTIFIER, &methodName,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_HasMethod() get args", error);
return error;
}
bool ret = g_NPN_HasMethod(PLUGIN_INSTANCE_NPP(plugin), npobj, methodName);
if (npobj)
NPN_ReleaseObject(npobj);
return rpc_method_send_reply(connection,
RPC_TYPE_UINT32, ret,
RPC_TYPE_INVALID);
}
// NPN_Enumerate
static bool
g_NPN_Enumerate(NPP instance, NPObject *npobj, NPIdentifier **identifiers,
uint32_t *count)
{
if (mozilla_funcs.enumerate == NULL)
return false;
D(bugiI("NPN_Enumerate instance=%p, npobj=%p\n", instance, npobj));
bool ret = mozilla_funcs.enumerate(instance, npobj, identifiers, count);
D(bugiD("NPN_Enumerate return: %d\n", ret));
return ret;
}
static int handle_NPN_Enumerate(rpc_connection_t *connection)
{
D(bug("handle_NPN_Enumerate\n"));
PluginInstance *plugin;
NPObject *npobj;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_NP_OBJECT, &npobj,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_Enumerate() get args", error);
return error;
}
NPIdentifier *identifiers = NULL;
uint32_t count = 0;
bool ret = g_NPN_Enumerate(PLUGIN_INSTANCE_NPP(plugin), npobj, &identifiers, &count);
if (npobj)
NPN_ReleaseObject(npobj);
error = rpc_method_send_reply(connection,
RPC_TYPE_UINT32, ret,
RPC_TYPE_ARRAY, RPC_TYPE_NP_IDENTIFIER, count, identifiers,
RPC_TYPE_INVALID);
NPN_MemFree(identifiers);
return error;
}
// NPN_Construct
static bool
g_NPN_Construct(NPP instance, NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
if (mozilla_funcs.construct == NULL)
return false;
D(bugiI("NPN_Construct instance=%p, npobj=%p\n", instance, npobj));
print_npvariant_args(args, argCount);
bool ret = mozilla_funcs.construct(instance, npobj, args, argCount, result);
gchar *result_str = string_of_NPVariant(result);
D(bugiD("NPN_Construct return: %d (%s)\n", ret, result_str));
g_free(result_str);
return ret;
}
static int handle_NPN_Construct(rpc_connection_t *connection)
{
D(bug("handle_NPN_Construct\n"));
PluginInstance *plugin;
NPObject *npobj;
NPVariant *args;
uint32_t argCount;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_NP_OBJECT, &npobj,
RPC_TYPE_ARRAY, RPC_TYPE_NP_VARIANT, &argCount, &args,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_Construct() get args", error);
return error;
}
NPVariant result;
VOID_TO_NPVARIANT(result);
bool ret = g_NPN_Construct(PLUGIN_INSTANCE_NPP(plugin), npobj, args, argCount, &result);
if (npobj)
NPN_ReleaseObject(npobj);
if (args) {
for (int i = 0; i < argCount; i++)
NPN_ReleaseVariantValue(&args[i]);
free(args);
}
return rpc_method_send_reply(connection,
RPC_TYPE_UINT32, ret,
RPC_TYPE_NP_VARIANT_PASS_REF, &result,
RPC_TYPE_INVALID);
}
// NPN_SetException
static void
g_NPN_SetException(NPObject *npobj, const char *message)
{
D(bugiI("NPN_SetException npobj=%p, message='%s'\n", npobj, message));
mozilla_funcs.setexception(npobj, message);
D(bugiD("NPN_SetException done\n"));
}
static int handle_NPN_SetException(rpc_connection_t *connection)
{
D(bug("handle_NPN_SetException\n"));
NPObject *npobj;
NPUTF8 *message;
int error = rpc_method_get_args(connection,
RPC_TYPE_NP_OBJECT, &npobj,
RPC_TYPE_STRING, &message,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_SetException() get args", error);
return error;
}
g_NPN_SetException(npobj, message);
if (npobj)
NPN_ReleaseObject(npobj);
if (message)
free(message);
return rpc_method_send_reply (connection, RPC_TYPE_INVALID);
}
// NPN_GetStringIdentifier
static NPIdentifier
g_NPN_GetStringIdentifier(const char *name)
{
D(bugiI("NPN_GetStringIdentifier name='%s'\n", name));
NPIdentifier ident = mozilla_funcs.getstringidentifier(name);
D(bugiD("NPN_GetStringIdentifier return: %p\n", ident));
return ident;
}
static int handle_NPN_GetStringIdentifier(rpc_connection_t *connection)
{
D(bug("handle_NPN_GetStringIdentifier\n"));
char *name;
int error = rpc_method_get_args(connection,
RPC_TYPE_STRING, &name,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_GetStringIdentifier() get args", error);
return error;
}
NPIdentifier ident = g_NPN_GetStringIdentifier(name);
if (name)
free(name);
return rpc_method_send_reply(connection,
RPC_TYPE_NP_IDENTIFIER, &ident,
RPC_TYPE_INVALID);
}
// NPN_GetStringIdentifiers
static void
g_NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier *idents)
{
D(bugiI("NPN_GetStringIdentifiers nameCount=%d\n", nameCount));
mozilla_funcs.getstringidentifiers(names, nameCount, idents);
D(bugiD("NPN_GetStringIdentifiers done\n"));
}
static int handle_NPN_GetStringIdentifiers(rpc_connection_t *connection)
{
D(bug("handle_NPN_GetStringIdentifiers\n"));
NPUTF8 **names;
int32_t nameCount;
int error = rpc_method_get_args(connection,
RPC_TYPE_ARRAY, RPC_TYPE_STRING, &nameCount, &names,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_GetStringIdentifiers() get args", error);
return error;
}
NPIdentifier *idents = NPW_MemNew0(NPIdentifier, nameCount);
if (idents)
g_NPN_GetStringIdentifiers((const NPUTF8 **)names, nameCount, idents);
if (names) {
for (int i = 0; i < nameCount; i++)
free(names[i]);
free(names);
}
int rpc_ret = rpc_method_send_reply(connection,
RPC_TYPE_ARRAY, RPC_TYPE_NP_IDENTIFIER, nameCount, idents,
RPC_TYPE_INVALID);
NPW_MemFree(idents);
return rpc_ret;
}
// NPN_GetIntIdentifier
static NPIdentifier
g_NPN_GetIntIdentifier(int32_t intid)
{
D(bugiI("NPN_GetIntIdentifier intid=%d\n", intid));
NPIdentifier ident = mozilla_funcs.getintidentifier(intid);
D(bugiD("NPN_GetIntIdentifier return: %p\n", ident));
return ident;
}
static int handle_NPN_GetIntIdentifier(rpc_connection_t *connection)
{
D(bug("handle_NPN_GetIntIdentifier\n"));
int32_t intid;
int error = rpc_method_get_args(connection,
RPC_TYPE_INT32, &intid,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_GetIntIdentifier() get args", error);
return error;
}
NPIdentifier ident = g_NPN_GetIntIdentifier(intid);
return rpc_method_send_reply(connection,
RPC_TYPE_NP_IDENTIFIER, &ident,
RPC_TYPE_INVALID);
}
// NPN_IdentifierIsString
static bool
g_NPN_IdentifierIsString(NPIdentifier ident)
{
D(bugiI("NPN_IdentifierIsString ident=%p\n", ident));
bool ret = mozilla_funcs.identifierisstring(ident);
D(bugiD("NPN_IdentifierIsString return: %s\n", ret ? "true" : "false"));
return ret;
}
static int handle_NPN_IdentifierIsString(rpc_connection_t *connection)
{
D(bug("handle_NPN_IdentifierIsString\n"));
NPIdentifier ident;
int error = rpc_method_get_args(connection,
RPC_TYPE_NP_IDENTIFIER, &ident,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_IdentifierIsString() get args", error);
return error;
}
bool ret = g_NPN_IdentifierIsString(ident);
return rpc_method_send_reply(connection,
RPC_TYPE_UINT32, ret,
RPC_TYPE_INVALID);
}
// NPN_UTF8FromIdentifier
static NPUTF8 *
g_NPN_UTF8FromIdentifier(NPIdentifier ident)
{
D(bugiI("NPN_UTF8FromIdentifier ident=%p\n", ident));
NPUTF8 *str = mozilla_funcs.utf8fromidentifier(ident);
D(bugiD("NPN_UTF8FromIdentifier return: '%s'\n", str));
return str;
}
static int handle_NPN_UTF8FromIdentifier(rpc_connection_t *connection)
{
D(bug("handle_NPN_UTF8FromIdentifier\n"));
NPIdentifier ident;
int error = rpc_method_get_args(connection,
RPC_TYPE_NP_IDENTIFIER, &ident,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_UTF8FromIdentifier() get args", error);
return error;
}
NPUTF8 *str = g_NPN_UTF8FromIdentifier(ident);
error = rpc_method_send_reply(connection,
RPC_TYPE_NP_UTF8, str,
RPC_TYPE_INVALID);
// the caller is responsible for deallocating the memory used by the string
NPN_MemFree(str);
return error;
}
// NPN_IntFromIdentifier
static int32_t
g_NPN_IntFromIdentifier(NPIdentifier ident)
{
D(bugiI("NPN_IntFromIdentifier ident=%p\n", ident));
int32_t ret = mozilla_funcs.intfromidentifier(ident);
D(bugiD("NPN_IntFromIdentifier return: %d\n", ret));
return ret;
}
static int handle_NPN_IntFromIdentifier(rpc_connection_t *connection)
{
D(bug("handle_NPN_IntFromIdentifier\n"));
NPIdentifier ident;
int error = rpc_method_get_args(connection,
RPC_TYPE_NP_IDENTIFIER, &ident,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_IntFromIdentifier() get args", error);
return error;
}
int32_t ret = g_NPN_IntFromIdentifier(ident);
return rpc_method_send_reply(connection,
RPC_TYPE_INT32, ret,
RPC_TYPE_INVALID);
}
// NPN_ReleaseVariantValue
static void
g_NPN_ReleaseVariantValue(NPVariant *variant)
{
D(bugiI("NPN_ReleaseVariantValue\n"));
NPN_ReleaseVariantValue(variant);
D(bugiD("NPN_ReleaseVariantValue done\n"));
}
// NPN_PluginThreadAsyncCall
static void
g_NPN_PluginThreadAsyncCall(NPP instance,
void (*func)(void *),
void *userData)
{
// No debug statements as the debug system is not thread-safe.
mozilla_funcs.pluginthreadasynccall(instance, func, userData);
}
// NPN_GetValueForURL
static NPError
g_NPN_GetValueForURL(NPP instance, NPNURLVariable variable,
const char *url, char **value,
uint32_t *len)
{
if (mozilla_funcs.getvalueforurl == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
D(bugiI("NPN_GetValueForURL instance=%p, variable=%d [%s], url=%s\n",
instance, variable, string_of_NPNURLVariable(variable), url));
NPError ret = mozilla_funcs.getvalueforurl(instance, variable, url, value, len);
D(bugiD("NPN_GetValueForURL return: %d [%s] len=%d\n",
ret, string_of_NPError(ret), *len));
return ret;
}
static int handle_NPN_GetValueForURL(rpc_connection_t *connection)
{
D(bug("handle_NPN_GetValueForURL\n"));
PluginInstance *plugin;
uint32_t variable;
char *url;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_UINT32, &variable,
RPC_TYPE_STRING, &url,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_GetValueForURL() get args", error);
return error;
}
char *value = NULL;
uint32_t len = 0;
int32_t ret = g_NPN_GetValueForURL(PLUGIN_INSTANCE_NPP(plugin), variable,
url, &value, &len);
if (url)
free(url);
error = rpc_method_send_reply(connection,
RPC_TYPE_INT32, ret,
RPC_TYPE_ARRAY, RPC_TYPE_CHAR, len, value,
RPC_TYPE_INVALID);
NPN_MemFree(value);
return error;
}
// NPN_SetValueForURL
static NPError
g_NPN_SetValueForURL(NPP instance, NPNURLVariable variable,
const char *url, const char *value,
uint32_t len)
{
if (mozilla_funcs.setvalueforurl == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
D(bugiI("NPN_SetValueForURL instance=%p, variable=%d [%s], url=%s, len=%d\n",
instance, variable, string_of_NPNURLVariable(variable), url, len));
NPError ret = mozilla_funcs.setvalueforurl(instance, variable, url, value, len);
D(bugiD("NPN_SetValueForURL return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
static int handle_NPN_SetValueForURL(rpc_connection_t *connection)
{
D(bug("handle_NPN_SetValueForURL\n"));
PluginInstance *plugin;
char *url;
uint32_t variable;
char *value;
uint32_t len;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_UINT32, &variable,
RPC_TYPE_STRING, &url,
RPC_TYPE_ARRAY, RPC_TYPE_CHAR, &len, &value,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_SetValueForURL() get args", error);
return error;
}
int32_t ret = g_NPN_SetValueForURL(PLUGIN_INSTANCE_NPP(plugin), variable,
url, value, len);
if (url)
free(url);
if (value)
free(value);
return rpc_method_send_reply(connection,
RPC_TYPE_INT32, ret,
RPC_TYPE_INVALID);
}
// NPN_GetAuthenticationInfo
static NPError
g_NPN_GetAuthenticationInfo(NPP instance, const char *protocol,
const char *host, int32_t port, const char *scheme,
const char *realm,
char **username, uint32_t *ulen,
char **password, uint32_t *plen)
{
if (mozilla_funcs.getauthenticationinfo == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
D(bugiI("NPN_GetAuthenticationInfo instance=%p, protocol=%s,"
" host=%s, port=%d, scheme=%s, realm=%s\n",
instance, protocol, host, port, scheme, realm));
NPError ret = mozilla_funcs.getauthenticationinfo(instance, protocol, host,
port, scheme, realm,
username, ulen,
password, plen);
D(bugiD("NPN_GetAuthenticationInfo return: %d [%s] ulen=%d, plen=%d\n",
ret, string_of_NPError(ret), *ulen, *plen));
return ret;
}
static int handle_NPN_GetAuthenticationInfo(rpc_connection_t *connection)
{
D(bug("handle_NPN_GetAuthenticationInfo\n"));
PluginInstance *plugin;
char *protocol, *host;
int32_t port;
char *scheme, *realm;
int error = rpc_method_get_args(connection,
RPC_TYPE_NPW_PLUGIN_INSTANCE, &plugin,
RPC_TYPE_STRING, &protocol,
RPC_TYPE_STRING, &host,
RPC_TYPE_INT32, &port,
RPC_TYPE_STRING, &scheme,
RPC_TYPE_STRING, &realm,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPN_GetAuthenticationInfo() get args", error);
return error;
}
char *username = NULL, *password = NULL;
uint32_t ulen = 0, plen = 0;
int32_t ret = g_NPN_GetAuthenticationInfo(PLUGIN_INSTANCE_NPP(plugin),
protocol, host, port, scheme, realm,
&username, &ulen, &password, &plen);
if (protocol)
free(protocol);
if (host)
free(host);
if (scheme)
free(scheme);
if (realm)
free(realm);
error = rpc_method_send_reply(connection,
RPC_TYPE_INT32, ret,
RPC_TYPE_ARRAY, RPC_TYPE_CHAR, ulen, username,
RPC_TYPE_ARRAY, RPC_TYPE_CHAR, plen, password,
RPC_TYPE_INVALID);
if (username)
NPN_MemFree(username);
if (password)
NPN_MemFree(password);
return error;
}
// NPN_ScheduleTimer
static uint32_t
g_NPN_ScheduleTimer(NPP instance, uint32_t interval, NPBool repeat,
void (*timerFunc)(NPP npp, uint32_t timerID))
{
if (mozilla_funcs.scheduletimer == NULL)
return 0;
D(bugiI("NPN_ScheduleTimer instance=%p, interval=%d, repeat=%d\n",
instance, interval, repeat));
uint32_t ret = mozilla_funcs.scheduletimer(instance, interval, repeat, timerFunc);
D(bugiD("NPN_ScheduleTimer return: %d\n", ret));
return ret;
}
// NPN_UnscheduleTimer
static void
g_NPN_UnscheduleTimer(NPP instance, uint32_t timerID)
{
if (mozilla_funcs.unscheduletimer == NULL)
return;
D(bugiI("NPN_UnscheduleTimer instance=%p, timerID=%d\n", instance, timerID));
mozilla_funcs.unscheduletimer(instance, timerID);
D(bugiD("NPN_UnscheduleTimer done\n"));
}
/* ====================================================================== */
/* === Plug-in side data === */
/* ====================================================================== */
// Creates a new instance of a plug-in
static NPError
invoke_NPP_New(PluginInstance *plugin, NPMIMEType mime_type,
uint16_t mode, int16_t argc, char *argn[], char *argv[],
NPSavedData *saved)
{
if (PLUGIN_DIRECT_EXEC)
return plugin_funcs.newp(mime_type, plugin->native_instance, mode, argc, argn, argv, saved);
npw_return_val_if_fail(rpc_method_invoke_possible(plugin->connection),
NPERR_GENERIC_ERROR);
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_NEW,
RPC_TYPE_UINT32, plugin->instance_id,
RPC_TYPE_STRING, mime_type,
RPC_TYPE_INT32, (int32_t)mode,
RPC_TYPE_ARRAY, RPC_TYPE_STRING, (uint32_t)argc, argn,
RPC_TYPE_ARRAY, RPC_TYPE_STRING, (uint32_t)argc, argv,
RPC_TYPE_NP_SAVED_DATA, saved,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_New() invoke", error);
return NPERR_GENERIC_ERROR;
}
int32_t ret;
error = rpc_method_wait_for_reply(plugin->connection,
RPC_TYPE_INT32, &ret,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_New() wait for reply", error);
return NPERR_GENERIC_ERROR;
}
return ret;
}
static NPError
g_NPP_New(NPMIMEType mime_type, NPP instance,
uint16_t mode, int16_t argc, char *argn[], char *argv[],
NPSavedData *saved)
{
if (instance == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
// Check if we need to restart the plug-in
NPError ret = plugin_start_if_needed();
if (ret != NPERR_NO_ERROR)
return ret;
PluginInstance *plugin = npw_plugin_instance_new(&PluginInstanceClass);
if (plugin == NULL)
return NPERR_OUT_OF_MEMORY_ERROR;
plugin->instance = instance;
plugin->instance_id = id_create(plugin);
plugin->connection = rpc_connection_ref(g_rpc_connection);
instance->pdata = plugin;
if (PLUGIN_DIRECT_EXEC) {
if ((plugin->native_instance = NPW_MemNew0(NPP_t, 1)) == NULL)
return NPERR_OUT_OF_MEMORY_ERROR;
plugin->native_instance->ndata = instance->ndata;
}
D(bugiI("NPP_New instance=%p\n", instance));
ret = invoke_NPP_New(plugin, mime_type, mode, argc, argn, argv, saved);
D(bugiD("NPP_New return: %d [%s]\n", ret, string_of_NPError(ret)));
if (saved) {
if (saved->buf)
free(saved->buf);
free(saved);
}
return ret;
}
// Deletes a specific instance of a plug-in
static NPError
invoke_NPP_Destroy(PluginInstance *plugin, NPSavedData **save)
{
if (PLUGIN_DIRECT_EXEC)
return plugin_funcs.destroy(plugin->native_instance, save);
npw_return_val_if_fail(rpc_method_invoke_possible(plugin->connection),
NPERR_GENERIC_ERROR);
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_DESTROY,
RPC_TYPE_NPW_PLUGIN_INSTANCE, plugin,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_Destroy() invoke", error);
return NPERR_GENERIC_ERROR;
}
int32_t ret;
NPSavedData *save_area = NULL;
error = rpc_method_wait_for_reply(plugin->connection,
RPC_TYPE_INT32, &ret,
RPC_TYPE_NP_SAVED_DATA, &save_area,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_Destroy() wait for reply", error);
return NPERR_GENERIC_ERROR;
}
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;
}
static NPError
g_NPP_Destroy(NPP instance, NPSavedData **save)
{
if (instance == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
D(bugiI("NPP_Destroy instance=%p\n", instance));
NPError ret = invoke_NPP_Destroy(plugin, save);
D(bugiD("NPP_Destroy return: %d [%s]\n", ret, string_of_NPError(ret)));
if (PLUGIN_DIRECT_EXEC) {
if (plugin->native_instance) {
NPW_MemFree(plugin->native_instance);
plugin->native_instance = NULL;
}
}
npw_plugin_instance_invalidate(plugin);
npw_plugin_instance_unref(plugin);
return ret;
}
// Tells the plug-in when a window is created, moved, sized, or destroyed
static NPError
invoke_NPP_SetWindow(PluginInstance *plugin, NPWindow *window)
{
if (PLUGIN_DIRECT_EXEC)
return plugin_funcs.setwindow(plugin->native_instance, window);
npw_return_val_if_fail(rpc_method_invoke_possible(plugin->connection),
NPERR_GENERIC_ERROR);
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_SET_WINDOW,
RPC_TYPE_NPW_PLUGIN_INSTANCE, plugin,
RPC_TYPE_NP_WINDOW, window,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_SetWindow() invoke", error);
return NPERR_GENERIC_ERROR;
}
int32_t ret;
error = rpc_method_wait_for_reply(plugin->connection,
RPC_TYPE_INT32, &ret,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_SetWindow() wait for reply", error);
return NPERR_GENERIC_ERROR;
}
return ret;
}
static NPError
g_NPP_SetWindow(NPP instance, NPWindow *window)
{
if (instance == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
D(bugiI("NPP_SetWindow instance=%p\n", instance));
NPError ret = invoke_NPP_SetWindow(plugin, window);
D(bugiD("NPP_SetWindow return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
// Allows the browser to query the plug-in for information
static NPError
invoke_NPP_GetValue(PluginInstance *plugin, NPPVariable variable, void *value)
{
if (PLUGIN_DIRECT_EXEC)
return plugin_funcs.getvalue(plugin->native_instance, variable, value);
npw_return_val_if_fail(rpc_method_invoke_possible(plugin->connection),
NPERR_GENERIC_ERROR);
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_GET_VALUE,
RPC_TYPE_NPW_PLUGIN_INSTANCE, plugin,
RPC_TYPE_INT32, variable,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_GetValue() invoke", error);
return NPERR_GENERIC_ERROR;
}
int32_t ret;
switch (rpc_type_of_NPPVariable(variable)) {
case RPC_TYPE_STRING:
{
char *str = NULL;
error = rpc_method_wait_for_reply(plugin->connection, RPC_TYPE_INT32, &ret, RPC_TYPE_STRING, &str, RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_GetValue() wait for reply", error);
ret = NPERR_GENERIC_ERROR;
}
D(bug("-> value: %s\n", str));
switch (variable) {
case NPPVformValue:
// this is a '\0'-terminated UTF-8 string data allocated by NPN_MemAlloc()
if (ret == NPERR_NO_ERROR) {
char *npn_str = NULL;
ret = NPW_ReallocData(str, strlen(str) + 1, (void**)&npn_str);
free(str);
str = npn_str;
}
break;
default:
// XXX memory leak (add to a deallocation pool?)
break;
}
*((char **)value) = str;
break;
}
case RPC_TYPE_INT32:
{
int32_t n = 0;
error = rpc_method_wait_for_reply(plugin->connection, RPC_TYPE_INT32, &ret, RPC_TYPE_INT32, &n, RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_GetValue() wait for reply", error);
ret = NPERR_GENERIC_ERROR;
}
D(bug("-> value: %d\n", n));
*((int *)value) = n;
break;
}
case RPC_TYPE_BOOLEAN:
{
uint32_t b = 0;
error = rpc_method_wait_for_reply(plugin->connection, RPC_TYPE_INT32, &ret, RPC_TYPE_BOOLEAN, &b, RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_GetValue() wait for reply", error);
ret = NPERR_GENERIC_ERROR;
}
D(bug("-> value: %s\n", b ? "true" : "false"));
*((NPBool *)value) = b ? TRUE : FALSE;
break;
}
case RPC_TYPE_NP_OBJECT:
{
NPObject *npobj = NULL;
error = rpc_method_wait_for_reply(plugin->connection,
RPC_TYPE_INT32, &ret,
RPC_TYPE_NP_OBJECT_PASS_REF, &npobj,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_GetValue() wait for reply", error);
ret = NPERR_GENERIC_ERROR;
}
D(bug("-> value: <object %p>\n", npobj));
*((NPObject **)value) = npobj;
// Caller is responsible for releasing reference.
break;
}
}
return ret;
}
static NPError
g_NPP_GetValue(NPP instance, NPPVariable variable, void *value)
{
// Firefox sometimes requests NP_GetValue values with NPP_GetValue
// for some reason. Notably it requests NPPVpluginDescriptionString
// with it when determining whether or not to apply a Flash-specific quirk.
//
// XXX: The more correct way to fix this may be to accept an NPP
// with NULL pdata and let the plugin decide whether to crash or
// not. Unfortunately, we get the connection out of the PluginInstance on
// the wrapper side, so it's best to avoid a NULL instance
// here. This way also avoids extra IPC.
if (variable == NPPVpluginNameString || variable == NPPVpluginDescriptionString) {
D(bugiI("NPP_GetValue instance=%p, variable=%d [%s]\n",
instance, variable, string_of_NPPVariable(variable)));
npw_printf("WARNING: browser requested NP_GetValue variable via NPP_GetValue.\n");
NPError ret = NP_GetValue(NULL, variable, value);
D(bugiD("NPP_GetValue return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
if (instance == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
switch (rpc_type_of_NPPVariable(variable)) {
case RPC_TYPE_STRING:
case RPC_TYPE_INT32:
case RPC_TYPE_BOOLEAN:
case RPC_TYPE_NP_OBJECT:
break;
default:
D(bug("WARNING: unhandled variable %d in NPP_GetValue()\n", variable));
return NPERR_INVALID_PARAM;
}
D(bugiI("NPP_GetValue instance=%p, variable=%d [%s]\n", instance, variable, string_of_NPPVariable(variable)));
NPError ret = invoke_NPP_GetValue(plugin, variable, value);
D(bugiD("NPP_GetValue return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
// Sets information about the plug-in
static NPError
invoke_NPP_SetValue(PluginInstance *plugin, NPNVariable variable, void *value)
{
NPW_UNIMPLEMENTED();
return NPERR_GENERIC_ERROR;
}
static NPError
g_NPP_SetValue(NPP instance, NPNVariable variable, void *value)
{
if (instance == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
D(bugiI("NPP_SetValue instance=%p, variable=%d [%s]\n", instance, variable, string_of_NPPVariable(variable)));
NPError ret = invoke_NPP_SetValue(plugin, variable, value);
D(bugiD("NPP_SetValue return: %d [%s]\n", ret, string_of_NPError(ret)));
return NPERR_GENERIC_ERROR;
}
// Notifies the instance of the completion of a URL request
static void
invoke_NPP_URLNotify(PluginInstance *plugin, const char *url, NPReason reason, void *notifyData)
{
if (PLUGIN_DIRECT_EXEC) {
plugin_funcs.urlnotify(plugin->native_instance, url, reason, notifyData);
return;
}
npw_return_if_fail(rpc_method_invoke_possible(plugin->connection));
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_URL_NOTIFY,
RPC_TYPE_NPW_PLUGIN_INSTANCE, plugin,
RPC_TYPE_STRING, url,
RPC_TYPE_INT32, reason,
RPC_TYPE_NP_NOTIFY_DATA, notifyData,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_URLNotify() invoke", error);
return;
}
error = rpc_method_wait_for_reply(plugin->connection, RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR)
npw_perror("NPP_URLNotify() wait for reply", error);
}
static void
g_NPP_URLNotify(NPP instance, const char *url, NPReason reason, void *notifyData)
{
if (instance == NULL)
return;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return;
D(bugiI("NPP_URLNotify instance=%p, url='%s', reason=%s, notifyData=%p\n", instance, url, string_of_NPReason(reason), notifyData));
invoke_NPP_URLNotify(plugin, url, reason, notifyData);
D(bugiD("NPP_URLNotify done\n"));
}
// Notifies a plug-in instance of a new data stream
static NPError
invoke_NPP_NewStream(PluginInstance *plugin, NPMIMEType type, NPStream *stream, NPBool seekable, uint16_t *stype)
{
if (PLUGIN_DIRECT_EXEC)
return plugin_funcs.newstream(plugin->native_instance, type, stream, seekable, stype);
npw_return_val_if_fail(rpc_method_invoke_possible(plugin->connection),
NPERR_GENERIC_ERROR);
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_NEW_STREAM,
RPC_TYPE_NPW_PLUGIN_INSTANCE, plugin,
RPC_TYPE_STRING, type,
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_BOOLEAN, seekable,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_NewStream() invoke", error);
return NPERR_GENERIC_ERROR;
}
int32_t ret;
uint32_t r_stype;
error = rpc_method_wait_for_reply(plugin->connection,
RPC_TYPE_INT32, &ret,
RPC_TYPE_UINT32, &r_stype,
RPC_TYPE_NP_NOTIFY_DATA, &stream->notifyData,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_NewStream() wait for reply", error);
return NPERR_GENERIC_ERROR;
}
*stype = r_stype;
return ret;
}
static NPError
g_NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream, NPBool seekable, uint16_t *stype)
{
if (instance == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
if (!PLUGIN_DIRECT_EXEC) {
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 = 0;
stream->pdata = stream_pdata;
}
D(bugiI("NPP_NewStream instance=%p\n", instance));
NPError ret = invoke_NPP_NewStream(plugin, type, stream, seekable, stype);
D(bugiD("NPP_NewStream return: %d [%s], stype=%s\n", ret, string_of_NPError(ret), string_of_NPStreamType(*stype)));
return ret;
}
// Tells the plug-in that a stream is about to be closed or destroyed
static NPError
invoke_NPP_DestroyStream(PluginInstance *plugin, NPStream *stream, NPReason reason)
{
if (PLUGIN_DIRECT_EXEC)
return plugin_funcs.destroystream(plugin->native_instance, stream, reason);
npw_return_val_if_fail(rpc_method_invoke_possible(plugin->connection),
NPERR_GENERIC_ERROR);
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_DESTROY_STREAM,
RPC_TYPE_NPW_PLUGIN_INSTANCE, plugin,
RPC_TYPE_NP_STREAM, stream,
RPC_TYPE_INT32, reason,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_DestroyStream() invoke", error);
return NPERR_GENERIC_ERROR;
}
int32_t ret;
error = rpc_method_wait_for_reply(plugin->connection,
RPC_TYPE_INT32, &ret,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_DestroyStream() wait for reply", error);
return NPERR_GENERIC_ERROR;
}
return ret;
}
static NPError
g_NPP_DestroyStream(NPP instance, NPStream *stream, NPReason reason)
{
if (instance == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
D(bugiI("NPP_DestroyStream instance=%p\n", instance));
NPError ret = invoke_NPP_DestroyStream(plugin, stream, reason);
D(bugiD("NPP_DestroyStream return: %d [%s]\n", ret, string_of_NPError(ret)));
if (!PLUGIN_DIRECT_EXEC) {
StreamInstance *stream_pdata = stream->pdata;
if (stream_pdata) {
id_remove(stream_pdata->stream_id);
free(stream->pdata);
stream->pdata = NULL;
}
}
return ret;
}
// Provides a local file name for the data from a stream
static void
invoke_NPP_StreamAsFile(PluginInstance *plugin, NPStream *stream, const char *fname)
{
if (PLUGIN_DIRECT_EXEC) {
plugin_funcs.asfile(plugin->native_instance, stream, fname);
return;
}
npw_return_if_fail(rpc_method_invoke_possible(plugin->connection));
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_STREAM_AS_FILE,
RPC_TYPE_NPW_PLUGIN_INSTANCE, plugin,
RPC_TYPE_NP_STREAM, stream,
RPC_TYPE_STRING, fname,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_StreamAsFile() invoke", error);
return;
}
error = rpc_method_wait_for_reply(plugin->connection, RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR)
npw_perror("NPP_StreamAsFile() wait for reply", error);
}
static void
g_NPP_StreamAsFile(NPP instance, NPStream *stream, const char *fname)
{
if (instance == NULL)
return;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return;
D(bugiI("NPP_StreamAsFile instance=%p\n", instance));
invoke_NPP_StreamAsFile(plugin, stream, fname);
D(bugiD("NPP_StreamAsFile done\n"));
}
// Determines maximum number of bytes that the plug-in can consume
static int32_t
invoke_NPP_WriteReady(PluginInstance *plugin, NPStream *stream)
{
if (PLUGIN_DIRECT_EXEC)
return plugin_funcs.writeready(plugin->native_instance, stream);
npw_return_val_if_fail(rpc_method_invoke_possible(plugin->connection),
NPERR_STREAM_BUFSIZ);
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_WRITE_READY,
RPC_TYPE_NPW_PLUGIN_INSTANCE, plugin,
RPC_TYPE_NP_STREAM, stream,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_WriteReady() invoke", error);
return NPERR_STREAM_BUFSIZ;
}
int32_t ret;
error = rpc_method_wait_for_reply(plugin->connection,
RPC_TYPE_INT32, &ret,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_WriteReady() wait for reply", error);
return NPERR_STREAM_BUFSIZ;
}
return ret;
}
static int32_t
g_NPP_WriteReady(NPP instance, NPStream *stream)
{
if (instance == NULL)
return 0;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return 0;
D(bugiI("NPP_WriteReady instance=%p\n", instance));
int32_t ret = invoke_NPP_WriteReady(plugin, stream);
D(bugiD("NPP_WriteReady return: %d\n", ret));
return ret;
}
// Delivers data to a plug-in instance
static int32_t
invoke_NPP_Write(PluginInstance *plugin, NPStream *stream, int32_t offset, int32_t len, void *buf)
{
if (PLUGIN_DIRECT_EXEC)
return plugin_funcs.write(plugin->native_instance, stream, offset, len, buf);
npw_return_val_if_fail(rpc_method_invoke_possible(plugin->connection), -1);
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_WRITE,
RPC_TYPE_NPW_PLUGIN_INSTANCE, plugin,
RPC_TYPE_NP_STREAM, stream,
RPC_TYPE_INT32, offset,
RPC_TYPE_ARRAY, RPC_TYPE_CHAR, len, buf,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_Write() invoke", error);
return -1;
}
int32_t ret;
error = rpc_method_wait_for_reply(plugin->connection,
RPC_TYPE_INT32, &ret,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_Write() wait for reply", error);
return -1;
}
return ret;
}
static int32_t
g_NPP_Write(NPP instance, NPStream *stream, int32_t offset, int32_t len, void *buf)
{
if (instance == NULL)
return -1;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return -1;
/* Don't try to propagate erroneous buffers.
*
* Actually, we can get to that case if NPP_WriteReady() returned -1
* or another negative value, in general. Some browsers (Konqueror,
* Google Chrome) send data through NPP_Write() anyway. Others
* (Firefox, WebKit) actually suspend the stream temporarily.
*
* Note that we could also return -1 here to destroy the stream
* right away. However, some plugins may want to handle that case
* themselves. i.e. it's not our role to idealize the plugin
* intents, we are just a "passthrough". On the other hand, the only
* useful way to handle that case is to range check the arguments
* and return -1. Is there a "real" plugin that wants to do more
* than that (except an explicit NPN_DestroyStream())?
*/
if (len <= 0)
buf = NULL;
D(bugiI("NPP_Write instance=%p\n", instance));
int32_t ret = invoke_NPP_Write(plugin, stream, offset, len, buf);
D(bugiD("NPP_Write return: %d\n", ret));
return ret;
}
// Requests a platform-specific print operation for an embedded or full-screen plug-in
static void invoke_NPP_Print(PluginInstance *plugin, NPPrint *PrintInfo)
{
if (PLUGIN_DIRECT_EXEC) {
plugin_funcs.print(plugin->native_instance, PrintInfo);
return;
}
NPPrintCallbackStruct *platformPrint;
switch (PrintInfo->mode) {
case NP_FULL:
platformPrint = PrintInfo->print.fullPrint.platformPrint;
break;
case NP_EMBED:
platformPrint = PrintInfo->print.embedPrint.platformPrint;
break;
default:
D(bug("WARNING: PrintInfo mode %d is not supported\n", PrintInfo->mode));
return;
}
uint32_t platform_print_id = 0;
if (platformPrint)
platform_print_id = id_create(platformPrint);
D(bug(" platformPrint=%p\n", platformPrint));
npw_return_if_fail(rpc_method_invoke_possible(plugin->connection));
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_PRINT,
RPC_TYPE_NPW_PLUGIN_INSTANCE, plugin,
RPC_TYPE_UINT32, platform_print_id,
RPC_TYPE_NP_PRINT, PrintInfo,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_Print() invoke", error);
return;
}
uint32_t pluginPrinted;
error = rpc_method_wait_for_reply(plugin->connection,
RPC_TYPE_BOOLEAN, &pluginPrinted,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_Print() wait for reply", error);
return;
}
// update browser-side NPPrint struct
if (PrintInfo->mode == NP_FULL)
PrintInfo->print.fullPrint.pluginPrinted = pluginPrinted;
if (platform_print_id)
id_remove(platform_print_id);
}
static void g_NPP_Print(NPP instance, NPPrint *PrintInfo)
{
if (instance == NULL)
return;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return;
if (PrintInfo == NULL)
return;
D(bugiI("NPP_Print instance=%p\n", instance));
invoke_NPP_Print(plugin, PrintInfo);
D(bugiD("NPP_Print done\n"));
}
// Delivers a platform-specific window event to the instance
static int16_t invoke_NPP_HandleEvent(PluginInstance *plugin, void *event)
{
if (PLUGIN_DIRECT_EXEC)
return plugin_funcs.event(plugin->native_instance, event);
npw_return_val_if_fail(rpc_method_invoke_possible(plugin->connection), false);
int error = rpc_method_invoke(plugin->connection,
RPC_METHOD_NPP_HANDLE_EVENT,
RPC_TYPE_NPW_PLUGIN_INSTANCE, plugin,
RPC_TYPE_NP_EVENT, event,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_HandleEvent() invoke", error);
return false;
}
int32_t ret;
error = rpc_method_wait_for_reply(plugin->connection,
RPC_TYPE_INT32, &ret,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_HandleEvent() wait for reply", error);
return false;
}
return ret;
}
static int16_t g_NPP_HandleEvent(NPP instance, void *event)
{
if (instance == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
PluginInstance *plugin = PLUGIN_INSTANCE(instance);
if (plugin == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
NPEvent *npevent = event;
if (npevent->type == GraphicsExpose) {
/* XXX: flush the X output buffer so that the call to
gdk_pixmap_foreign_new() in the viewer can work */
toolkit_flush(instance);
}
if (npevent->type == ButtonPress) {
// Release any implicit passive grabs we have so Flash can show a
// menu. This is only relevant if your browser does not do
// out-of-process plugins. Otherwise, we need the browser to do it
// instead.
pointer_ungrab(instance, npevent->xbutton.time);
toolkit_flush(instance);
}
D(bugiI("NPP_HandleEvent instance=%p\n", instance));
int16_t ret = invoke_NPP_HandleEvent(plugin, event);
D(bugiD("NPP_HandleEvent return: %d\n", ret));
return ret;
}
// Clears site-data stored by the plug-in
static NPError
invoke_NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge)
{
if (PLUGIN_DIRECT_EXEC)
return plugin_funcs.clearsitedata(site, flags, maxAge);
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection),
NPERR_GENERIC_ERROR);
int error = rpc_method_invoke(g_rpc_connection,
RPC_METHOD_NPP_CLEAR_SITE_DATA,
RPC_TYPE_STRING, site,
RPC_TYPE_UINT64, flags,
RPC_TYPE_UINT64, maxAge,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_ClearSiteData() 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("NPP_ClearSiteData() wait for reply", error);
return NPERR_GENERIC_ERROR;
}
return ret;
}
static NPError
g_NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge)
{
D(bugiI("NPP_ClearSiteData site=%s, flags=%" G_GUINT64_FORMAT
", maxAge=%" G_GUINT64_FORMAT "\n",
site ? site : "<null>", flags, maxAge));
NPError ret = invoke_NPP_ClearSiteData(site, flags, maxAge);
D(bugiD("NPP_ClearSiteData return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
// Get sites with data stored by the plug-in
static char **
invoke_NPP_GetSitesWithData(void)
{
if (PLUGIN_DIRECT_EXEC)
return plugin_funcs.getsiteswithdata();
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection), NULL);
int error = rpc_method_invoke(g_rpc_connection,
RPC_METHOD_NPP_GET_SITES_WITH_DATA,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_GetSitesWithData() invoke", error);
return NULL;
}
char **sites = NULL;
uint32_t siteCount = 0;
error = rpc_method_wait_for_reply(g_rpc_connection,
RPC_TYPE_ARRAY, RPC_TYPE_STRING,
&siteCount, &sites,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NPP_GetSitesWithData() wait for reply", error);
return NULL;
}
// Convert this to the format NPAPI wants... ugh.
char **sites_ret = NULL;
if (siteCount > 0) {
sites_ret = NPN_MemAlloc(sizeof(char*) * (siteCount+1));
if (sites_ret) {
for (int i = 0; i < siteCount; i++) {
// Ignore allocation failures here.
NPW_ReallocData(sites[i], strlen(sites[i]), (void**)&sites_ret[i]);
}
sites_ret[siteCount] = NULL;
}
}
// Delete the other copy.
if (sites) {
for (int i = 0; i < siteCount; i++) {
free(sites[i]);
}
free(sites);
}
return sites_ret;
}
static char **
g_NPP_GetSitesWithData(void)
{
D(bugiI("NPP_GetSitesWithData\n"));
char **ret = invoke_NPP_GetSitesWithData();
D(bugiD("NPP_GetSitesWithData return: %d sites\n",
ret ? g_strv_length(ret) : 0));
return ret;
}
// Allows the browser to query the plug-in for information
static NPError
g_NP_GetValue(void *future, NPPVariable variable, void *value)
{
if (g_plugin.initialized == 0)
plugin_init(0);
if (g_plugin.initialized <= 0)
return NPERR_GENERIC_ERROR;
if (PLUGIN_DIRECT_EXEC)
return g_plugin_NP_GetValue(future, variable, value);
char *str = NULL;
int ret = NPERR_GENERIC_ERROR;
switch (variable) {
case NPPVpluginNameString:
if (g_plugin.is_wrapper) {
str = "NPAPI Plugins Wrapper " NPW_VERSION;
ret = NPERR_NO_ERROR;
}
else if (g_plugin.name) {
str = g_plugin.name;
ret = NPERR_NO_ERROR;
}
break;
case NPPVpluginDescriptionString:
if (g_plugin.is_wrapper) {
str =
"<a href=\"http://gwenole.beauchesne.info/projects/nspluginwrapper/\">nspluginwrapper</a> "
" is a cross-platform NPAPI plugin viewer, in particular for linux/i386 plugins.<br>"
"This <b>beta</b> software is available under the terms of the GNU General Public License.<br>"
;
ret = NPERR_NO_ERROR;
}
else if (g_plugin.description) {
str = g_plugin.description;
ret = NPERR_NO_ERROR;
}
break;
default:
return NPERR_INVALID_PARAM;
}
*((char **)value) = str;
return ret;
}
NPError
NP_GetValue(void *future, NPPVariable variable, void *value)
{
D(bugiI("NP_GetValue variable=%d [%s]\n", variable, string_of_NPPVariable(variable)));
NPError ret = g_NP_GetValue(future, variable, value);
D(bugiD("NP_GetValue return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
// Allows the browser to query the plug-in supported formats
static const char *
g_NP_GetMIMEDescription(void)
{
if (g_plugin.initialized == 0)
plugin_init(0);
if (g_plugin.initialized <= 0)
return NULL;
if (PLUGIN_DIRECT_EXEC)
return g_plugin_NP_GetMIMEDescription();
if (g_plugin.is_wrapper)
return "unknown/mime-type:none:Do not open";
return g_plugin.formats;
}
const char *
NP_GetMIMEDescription(void)
{
D(bugiI("NP_GetMIMEDescription\n"));
const char *formats = g_NP_GetMIMEDescription();
D(bugiD("NP_GetMIMEDescription return: '%s'\n", formats));
return 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_t end;
uint64_t 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_t offset;
uint64_t length;
struct _LONG64_NPByteRange* next;
} LONG64_NPByteRange;
// NPSavedData
typedef struct _LONG64_NPSavedData {
int64_t 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_t 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_t 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_t x;
int64_t y;
uint64_t width;
uint64_t 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_t *stype)
{
if (instance == NULL)
return NPERR_INVALID_INSTANCE_ERROR;
// Detect broken 64-bit NPAPI
if (g_use_long64_thunks < 0) {
D(bug("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_t
g_LONG64_NPP_WriteReady(NPP instance, void *stream)
{
if (instance == NULL)
return 0;
if (stream == NULL)
return 0;
return (int64_t)(int32_t)g_NPP_WriteReady(instance, NP_STREAM32(stream));
}
// NPP_Write (LONG64)
static int64_t
g_LONG64_NPP_Write(NPP instance, void *stream, int64_t offset, int64_t len, void *buf)
{
if (instance == NULL)
return -1L;
if (stream == NULL)
return -1L;
return (int64_t)(int32_t)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
static NPError
invoke_NP_Initialize(uint32_t npapi_version,
uint32_t *plugin_version,
uint32_t **plugin_capabilities,
uint32_t *plugin_capabilities_len)
{
if (PLUGIN_DIRECT_EXEC) {
NPNetscapeFuncs wrapped_mozilla_funcs;
memset(&wrapped_mozilla_funcs, 0, sizeof(wrapped_mozilla_funcs));
wrapped_mozilla_funcs.size = sizeof(wrapped_mozilla_funcs);
wrapped_mozilla_funcs.version = npapi_version;
#define BROWSER_FUNC(func, member) \
if (mozilla_funcs.member != NULL) \
wrapped_mozilla_funcs.member = g_ ## func;
#include "browser-funcs.h"
#undef BROWSER_FUNC
NPError error = g_plugin_NP_Initialize(&wrapped_mozilla_funcs, &plugin_funcs);
*plugin_version = plugin_funcs.version;
return error;
}
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection),
NPERR_MODULE_LOAD_FAILED_ERROR);
// Allocate browser capabilities.
uint32_t browser_capabilities[] = {
#define BROWSER_FUNC(func, member) \
(mozilla_funcs.member != NULL) ? 1 : 0,
#include "browser-funcs.h"
#undef BROWSER_FUNC
};
int error = rpc_method_invoke(g_rpc_connection,
RPC_METHOD_NP_INITIALIZE,
RPC_TYPE_UINT32, npapi_version,
RPC_TYPE_ARRAY, RPC_TYPE_UINT32,
G_N_ELEMENTS(browser_capabilities),
browser_capabilities,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NP_Initialize() invoke", error);
return NPERR_MODULE_LOAD_FAILED_ERROR;
}
int32_t ret;
error = rpc_method_wait_for_reply(g_rpc_connection,
RPC_TYPE_INT32, &ret,
RPC_TYPE_UINT32, plugin_version,
RPC_TYPE_ARRAY, RPC_TYPE_UINT32,
plugin_capabilities_len,
plugin_capabilities,
RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NP_Initialize() wait for reply", error);
return NPERR_MODULE_LOAD_FAILED_ERROR;
}
return ret;
}
static NPError
g_NP_Initialize(uint32_t npapi_version,
uint32_t *plugin_version,
uint32_t **plugin_capabilities, uint32_t *plugin_capabilities_len)
{
D(bugiI("NP_Initialize\n"));
NPError ret = invoke_NP_Initialize(npapi_version, plugin_version,
plugin_capabilities, plugin_capabilities_len);
D(bugiD("NP_Initialize return: %d [%s], plugin_version=%d\n",
ret, string_of_NPError(ret), *plugin_version));
return ret;
}
NPError
NP_Initialize(NPNetscapeFuncs *moz_funcs, NPPluginFuncs *plugin_funcs)
{
D(bug("NP_Initialize\n"));
if (moz_funcs == NULL || plugin_funcs == NULL)
return NPERR_INVALID_FUNCTABLE_ERROR;
if ((moz_funcs->version >> 8) != NP_VERSION_MAJOR)
return NPERR_INCOMPATIBLE_VERSION_ERROR;
// for now, we only need fields up to including forceRedraw
if (moz_funcs->size < (offsetof(NPNetscapeFuncs, forceredraw) + sizeof(NPN_ForceRedrawProcPtr)))
return NPERR_INVALID_FUNCTABLE_ERROR;
if (g_plugin.initialized == 0)
plugin_init(0);
if (g_plugin.initialized <= 0)
return NPERR_GENERIC_ERROR;
if (g_plugin.is_wrapper)
return NPERR_NO_ERROR;
// copy mozilla_funcs table here as plugin_init() will need it
memcpy(&mozilla_funcs, moz_funcs, MIN(moz_funcs->size, sizeof(mozilla_funcs)));
static NPPluginFuncs full_plugin_funcs;
memset(&full_plugin_funcs, 0, sizeof(full_plugin_funcs));
full_plugin_funcs.size = sizeof(NPPluginFuncs);
full_plugin_funcs.version = NPW_NPAPI_VERSION;
#define PLUGIN_FUNC(func, member) \
full_plugin_funcs.member = g_ ## func;
#include "plugin-funcs.h"
#undef PLUGIN_FUNC
full_plugin_funcs.javaClass = NULL;
// override function table with an additional thunking layer for
// possibly broken 64-bit Konqueror versions (NPAPI 0.11)
if (sizeof(void *) == 8 && ! NPN_HAS_FEATURE(NPRUNTIME_SCRIPTING) && is_konqueror()) {
D(bug("Installing Konqueror workarounds\n"));
// We're doing sketchy pointer casts, so just cast the function pointers to
// make the compiler be quiet. We're doing this intentionally.
full_plugin_funcs.setwindow = (NPP_SetWindowProcPtr)g_LONG64_NPP_SetWindow;
full_plugin_funcs.newstream = (NPP_NewStreamProcPtr)g_LONG64_NPP_NewStream;
full_plugin_funcs.destroystream = (NPP_DestroyStreamProcPtr)g_LONG64_NPP_DestroyStream;
full_plugin_funcs.asfile = (NPP_StreamAsFileProcPtr)g_LONG64_NPP_StreamAsFile;
full_plugin_funcs.writeready = (NPP_WriteReadyProcPtr)g_LONG64_NPP_WriteReady;
full_plugin_funcs.write = (NPP_WriteProcPtr)g_LONG64_NPP_Write;
full_plugin_funcs.print = (NPP_PrintProcPtr)g_LONG64_NPP_Print;
full_plugin_funcs.newp = (NPP_NewProcPtr)g_LONG64_NPP_New;
full_plugin_funcs.destroy = (NPP_DestroyProcPtr)g_LONG64_NPP_Destroy;
}
// Initialize function tables
// XXX: remove the local copies from this file
// XXX: This doesn't get the adjust plugin version number below, but don't
// use it and someone may try to call something in npw-common.c in the
// meantime.
NPW_InitializeFuncs(moz_funcs, &full_plugin_funcs);
if (g_plugin.initialized == 0 || g_plugin.initialized == 1)
plugin_init(1);
if (g_plugin.initialized <= 0)
return NPERR_MODULE_LOAD_FAILED_ERROR;
if (!id_init())
return NPERR_MODULE_LOAD_FAILED_ERROR;
if (!npobject_bridge_new())
return NPERR_MODULE_LOAD_FAILED_ERROR;
// pass down common NPAPI version supported by both the underlying
// browser and the thunking capabilities of nspluginwrapper
D(bug("Thunking layer supports NPAPI %d\n", NPW_NPAPI_VERSION));
npapi_version = MIN(moz_funcs->version, NPW_NPAPI_VERSION);
D(bug("Browser supports NPAPI %d, advertising version %d to plugin\n",
moz_funcs->version, npapi_version));
uint32_t plugin_version = 0;
uint32_t *plugin_capabilities = NULL;
uint32_t plugin_capabilities_len = 0;
NPError error = g_NP_Initialize(npapi_version, &plugin_version,
&plugin_capabilities, &plugin_capabilities_len);
// Likewise, advertise the common NPAPI version between the plugin and our
// thunking capabilities.
full_plugin_funcs.version = MIN(plugin_version, NPW_NPAPI_VERSION);
D(bug("Plugin supports NPAPI %d, advertising version %d to browser\n",
plugin_version, full_plugin_funcs.version));
if (plugin_capabilities) {
// Don't advertise any functions the plugin doesn't support.
int num = 0;
#define PLUGIN_FUNC(func, member) \
if (num >= plugin_capabilities_len) { \
D(bug("ERROR: provided array was too small.\n")); \
goto plugin_func_done; \
} \
if (!plugin_capabilities[num]) { \
D(bug("plugin does not support " #func "\n")); \
full_plugin_funcs.member = NULL; \
} \
num++;
#include "plugin-funcs.h"
#undef PLUGIN_FUNC
plugin_func_done:
free(plugin_capabilities);
}
// Copy only the portion of full_plugin_funcs that the browser
// understands.
uint16_t plugin_funcs_size = MIN(plugin_funcs->size, sizeof(full_plugin_funcs));
memcpy(plugin_funcs, &full_plugin_funcs, plugin_funcs_size);
plugin_funcs->size = plugin_funcs_size;
return error;
}
// Provides global deinitialization for a plug-in
static NPError
invoke_NP_Shutdown(void)
{
if (PLUGIN_DIRECT_EXEC)
return g_plugin_NP_Shutdown();
if (g_rpc_connection == NULL)
return NPERR_NO_ERROR;
npw_return_val_if_fail(rpc_method_invoke_possible(g_rpc_connection),
NPERR_GENERIC_ERROR);
int error = rpc_method_invoke(g_rpc_connection, RPC_METHOD_NP_SHUTDOWN, RPC_TYPE_INVALID);
if (error != RPC_ERROR_NO_ERROR) {
npw_perror("NP_Shutdown() 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("NP_Shutdown() wait for reply", error);
return NPERR_GENERIC_ERROR;
}
return ret;
}
static NPError
g_NP_Shutdown(void)
{
D(bugiI("NP_Shutdown\n"));
NPError ret = invoke_NP_Shutdown();
D(bugiD("NP_Shutdown return: %d [%s]\n", ret, string_of_NPError(ret)));
if (!g_plugin.is_wrapper)
plugin_exit();
return ret;
}
NPError
NP_Shutdown(void)
{
NPError ret = g_NP_Shutdown();
npobject_bridge_destroy();
id_kill();
return ret;
}
// Initialize wrapper plugin and execute viewer
static void plugin_init(int is_NP_Initialize)
{
if (g_plugin.initialized < 0)
return;
g_plugin.initialized = -1;
D(bug("plugin_init for %s\n", plugin_path));
if (strcmp(plugin_path, NPW_DEFAULT_PLUGIN_PATH) == 0) {
g_plugin.is_wrapper = 1;
g_plugin.initialized = 1 + is_NP_Initialize;
return;
}
if (PLUGIN_DIRECT_EXEC){
g_plugin.initialized = 1;
return;
}
static const char *plugin_file_name = NULL;
if (plugin_file_name == NULL) {
const char *p;
for (p = &plugin_path[strlen(plugin_path) - 1]; p > plugin_path; p--) {
if (*p == '/') {
plugin_file_name = p + 1;
break;
}
}
if (plugin_file_name == NULL)
return;
}
static int init_count = 0;
++init_count;
// Cache MIME info and plugin name/description
if (g_plugin.name == NULL &&
g_plugin.description == NULL &&
g_plugin.formats == NULL) {
char *command = g_strdup_printf("%s --info --plugin %s",
plugin_viewer_path, plugin_path);
FILE *viewer_fp = popen(command, "r");
g_free(command);
if (viewer_fp == NULL)
return;
char line[256];
while (fgets(line, sizeof(line), viewer_fp)) {
// Read line
int len = strlen(line);
if (len == 0)
continue;
if (line[len - 1] != '\n') {
// Consume the whole line, we can't see our tags here
while (fgets(line, sizeof(line), viewer_fp)) {
len = strlen(line);
if (len > 0 && line[len - 1] == '\n')
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)) {
char **ptag = NULL;
if (strcmp(tag, "PLUGIN_NAME") == 0)
ptag = &g_plugin.name;
else if (strcmp(tag, "PLUGIN_DESC") == 0)
ptag = &g_plugin.description;
else if (strcmp(tag, "PLUGIN_MIME") == 0)
ptag = &g_plugin.formats;
if (ptag)
*ptag = str;
}
}
}
pclose(viewer_fp);
g_plugin.initialized = 1;
}
if (!is_NP_Initialize)
return;
char *connection_path =
g_strdup_printf("%s/%s/%d-%d/%ld",
NPW_CONNECTION_PATH, plugin_file_name,
getpid(), init_count, random());
// Start plug-in viewer
if ((g_plugin.viewer_pid = fork()) == 0) {
char *argv[8];
int argc = 0;
argv[argc++] = NPW_VIEWER;
argv[argc++] = "--plugin";
argv[argc++] = (char *)plugin_path;
argv[argc++] = "--connection";
argv[argc++] = connection_path;
argv[argc] = NULL;
npw_close_all_open_files();
execv(plugin_viewer_path, argv);
npw_printf("ERROR: failed to execute NSPlugin viewer\n");
_Exit(255);
}
// Initialize browser-side RPC communication channel
if ((g_rpc_connection = rpc_init_client(connection_path)) == NULL) {
npw_printf("ERROR: failed to initialize plugin-side RPC client connection\n");
g_free(connection_path);
return;
}
g_free(connection_path);
if (rpc_add_np_marshalers(g_rpc_connection) < 0) {
npw_printf("ERROR: failed to initialize browser-side marshalers\n");
return;
}
static const rpc_method_descriptor_t vtable[] = {
{ RPC_METHOD_NPN_USER_AGENT, handle_NPN_UserAgent },
{ RPC_METHOD_NPN_GET_VALUE, handle_NPN_GetValue },
{ RPC_METHOD_NPN_SET_VALUE, handle_NPN_SetValue },
{ RPC_METHOD_NPN_GET_URL, handle_NPN_GetURL },
{ RPC_METHOD_NPN_GET_URL_NOTIFY, handle_NPN_GetURLNotify },
{ RPC_METHOD_NPN_POST_URL, handle_NPN_PostURL },
{ RPC_METHOD_NPN_POST_URL_NOTIFY, handle_NPN_PostURLNotify },
{ 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_INVALIDATE_RECT, handle_NPN_InvalidateRect },
{ RPC_METHOD_NPN_GET_VALUE_FOR_URL, handle_NPN_GetValueForURL },
{ RPC_METHOD_NPN_SET_VALUE_FOR_URL, handle_NPN_SetValueForURL },
{ RPC_METHOD_NPN_GET_AUTHENTICATION_INFO, handle_NPN_GetAuthenticationInfo },
{ RPC_METHOD_NPN_INVOKE, handle_NPN_Invoke },
{ RPC_METHOD_NPN_INVOKE_DEFAULT, handle_NPN_InvokeDefault },
{ RPC_METHOD_NPN_EVALUATE, handle_NPN_Evaluate },
{ RPC_METHOD_NPN_GET_PROPERTY, handle_NPN_GetProperty },
{ RPC_METHOD_NPN_SET_PROPERTY, handle_NPN_SetProperty },
{ RPC_METHOD_NPN_REMOVE_PROPERTY, handle_NPN_RemoveProperty },
{ RPC_METHOD_NPN_HAS_PROPERTY, handle_NPN_HasProperty },
{ RPC_METHOD_NPN_HAS_METHOD, handle_NPN_HasMethod },
{ RPC_METHOD_NPN_ENUMERATE, handle_NPN_Enumerate },
{ RPC_METHOD_NPN_CONSTRUCT, handle_NPN_Construct },
{ RPC_METHOD_NPN_SET_EXCEPTION, handle_NPN_SetException },
{ RPC_METHOD_NPN_GET_STRING_IDENTIFIER, handle_NPN_GetStringIdentifier },
{ RPC_METHOD_NPN_GET_STRING_IDENTIFIERS, handle_NPN_GetStringIdentifiers },
{ RPC_METHOD_NPN_GET_INT_IDENTIFIER, handle_NPN_GetIntIdentifier },
{ RPC_METHOD_NPN_IDENTIFIER_IS_STRING, handle_NPN_IdentifierIsString },
{ RPC_METHOD_NPN_UTF8_FROM_IDENTIFIER, handle_NPN_UTF8FromIdentifier },
{ RPC_METHOD_NPN_INT_FROM_IDENTIFIER, handle_NPN_IntFromIdentifier },
};
if (rpc_connection_add_method_descriptors(g_rpc_connection, vtable, sizeof(vtable) / sizeof(vtable[0])) < 0) {
npw_printf("ERROR: failed to setup NPN method callbacks\n");
return;
}
if (npclass_add_method_descriptors(g_rpc_connection) < 0) {
npw_printf("ERROR: failed to setup NPClass method callbacks\n");
return;
}
// Retrieve toolkit information
if (mozilla_funcs.getvalue == NULL)
return;
NPNToolkitType toolkit = 0;
mozilla_funcs.getvalue(NULL, NPNVToolkit, (void *)&toolkit);
// Initialize RPC events listener, try to attach it to the main event loop
if (toolkit == NPNVGtk12 || toolkit == NPNVGtk2
|| toolkit == 0xFEEDABEE) { // GLib
// We use the glib event loop in Konqueror (0xFEEDABEE) because
// its Xt event loop bridge is completely broken and non-functional.
D(bug(" trying to attach RPC listener to main GLib event loop\n"));
g_rpc_source = rpc_event_source_new(g_rpc_connection);
g_source_set_priority(g_rpc_source, G_PRIORITY_LOW);
g_source_attach(g_rpc_source, NULL);
g_rpc_sync_source = rpc_sync_source_new(g_rpc_connection);
g_source_set_priority(g_rpc_sync_source, G_PRIORITY_HIGH);
g_source_attach(g_rpc_sync_source, NULL);
} else { // X11
D(bug(" trying to attach RPC listener to main X11 event loop\n"));
XtAppContext x_app_context = NULL;
int error = mozilla_funcs.getvalue(NULL, NPNVxtAppContext, (void *)&x_app_context);
if (error != NPERR_NO_ERROR || x_app_context == NULL) {
D(bug(" ... getting raw application context through X display\n"));
Display *x_display = NULL;
error = mozilla_funcs.getvalue(NULL, NPNVxDisplay, (void *)&x_display);
if (error == NPERR_NO_ERROR && x_display)
x_app_context = XtDisplayToApplicationContext(x_display);
}
if (x_app_context) {
xt_rpc_source_id = XtAppAddInput(x_app_context,
rpc_socket(g_rpc_connection),
(XtPointer)XtInputReadMask,
(XtInputCallbackProc)rpc_dispatch, g_rpc_connection);
xt_rpc_sync_id = XtAppAddBlockHook(x_app_context,
(XtBlockHookProc)rpc_dispatch_pending_sync,
g_rpc_connection);
}
}
if (g_rpc_source == NULL && (xt_rpc_source_id == 0 || xt_rpc_sync_id == 0)) {
npw_printf("ERROR: failed to initialize brower-side RPC events listener\n");
return;
}
// Set error handler - stop plugin if there's a connection error
rpc_connection_set_error_callback(g_rpc_connection, plugin_kill_cb, NULL);
g_plugin.initialized = 1 + is_NP_Initialize;
D(bug("--- INIT ---\n"));
}
// Kill NSPlugin Viewer process
static void plugin_exit(void)
{
D(bug("plugin_exit\n"));
if (xt_rpc_source_id) {
XtRemoveInput(xt_rpc_source_id);
xt_rpc_source_id = 0;
}
if (xt_rpc_sync_id) {
XtRemoveBlockHook(xt_rpc_sync_id);
xt_rpc_sync_id = 0;
}
if (g_rpc_source) {
g_source_destroy(g_rpc_source);
g_rpc_source = NULL;
}
if (g_rpc_sync_source) {
g_source_destroy(g_rpc_sync_source);
g_rpc_sync_source = NULL;
}
if (g_rpc_connection) {
rpc_connection_unref(g_rpc_connection);
g_rpc_connection = NULL;
}
if (g_plugin.viewer_pid != -1) {
// let it shutdown gracefully, then kill it gently to no mercy
const int WAITPID_DELAY_TO_SIGTERM = 3;
const int WAITPID_DELAY_TO_SIGKILL = 3;
int counter = 0;
while (waitpid(g_plugin.viewer_pid, NULL, WNOHANG) == 0) {
if (++counter > WAITPID_DELAY_TO_SIGTERM) {
kill(g_plugin.viewer_pid, SIGTERM);
counter = 0;
while (waitpid(g_plugin.viewer_pid, NULL, WNOHANG) == 0) {
if (++counter > WAITPID_DELAY_TO_SIGKILL) {
kill(g_plugin.viewer_pid, SIGKILL);
break;
}
sleep(1);
}
break;
}
sleep(1);
}
g_plugin.viewer_pid = -1;
}
g_plugin.initialized = 0;
}
static void __attribute__((destructor)) plugin_exit_sentinel(void)
{
plugin_exit();
if (plugin_handle) {
dlclose(plugin_handle);
plugin_handle = NULL;
}
if (g_plugin.formats) {
free(g_plugin.formats);
g_plugin.formats = NULL;
}
if (g_plugin.name) {
free(g_plugin.name);
g_plugin.name = NULL;
}
if (g_plugin.description) {
free(g_plugin.description);
g_plugin.description = NULL;
}
}
static void plugin_kill(void)
{
if (g_plugin.is_wrapper)
return;
// Kill viewer and plugin
plugin_exit();
// Clear-up
g_plugin.initialized = 0;
g_plugin.viewer_pid = -1;
g_plugin.is_wrapper = 0;
npruntime_deactivate();
// Set the kill flag
plugin_killed = 1;
}
static void plugin_kill_cb(rpc_connection_t *connection, void *user_data)
{
D(bug("plugin_kill, connection %p\n", connection));
// Don't kill the plugin again through another instance
rpc_connection_set_error_callback(connection, NULL, NULL);
plugin_kill();
}
static NPError plugin_start(void)
{
D(bug("plugin_start\n"));
if (!plugin_killed) {
// Plugin is still active, terminate it before the restart
D(bug("plugin_start: plugin_killed == 0!\n"));
plugin_kill();
}
plugin_killed = 0;
// And start it again
plugin_init(1);
if (g_plugin.initialized <= 0)
return NPERR_MODULE_LOAD_FAILED_ERROR;
uint32_t plugin_version;
uint32_t *plugin_capabilities = NULL;
uint32_t plugin_capabilities_len;
NPError ret = g_NP_Initialize(npapi_version, &plugin_version,
&plugin_capabilities, &plugin_capabilities_len);
// Assume capabilities unchanged.
if (plugin_capabilities)
free(plugin_capabilities);
return ret;
}
static NPError plugin_start_if_needed(void)
{
if (PLUGIN_DIRECT_EXEC)
return NPERR_NO_ERROR;
if (rpc_status(g_rpc_connection) != RPC_STATUS_ACTIVE) {
static time_t last_restart = 0;
time_t now = time(NULL);
if (now - last_restart < MIN_RESTART_INTERVAL)
return NPERR_GENERIC_ERROR;
last_restart = now;
D(bug("Restart plugins viewer\n"));
NPError ret = plugin_start();
D(bug(" return: %d [%s]\n", ret, string_of_NPError(ret)));
return ret;
}
return NPERR_NO_ERROR;
}