4163 lines
116 KiB
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, ¬ifyData,
|
|
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, ¬ifyData,
|
|
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;
|
|
}
|