Mercurial > hg > release > icedtea6-1.6
changeset 1634:dd1ce0a6da73
Commit changes to the new np plugin. These changes do a lot of things to list.
But most notably, they:
- Lay down the scriptability framework.
- Implement the new MessageBus architecture that is independent of how messages
are passed.
- Implement GetWindow and GetMember (partially) as proof of concept for
message passing, threading, unicode translation, etc.
ChangeLog:
* Makefile.am: Update makefile to pick up plugin C++ files from new
location.
* plugin/icedtea/sun/applet/PluginMessageConsumer.java : Minor typo fix.
* plugin/icedteanp/IcedTeaJavaRequestProcessor.cc: New file. Processes
requests from JS/C++ side to JavaSide.
* plugin/icedteanp/IcedTeaJavaRequestProcessor.h: New file. Header for
IcedTeaJavaRequestProcessor.cc.
* plugin/icedteanp/IcedTeaNPPlugin.cc: Modified to work with the new
MessageBus archtecture for the new plugin. Also, moved from top level
directory.
* plugin/icedteanp/IcedTeaNPPlugin.h: New file. Header for
IcedTeaNPPlugin.cc.
* plugin/icedteanp/IcedTeaPluginRequestProcessor.cc: New file. Processes
plugin data requests from Java side.
* plugin/icedteanp/IcedTeaPluginRequestProcessor.h: new file. Header for
IcedTeaPluginRequestProcessor.cc.
* plugin/icedteanp/IcedTeaPluginUtils.cc: New file. Utility functions for
the rest of the plugin code.
* plugin/icedteanp/IcedTeaPluginUtils.h: New file. Header for
IcedTeaPluginUtils.cc.
* plugin/icedteanp/IcedTeaScriptablePluginObject.cc: New file. Scriptable
object that extends NPObject and implements hooks from NPClass.
* plugin/icedteanp/IcedTeaScriptablePluginObject.h: New file. Header for
IcedTeaScriptablePluginObject.h
* plugin/icedtea/sun/applet/PluginMessageConsumer.java: Sync with current
plugin.
* plugin/icedteanp/sun/applet/PluginAppletSecurityContext.java: Same.
* plugin/icedteanp/sun/applet/PluginAppletViewer.java: Same.
* plugin/icedteanp/sun/applet/PluginCallRequestFactory.java: Same.
* plugin/icedteanp/sun/applet/PluginCookieInfoRequest.java: Same
* plugin/icedteanp/sun/applet/PluginCookieStore.java: Same.
* plugin/icedteanp/sun/applet/PluginMain.java: Same.
* plugin/icedteanp/sun/applet/PluginMessageConsumer.java: Same.
* rt/net/sourceforge/jnlp/tools/JarSigner.java: Use JarFile instead of
JarInputstream when verifying jars.
line wrap: on
line diff
--- a/ChangeLog Fri Jul 10 13:12:21 2009 -0400 +++ b/ChangeLog Fri Jul 10 19:02:10 2009 -0400 @@ -1,3 +1,41 @@ +2009-07-10 Deepak Bhole <dbhole@redhat.com> + + * Makefile.am: Update makefile to pick up plugin C++ files from new + location. + * plugin/icedtea/sun/applet/PluginMessageConsumer.java : Minor typo fix. + * plugin/icedteanp/IcedTeaJavaRequestProcessor.cc: New file. Processes + requests from JS/C++ side to JavaSide. + * plugin/icedteanp/IcedTeaJavaRequestProcessor.h: New file. Header for + IcedTeaJavaRequestProcessor.cc. + * plugin/icedteanp/IcedTeaNPPlugin.cc: Modified to work with the new + MessageBus archtecture for the new plugin. Also, moved from top level + directory. + * plugin/icedteanp/IcedTeaNPPlugin.h: New file. Header for + IcedTeaNPPlugin.cc. + * plugin/icedteanp/IcedTeaPluginRequestProcessor.cc: New file. Processes + plugin data requests from Java side. + * plugin/icedteanp/IcedTeaPluginRequestProcessor.h: new file. Header for + IcedTeaPluginRequestProcessor.cc. + * plugin/icedteanp/IcedTeaPluginUtils.cc: New file. Utility functions for + the rest of the plugin code. + * plugin/icedteanp/IcedTeaPluginUtils.h: New file. Header for + IcedTeaPluginUtils.cc. + * plugin/icedteanp/IcedTeaScriptablePluginObject.cc: New file. Scriptable + object that extends NPObject and implements hooks from NPClass. + * plugin/icedteanp/IcedTeaScriptablePluginObject.h: New file. Header for + IcedTeaScriptablePluginObject.h + * plugin/icedtea/sun/applet/PluginMessageConsumer.java: Sync with current + plugin. + * plugin/icedteanp/sun/applet/PluginAppletSecurityContext.java: Same. + * plugin/icedteanp/sun/applet/PluginAppletViewer.java: Same. + * plugin/icedteanp/sun/applet/PluginCallRequestFactory.java: Same. + * plugin/icedteanp/sun/applet/PluginCookieInfoRequest.java: Same + * plugin/icedteanp/sun/applet/PluginCookieStore.java: Same. + * plugin/icedteanp/sun/applet/PluginMain.java: Same. + * plugin/icedteanp/sun/applet/PluginMessageConsumer.java: Same. + * rt/net/sourceforge/jnlp/tools/JarSigner.java: Use JarFile instead of + JarInputstream when verifying jars. + 2009-07-10 Lillian Angel <langel@redhat.com> * patches/icedtea-graphics.patch: Updated patch to fix Sun bugs 6491856 and
--- a/IcedTeaNPPlugin.cc Fri Jul 10 13:12:21 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2006 +0,0 @@ -/* gcjwebplugin.cc -- web browser plugin to execute Java applets - Copyright (C) 2003, 2004, 2006, 2007 Free Software Foundation, Inc. - -This file is part of GNU Classpath. - -GNU Classpath 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, or (at your option) -any later version. - -GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301 USA. - -Linking this library statically or dynamically with other modules is -making a combined work based on this library. Thus, the terms and -conditions of the GNU General Public License cover the whole -combination. - -As a special exception, the copyright holders of this library give you -permission to link this library with independent modules to produce an -executable, regardless of the license terms of these independent -modules, and to copy and distribute the resulting executable under -terms of your choice, provided that you also meet, for each linked -independent module, the terms and conditions of the license of that -module. An independent module is a module which is not derived from -or based on this library. If you modify this library, you may extend -this exception to your version of the library, but you are not -obligated to do so. If you do not wish to do so, delete this -exception statement from your version. */ - -// System includes. -#include <dlfcn.h> -#include <errno.h> -#include <libgen.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -// Netscape plugin API includes. -#include <npapi.h> -#include <npupp.h> - -// GLib includes. -#include <glib.h> -#include <glib/gstdio.h> - -// GTK includes. -#include <gtk/gtk.h> - -// Documentbase retrieval includes. -#include <nsIPluginInstance.h> -#include <nsIPluginInstancePeer.h> -#include <nsIPluginTagInfo2.h> - -// API's into Mozilla -#include <nsCOMPtr.h> -#include <nsICookieService.h> -#include <nsIDNSRecord.h> -#include <nsIDNSService.h> -#include <nsINetUtil.h> -#include <nsIProxyInfo.h> -#include <nsIProtocolProxyService.h> -#include <nsIScriptSecurityManager.h> -#include <nsIIOService.h> -#include <nsIURI.h> -#include <nsNetCID.h> -#include <nsStringAPI.h> -#include <nsServiceManagerUtils.h> - - -// Debugging macros. -#define PLUGIN_DEBUG(message) \ - g_print ("GCJ PLUGIN: thread %p: %s\n", g_thread_self (), message) - -#define PLUGIN_DEBUG_TWO(first, second) \ - g_print ("GCJ PLUGIN: thread %p: %s %s\n", g_thread_self (), \ - first, second) - -// Error reporting macros. -#define PLUGIN_ERROR(message) \ - g_printerr ("%s:%d: thread %p: Error: %s\n", __FILE__, __LINE__, \ - g_thread_self (), message) - -#define PLUGIN_ERROR_TWO(first, second) \ - g_printerr ("%s:%d: thread %p: Error: %s: %s\n", __FILE__, __LINE__, \ - g_thread_self (), first, second) - -#define PLUGIN_ERROR_THREE(first, second, third) \ - g_printerr ("%s:%d: thread %p: Error: %s: %s: %s\n", __FILE__, \ - __LINE__, g_thread_self (), first, second, third) - -// Plugin information passed to about:plugins. -#define PLUGIN_NAME "IcedTea NPR Web Browser Plugin (using IcedTea)" -#define PLUGIN_DESC "The " PLUGIN_NAME PLUGIN_VERSION " executes Java applets." -#define PLUGIN_MIME_DESC \ - "application/x-java-vm:class,jar:IcedTea;" \ - "application/x-java-applet:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.1:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.1.1:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.1.2:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.1.3:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.2:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.2.1:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.2.2:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.3:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.3.1:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.4:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.4.1:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.4.2:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.5:class,jar:IcedTea;" \ - "application/x-java-applet;version=1.6:class,jar:IcedTea;" \ - "application/x-java-applet;jpi-version=1.6.0_00:class,jar:IcedTea;" \ - "application/x-java-bean:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.1:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.1.1:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.1.2:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.1.3:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.2:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.2.1:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.2.2:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.3:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.3.1:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.4:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.4.1:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.4.2:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.5:class,jar:IcedTea;" \ - "application/x-java-bean;version=1.6:class,jar:IcedTea;" \ - "application/x-java-bean;jpi-version=1.6.0_00:class,jar:IcedTea;" -#define PLUGIN_URL NS_INLINE_PLUGIN_CONTRACTID_PREFIX NS_JVM_MIME_TYPE -#define PLUGIN_MIME_TYPE "application/x-java-vm" -#define PLUGIN_FILE_EXTS "class,jar,zip" -#define PLUGIN_MIME_COUNT 1 - -#define FAILURE_MESSAGE "gcjwebplugin error: Failed to run %s." \ - " For more detail rerun \"firefox -g\" in a terminal window." - -static int plugin_debug = 1; - -#define PLUGIN_DEBUG_0ARG(str) \ - do \ - { \ - if (plugin_debug) \ - { \ - fprintf(stderr, "GCJ PLUGIN: thread %p: ", g_thread_self ()); \ - fprintf(stderr, str); \ - } \ - } while (0) - -#define PLUGIN_DEBUG_1ARG(str, arg1) \ - do \ - { \ - if (plugin_debug) \ - { \ - fprintf(stderr, "GCJ PLUGIN: thread %p: ", g_thread_self ()); \ - fprintf(stderr, str, arg1); \ - } \ - } while (0) - -#define PLUGIN_DEBUG_2ARG(str, arg1, arg2) \ - do \ - { \ - if (plugin_debug) \ - { \ - fprintf(stderr, "GCJ PLUGIN: thread %p: ", g_thread_self ()); \ - fprintf(stderr, str, arg1, arg2); \ - } \ - } while (0) - -#define PLUGIN_DEBUG_3ARG(str, arg1, arg2, arg3) \ - do \ - { \ - if (plugin_debug) \ - { \ - fprintf(stderr, "GCJ PLUGIN: thread %p: ", g_thread_self ()); \ - fprintf(stderr, str, arg1, arg2, arg3); \ - } \ - } while (0) - -#define PLUGIN_DEBUG_4ARG(str, arg1, arg2, arg3, arg4) \ - do \ - { \ - if (plugin_debug) \ - { \ - fprintf(stderr, "GCJ PLUGIN: thread %p: ", g_thread_self ()); \ - fprintf(stderr, str, arg1, arg2, arg3, arg4); \ - } \ - } while (0) - -// Documentbase retrieval required definition. -static NS_DEFINE_IID (kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID); - -// Browser function table. -static NPNetscapeFuncs browserFunctions; - -// Data directory for plugin. -static gchar* data_directory = NULL; - -// Fully-qualified appletviewer executable. -static gchar* appletviewer_executable = NULL; - -// Applet viewer input channel (needs to be static because it is used in plugin_in_pipe_callback) -static GIOChannel* in_from_appletviewer = NULL; - -// Applet viewer input pipe name. -gchar* in_pipe_name; - -// Applet viewer input watch source. -gint in_watch_source; - -// Applet viewer output pipe name. -gchar* out_pipe_name; - -// Applet viewer output watch source. -gint out_watch_source; - -// Applet viewer output channel. -GIOChannel* out_to_appletviewer; - -// Tracks jvm status -gboolean jvm_up = FALSE; - -// Keeps track of initialization. NP_Initialize should only be -// called once. -gboolean initialized = false; - -GQuark ITNP_PLUGIN_ERROR = g_quark_from_string("IcedTeaNPPlugin"); - -// GCJPluginData stores all the data associated with a single plugin -// instance. A separate plugin instance is created for each <APPLET> -// tag. For now, each plugin instance spawns its own applet viewer -// process but this may need to change if we find pages containing -// multiple applets that expect to be running in the same VM. -struct GCJPluginData -{ - // A unique identifier for this plugin window. - gchar* instance_string; - // Mutex to protect appletviewer_alive. - GMutex* appletviewer_mutex; - // Back-pointer to the plugin instance to which this data belongs. - // This should not be freed but instead simply set to NULL. - NPP owner; - // The address of the plugin window. This should not be freed but - // instead simply set to NULL. - gpointer window_handle; - // The last plugin window width sent to us by the browser. - guint32 window_width; - // The last plugin window height sent to us by the browser. - guint32 window_height; -}; - -// Documentbase retrieval type-punning union. -typedef union -{ - void** void_field; - nsIPluginTagInfo2** info_field; -} info_union; - -// Static instance helper functions. -// Have the browser allocate a new GCJPluginData structure. -static void plugin_data_new (GCJPluginData** data); -// Retrieve the current document's documentbase. -static gchar* plugin_get_documentbase (NPP instance); -// Notify the user that the appletviewer is not installed correctly. -static void plugin_display_failure_dialog (); -// Callback used to monitor input pipe status. -static gboolean plugin_in_pipe_callback (GIOChannel* source, - GIOCondition condition, - gpointer plugin_data); -// Callback used to monitor output pipe status. -static gboolean plugin_out_pipe_callback (GIOChannel* source, - GIOCondition condition, - gpointer plugin_data); -static NPError plugin_start_appletviewer (GCJPluginData* data); -static gchar* plugin_create_applet_tag (int16 argc, char* argn[], - char* argv[]); -static void plugin_send_message_to_appletviewer (gchar const* message); -static void plugin_stop_appletviewer (); -// Uninitialize GCJPluginData structure -static void plugin_data_destroy (NPP instance); - -NS_IMETHODIMP get_cookie_info(const char* siteAddr, char** cookieString); -void get_proxy_info(const char* siteAddr, char** proxy_scheme, char** proxy_host, char** proxy_port, GError *error); -void decode_url(const gchar* url, gchar** decoded_url); -void consume_message(gchar* message); -void start_jvm_if_needed(); -static void appletviewer_monitor(GPid pid, gint status, gpointer data); - -// Global instance counter. -// Mutex to protect plugin_instance_counter. -static GMutex* plugin_instance_mutex = NULL; -// A global variable for reporting GLib errors. This must be free'd -// and set to NULL after each use. -static GError* channel_error = NULL; - -static GHashTable* instance_to_id_map = g_hash_table_new(NULL, NULL); -static GHashTable* id_to_instance_map = g_hash_table_new(NULL, NULL); -static gint instance_counter = 1; -static GPid appletviewer_pid = -1; -static guint appletviewer_watch_id = -1; - -// Functions prefixed by GCJ_ are instance functions. They are called -// by the browser and operate on instances of GCJPluginData. -// Functions prefixed by plugin_ are static helper functions. -// Functions prefixed by NP_ are factory functions. They are called -// by the browser and provide functionality needed to create plugin -// instances. - -// INSTANCE FUNCTIONS - -// Creates a new gcjwebplugin instance. This function creates a -// GCJPluginData* and stores it in instance->pdata. The following -// GCJPluginData fiels are initialized: instance_string, in_pipe_name, -// in_from_appletviewer, in_watch_source, out_pipe_name, -// out_to_appletviewer, out_watch_source, appletviewer_mutex, owner, -// appletviewer_alive. In addition two pipe files are created. All -// of those fields must be properly destroyed, and the pipes deleted, -// by GCJ_Destroy. If an error occurs during initialization then this -// function will free anything that's been allocated so far, set -// instance->pdata to NULL and return an error code. -NPError -GCJ_New (NPMIMEType pluginType, NPP instance, uint16 mode, - int16 argc, char* argn[], char* argv[], - NPSavedData* saved) -{ - PLUGIN_DEBUG ("GCJ_New"); - - NPError np_error = NPERR_NO_ERROR; - GCJPluginData* data = NULL; - - gchar* documentbase = NULL; - gchar* read_message = NULL; - gchar* applet_tag = NULL; - gchar* tag_message = NULL; - gchar* cookie_info = NULL; - - if (!instance) - { - PLUGIN_ERROR ("Browser-provided instance pointer is NULL."); - np_error = NPERR_INVALID_INSTANCE_ERROR; - goto cleanup_done; - } - - // data - plugin_data_new (&data); - if (data == NULL) - { - PLUGIN_ERROR ("Failed to allocate plugin data."); - np_error = NPERR_OUT_OF_MEMORY_ERROR; - goto cleanup_done; - } - - // start the jvm if needed - start_jvm_if_needed(); - - // Initialize data->instance_string. - // - // instance_string should be unique for this process so we use a - // combination of getpid and plugin_instance_counter. - // - // Critical region. Reference and increment plugin_instance_counter - // global. - g_mutex_lock (plugin_instance_mutex); - - // data->instance_string - data->instance_string = g_strdup_printf ("%d", - instance_counter); - - g_mutex_unlock (plugin_instance_mutex); - - // data->appletviewer_mutex - data->appletviewer_mutex = g_mutex_new (); - - g_mutex_lock (data->appletviewer_mutex); - - // Documentbase retrieval. - documentbase = plugin_get_documentbase (instance); - if (!documentbase) - { - PLUGIN_ERROR ("Documentbase retrieval failed." - " Browser not Mozilla-based?"); - goto cleanup_appletviewer_mutex; - } - - // Send applet tag message to appletviewer. - applet_tag = plugin_create_applet_tag (argc, argn, argv); - - tag_message = (gchar*) malloc(strlen(applet_tag)*sizeof(gchar) + 1024); - g_sprintf(tag_message, "instance %d tag %s %s", instance_counter, documentbase, applet_tag); - - //plugin_send_message_to_appletviewer (data, data->instance_string); - plugin_send_message_to_appletviewer (tag_message); - - //send cookie information - char* cookie_string; - if (get_cookie_info(documentbase, &cookie_string) == NS_OK) - { - cookie_info = (gchar*) malloc(sizeof(cookie_string) + 1024); - g_sprintf(cookie_info, "instance %d cookie %s", instance_counter, cookie_string); - } - else - { - cookie_info = (gchar*) malloc(1024); - g_sprintf(cookie_info, "instance %d cookie", instance_counter); - } - - plugin_send_message_to_appletviewer (cookie_info); - - g_mutex_unlock (data->appletviewer_mutex); - - // If initialization succeeded entirely then we store the plugin - // data in the instance structure and return. Otherwise we free the - // data we've allocated so far and set instance->pdata to NULL. - - // Set back-pointer to owner instance. - data->owner = instance; - instance->pdata = data; - goto cleanup_done; - - cleanup_appletviewer_mutex: - g_free (data->appletviewer_mutex); - data->appletviewer_mutex = NULL; - - // cleanup_instance_string: - g_free (data->instance_string); - data->instance_string = NULL; - - // cleanup_data: - // Eliminate back-pointer to plugin instance. - data->owner = NULL; - (*browserFunctions.memfree) (data); - data = NULL; - - // Initialization failed so return a NULL pointer for the browser - // data. - instance->pdata = NULL; - - cleanup_done: - g_free (tag_message); - tag_message = NULL; - g_free (applet_tag); - applet_tag = NULL; - g_free (read_message); - read_message = NULL; - g_free (documentbase); - documentbase = NULL; - - // store an identifier for this plugin - g_hash_table_insert(instance_to_id_map, instance, GINT_TO_POINTER(instance_counter)); - g_hash_table_insert(id_to_instance_map, GINT_TO_POINTER(instance_counter), instance); - instance_counter++; - - PLUGIN_DEBUG ("GCJ_New return"); - - return np_error; -} - -// Starts the JVM if it is not already running -void start_jvm_if_needed() -{ - - // This is asynchronized function. It must - // have exclusivity when running. - - GMutex *vm_start_mutex = g_mutex_new(); - g_mutex_lock(vm_start_mutex); - - PLUGIN_DEBUG_0ARG("Checking JVM status...\n"); - - // If the jvm is already up, do nothing - if (jvm_up) - { - PLUGIN_DEBUG_0ARG("JVM is up. Returning.\n"); - return; - } - - PLUGIN_DEBUG_0ARG("No JVM is running. Attempting to start one...\n"); - - NPError np_error = NPERR_NO_ERROR; - GCJPluginData* data = NULL; - - // Create appletviewer-to-plugin pipe which we refer to as the input - // pipe. - - // in_pipe_name - in_pipe_name = g_strdup_printf ("%s/icedteanp-appletviewer-to-plugin", - data_directory); - if (!in_pipe_name) - { - PLUGIN_ERROR ("Failed to create input pipe name."); - np_error = NPERR_OUT_OF_MEMORY_ERROR; - // If in_pipe_name is NULL then the g_free at - // cleanup_in_pipe_name will simply return. - goto cleanup_in_pipe_name; - } - - // clean up any older pip - unlink (in_pipe_name); - - PLUGIN_DEBUG_TWO ("GCJ_New: creating input fifo:", in_pipe_name); - if (mkfifo (in_pipe_name, 0700) == -1 && errno != EEXIST) - { - PLUGIN_ERROR_TWO ("Failed to create input pipe", strerror (errno)); - np_error = NPERR_GENERIC_ERROR; - goto cleanup_in_pipe_name; - } - PLUGIN_DEBUG_TWO ("GCJ_New: created input fifo:", in_pipe_name); - - // Create plugin-to-appletviewer pipe which we refer to as the - // output pipe. - - // out_pipe_name - out_pipe_name = g_strdup_printf ("%s/icedteanp-plugin-to-appletviewer", - data_directory); - - if (!out_pipe_name) - { - PLUGIN_ERROR ("Failed to create output pipe name."); - np_error = NPERR_OUT_OF_MEMORY_ERROR; - goto cleanup_out_pipe_name; - } - - // clean up any older pip - unlink (out_pipe_name); - - PLUGIN_DEBUG_TWO ("GCJ_New: creating output fifo:", out_pipe_name); - if (mkfifo (out_pipe_name, 0700) == -1 && errno != EEXIST) - { - PLUGIN_ERROR_TWO ("Failed to create output pipe", strerror (errno)); - np_error = NPERR_GENERIC_ERROR; - goto cleanup_out_pipe_name; - } - PLUGIN_DEBUG_TWO ("GCJ_New: created output fifo:", out_pipe_name); - - // Start a separate appletviewer process for each applet, even if - // there are multiple applets in the same page. We may need to - // change this behaviour if we find pages with multiple applets that - // rely on being run in the same VM. - - np_error = plugin_start_appletviewer (data); - - // Create plugin-to-appletviewer channel. The default encoding for - // the file is UTF-8. - // out_to_appletviewer - out_to_appletviewer = g_io_channel_new_file (out_pipe_name, - "w", &channel_error); - if (!out_to_appletviewer) - { - if (channel_error) - { - PLUGIN_ERROR_TWO ("Failed to create output channel", - channel_error->message); - g_error_free (channel_error); - channel_error = NULL; - } - else - PLUGIN_ERROR ("Failed to create output channel"); - - np_error = NPERR_GENERIC_ERROR; - goto cleanup_out_to_appletviewer; - } - - // Watch for hangup and error signals on the output pipe. - out_watch_source = - g_io_add_watch (out_to_appletviewer, - (GIOCondition) (G_IO_ERR | G_IO_HUP), - plugin_out_pipe_callback, (gpointer) out_to_appletviewer); - - // Create appletviewer-to-plugin channel. The default encoding for - // the file is UTF-8. - // in_from_appletviewer - in_from_appletviewer = g_io_channel_new_file (in_pipe_name, - "r", &channel_error); - if (!in_from_appletviewer) - { - if (channel_error) - { - PLUGIN_ERROR_TWO ("Failed to create input channel", - channel_error->message); - g_error_free (channel_error); - channel_error = NULL; - } - else - PLUGIN_ERROR ("Failed to create input channel"); - - np_error = NPERR_GENERIC_ERROR; - goto cleanup_in_from_appletviewer; - } - - // Watch for hangup and error signals on the input pipe. - in_watch_source = - g_io_add_watch (in_from_appletviewer, - (GIOCondition) (G_IO_IN | G_IO_ERR | G_IO_HUP), - plugin_in_pipe_callback, (gpointer) in_from_appletviewer); - - jvm_up = TRUE; - - goto done; - - // Free allocated data - - cleanup_in_watch_source: - // Removing a source is harmless if it fails since it just means the - // source has already been removed. - g_source_remove (in_watch_source); - in_watch_source = 0; - - cleanup_in_from_appletviewer: - if (in_from_appletviewer) - g_io_channel_unref (in_from_appletviewer); - in_from_appletviewer = NULL; - - // cleanup_out_watch_source: - g_source_remove (out_watch_source); - out_watch_source = 0; - - cleanup_out_to_appletviewer: - if (out_to_appletviewer) - g_io_channel_unref (out_to_appletviewer); - out_to_appletviewer = NULL; - - // cleanup_out_pipe: - // Delete output pipe. - PLUGIN_DEBUG_TWO ("GCJ_New: deleting input fifo:", in_pipe_name); - unlink (out_pipe_name); - PLUGIN_DEBUG_TWO ("GCJ_New: deleted input fifo:", in_pipe_name); - - cleanup_out_pipe_name: - g_free (out_pipe_name); - out_pipe_name = NULL; - - // cleanup_in_pipe: - // Delete input pipe. - PLUGIN_DEBUG_TWO ("GCJ_New: deleting output fifo:", out_pipe_name); - unlink (in_pipe_name); - PLUGIN_DEBUG_TWO ("GCJ_New: deleted output fifo:", out_pipe_name); - - cleanup_in_pipe_name: - g_free (in_pipe_name); - in_pipe_name = NULL; - - done: - - // Now other threads may re-enter.. unlock the mutex - g_mutex_unlock(vm_start_mutex); - -} - -NPError -GCJ_GetValue (NPP instance, NPPVariable variable, void* value) -{ - PLUGIN_DEBUG ("GCJ_GetValue"); - - NPError np_error = NPERR_NO_ERROR; - - switch (variable) - { - // This plugin needs XEmbed support. - case NPPVpluginNeedsXEmbed: - { - PLUGIN_DEBUG ("GCJ_GetValue: returning TRUE for NeedsXEmbed."); - PRBool* bool_value = (PRBool*) value; - *bool_value = PR_TRUE; - } - break; - - default: - PLUGIN_ERROR ("Unknown plugin value requested."); - np_error = NPERR_GENERIC_ERROR; - break; - } - - PLUGIN_DEBUG ("GCJ_GetValue return"); - - return np_error; -} - -NPError -GCJ_Destroy (NPP instance, NPSavedData** save) -{ - PLUGIN_DEBUG ("GCJ_Destroy"); - - GCJPluginData* data = (GCJPluginData*) instance->pdata; - - if (data) - { - // Free plugin data. - plugin_data_destroy (instance); - } - - PLUGIN_DEBUG ("GCJ_Destroy return"); - - return NPERR_NO_ERROR; -} - -NPError -GCJ_SetWindow (NPP instance, NPWindow* window) -{ - PLUGIN_DEBUG ("GCJ_SetWindow"); - - if (instance == NULL) - { - PLUGIN_ERROR ("Invalid instance."); - - return NPERR_INVALID_INSTANCE_ERROR; - } - - gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance); - - gint id = 0; - if (id_ptr) - { - id = GPOINTER_TO_INT(id_ptr); - } - - GCJPluginData* data = (GCJPluginData*) instance->pdata; - - // Simply return if we receive a NULL window. - if ((window == NULL) || (window->window == NULL)) - { - PLUGIN_DEBUG ("GCJ_SetWindow: got NULL window."); - - return NPERR_NO_ERROR; - } - - if (data->window_handle) - { - // The window already exists. - if (data->window_handle == window->window) - { - // The parent window is the same as in previous calls. - PLUGIN_DEBUG ("GCJ_SetWindow: window already exists."); - - // Critical region. Read data->appletviewer_mutex and send - // a message to the appletviewer. - g_mutex_lock (data->appletviewer_mutex); - - if (jvm_up) - { - - gboolean dim_changed = FALSE; - - // The window is the same as it was for the last - // SetWindow call. - if (window->width != data->window_width) - { - PLUGIN_DEBUG ("GCJ_SetWindow: window width changed."); - // The width of the plugin window has changed. - - // Store the new width. - data->window_width = window->width; - dim_changed = TRUE; - } - - if (window->height != data->window_height) - { - PLUGIN_DEBUG ("GCJ_SetWindow: window height changed."); - // The height of the plugin window has changed. - - // Store the new height. - data->window_height = window->height; - - dim_changed = TRUE; - } - - if (dim_changed) { - gchar* message = g_strdup_printf ("instance 1 width %d height %d", - window->width, window->height); - plugin_send_message_to_appletviewer (message); - g_free (message); - message = NULL; - } - - - } - else - { - // The appletviewer is not running. - PLUGIN_DEBUG ("GCJ_SetWindow: appletviewer is not running."); - } - - g_mutex_unlock (data->appletviewer_mutex); - } - else - { - // The parent window has changed. This branch does run but - // doing nothing in response seems to be sufficient. - PLUGIN_DEBUG ("GCJ_SetWindow: parent window changed."); - } - } - else - { - PLUGIN_DEBUG ("GCJ_SetWindow: setting window."); - - // Critical region. Send messages to appletviewer. - g_mutex_lock (data->appletviewer_mutex); - - gchar *window_message = g_strdup_printf ("instance %d handle %ld", - id, (gulong) window->window); - plugin_send_message_to_appletviewer (window_message); - g_free (window_message); - - window_message = g_strdup_printf ("instance %d width %d height %d", - id, - window->width, - window->height); - plugin_send_message_to_appletviewer (window_message); - g_free (window_message); - window_message = NULL; - - g_mutex_unlock (data->appletviewer_mutex); - - // Store the window handle. - data->window_handle = window->window; - } - - PLUGIN_DEBUG ("GCJ_SetWindow return"); - - return NPERR_NO_ERROR; -} - -NPError -GCJ_NewStream (NPP instance, NPMIMEType type, NPStream* stream, - NPBool seekable, uint16* stype) -{ - PLUGIN_DEBUG ("GCJ_NewStream"); - - PLUGIN_DEBUG ("GCJ_NewStream return"); - - return NPERR_NO_ERROR; -} - -void -GCJ_StreamAsFile (NPP instance, NPStream* stream, const char* filename) -{ - PLUGIN_DEBUG ("GCJ_StreamAsFile"); - - PLUGIN_DEBUG ("GCJ_StreamAsFile return"); -} - -NPError -GCJ_DestroyStream (NPP instance, NPStream* stream, NPReason reason) -{ - PLUGIN_DEBUG ("GCJ_DestroyStream"); - - PLUGIN_DEBUG ("GCJ_DestroyStream return"); - - return NPERR_NO_ERROR; -} - -int32 -GCJ_WriteReady (NPP instance, NPStream* stream) -{ - PLUGIN_DEBUG ("GCJ_WriteReady"); - - PLUGIN_DEBUG ("GCJ_WriteReady return"); - - return 0; -} - -int32 -GCJ_Write (NPP instance, NPStream* stream, int32 offset, int32 len, - void* buffer) -{ - PLUGIN_DEBUG ("GCJ_Write"); - - PLUGIN_DEBUG ("GCJ_Write return"); - - return 0; -} - -void -GCJ_Print (NPP instance, NPPrint* platformPrint) -{ - PLUGIN_DEBUG ("GCJ_Print"); - - PLUGIN_DEBUG ("GCJ_Print return"); -} - -int16 -GCJ_HandleEvent (NPP instance, void* event) -{ - PLUGIN_DEBUG ("GCJ_HandleEvent"); - - PLUGIN_DEBUG ("GCJ_HandleEvent return"); - - return 0; -} - -void -GCJ_URLNotify (NPP instance, const char* url, NPReason reason, - void* notifyData) -{ - PLUGIN_DEBUG ("GCJ_URLNotify"); - - PLUGIN_DEBUG ("GCJ_URLNotify return"); -} - -jref -GCJ_GetJavaClass (void) -{ - PLUGIN_DEBUG ("GCJ_GetJavaClass"); - - PLUGIN_DEBUG ("GCJ_GetJavaClass return"); - - return 0; -} - -NS_IMETHODIMP -get_cookie_info(const char* siteAddr, char** cookieString) -{ - - nsresult rv; - nsCOMPtr<nsIScriptSecurityManager> sec_man = - do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); - - if (!sec_man) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv); - - if (NS_FAILED(rv) || !io_svc) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIURI> uri; - io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); - - nsCOMPtr<nsICookieService> cookie_svc = do_GetService("@mozilla.org/cookieService;1", &rv); - - if (NS_FAILED(rv) || !cookie_svc) { - return NS_ERROR_FAILURE; - } - - rv = cookie_svc->GetCookieString(uri, NULL, cookieString); - - if (NS_FAILED(rv) || !*cookieString) { - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - - -// HELPER FUNCTIONS - -static void -plugin_data_new (GCJPluginData** data) -{ - PLUGIN_DEBUG ("plugin_data_new"); - - *data = (GCJPluginData*) - (*browserFunctions.memalloc) (sizeof (struct GCJPluginData)); - - // appletviewer_alive is false until the applet viewer is spawned. - if (*data) - memset (*data, 0, sizeof (struct GCJPluginData)); - - PLUGIN_DEBUG ("plugin_data_new return"); -} - -// Documentbase retrieval. This function gets the current document's -// documentbase. This function relies on browser-private data so it -// will only work when the plugin is loaded in a Mozilla-based -// browser. We could not find a way to retrieve the documentbase -// using the original Netscape plugin API so we use the XPCOM API -// instead. -static gchar* -plugin_get_documentbase (NPP instance) -{ - PLUGIN_DEBUG ("plugin_get_documentbase"); - - nsIPluginInstance* xpcom_instance = NULL; - nsIPluginInstancePeer* peer = NULL; - nsresult result = 0; - nsIPluginTagInfo2* pluginTagInfo2 = NULL; - info_union u = { NULL }; - char const* documentbase = NULL; - gchar* documentbase_copy = NULL; - - xpcom_instance = (nsIPluginInstance*) (instance->ndata); - if (!xpcom_instance) - { - PLUGIN_ERROR ("xpcom_instance is NULL."); - goto cleanup_done; - } - - xpcom_instance->GetPeer (&peer); - if (!peer) - { - PLUGIN_ERROR ("peer is NULL."); - goto cleanup_done; - } - - u.info_field = &pluginTagInfo2; - - result = peer->QueryInterface (kIPluginTagInfo2IID, - u.void_field); - if (result || !pluginTagInfo2) - { - PLUGIN_ERROR ("pluginTagInfo2 retrieval failed."); - goto cleanup_peer; - } - - pluginTagInfo2->GetDocumentBase (&documentbase); - - if (!documentbase) - { - PLUGIN_ERROR ("documentbase is NULL."); - goto cleanup_plugintaginfo2; - } - - documentbase_copy = g_strdup (documentbase); - - // Release references. - cleanup_plugintaginfo2: - NS_RELEASE (pluginTagInfo2); - - cleanup_peer: - NS_RELEASE (peer); - - cleanup_done: - PLUGIN_DEBUG ("plugin_get_documentbase return"); - - return documentbase_copy; -} - -// This function displays an error message if the appletviewer has not -// been installed correctly. -static void -plugin_display_failure_dialog () -{ - GtkWidget* dialog = NULL; - - PLUGIN_DEBUG ("plugin_display_failure_dialog"); - - dialog = gtk_message_dialog_new (NULL, - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - FAILURE_MESSAGE, - appletviewer_executable); - gtk_widget_show_all (dialog); - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); - - PLUGIN_DEBUG ("plugin_display_failure_dialog return"); -} - - - -// plugin_in_pipe_callback is called when data is available on the -// input pipe, or when the appletviewer crashes or is killed. It may -// be called after data has been destroyed in which case it simply -// returns FALSE to remove itself from the glib main loop. -static gboolean -plugin_in_pipe_callback (GIOChannel* source, - GIOCondition condition, - gpointer plugin_data) -{ - PLUGIN_DEBUG ("plugin_in_pipe_callback"); - - gboolean keep_installed = TRUE; - - if (condition & G_IO_IN) - { - gchar* message = NULL; - - if (g_io_channel_read_line (in_from_appletviewer, - &message, NULL, NULL, - &channel_error) - != G_IO_STATUS_NORMAL) - { - if (channel_error) - { - PLUGIN_ERROR_TWO ("Failed to read line from input channel", - channel_error->message); - g_error_free (channel_error); - channel_error = NULL; - } - else - PLUGIN_ERROR ("Failed to read line from input channel"); - } else - { - consume_message(message); - } - - g_free (message); - message = NULL; - - keep_installed = TRUE; - } - - if (condition & (G_IO_ERR | G_IO_HUP)) - { - PLUGIN_DEBUG ("appletviewer has stopped."); - keep_installed = FALSE; - } - - PLUGIN_DEBUG ("plugin_in_pipe_callback return"); - - return keep_installed; -} - -void consume_message(gchar* message) { - - g_print (" PIPE: plugin read: %s\n", message); - - if (g_str_has_prefix (message, "instance")) - { - - gchar** parts = g_strsplit (message, " ", -1); - guint parts_sz = g_strv_length (parts); - - int instance_id = atoi(parts[1]); - NPP instance = (NPP) g_hash_table_lookup(id_to_instance_map, - GINT_TO_POINTER(instance_id)); - - if (!instance) - { - PLUGIN_DEBUG_2ARG("Instance %d is not active. Refusing to consume message \"%s\"\n", instance_id, message); - return; - } - - GCJPluginData* data = (GCJPluginData*) instance->pdata; - - if (g_str_has_prefix (parts[2], "url")) - { - // Open the URL in a new browser window. - gchar* decoded_url = (gchar*) malloc(strlen(parts[3])*sizeof(gchar) + sizeof(gchar)); - decode_url(parts[3], &decoded_url); - - PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback: opening URL", decoded_url); - PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback: URL target", parts[4]); - - NPError np_error = - (*browserFunctions.geturl) (data->owner, decoded_url, parts[4]); - - - if (np_error != NPERR_NO_ERROR) - PLUGIN_ERROR ("Failed to load URL."); - - g_free(decoded_url); - decoded_url = NULL; - } - else if (g_str_has_prefix (parts[2], "status")) - { - - // clear the "instance X status" parts - sprintf(parts[0], ""); - sprintf(parts[1], ""); - sprintf(parts[2], ""); - - // join the rest - gchar* status_message = g_strjoinv(" ", parts); - PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback: setting status", status_message); - (*browserFunctions.status) (data->owner, status_message); - - g_free(status_message); - status_message = NULL; - } - - g_strfreev (parts); - parts = NULL; - } - else if (g_str_has_prefix (message, "plugin ")) - { - // internal plugin related message - gchar** parts = g_strsplit (message, " ", 3); - if (g_str_has_prefix(parts[1], "PluginProxyInfo")) - { - gchar* proxy_scheme = (gchar*) malloc(sizeof(gchar)*32); - gchar* proxy_host = (gchar*) malloc(sizeof(gchar)*64); - gchar* proxy_port = (gchar*) malloc(sizeof(gchar)*8); - - GError *error = g_error_new(ITNP_PLUGIN_ERROR, 0, ""); - - gchar* decoded_url = (gchar*) malloc(strlen(parts[2])*sizeof(gchar) + sizeof(gchar)); - decode_url(parts[2], &decoded_url); - get_proxy_info(decoded_url, &proxy_scheme, &proxy_host, &proxy_port, error); - - gchar* proxy_info; - proxy_info = g_strconcat ("plugin PluginProxyInfo ", NULL); - if (error->code == 0) - { - proxy_info = g_strconcat (proxy_info, proxy_scheme, " ", proxy_host, " ", proxy_port, NULL); - } - - PLUGIN_DEBUG_1ARG("Proxy info: %s\n", proxy_info); - plugin_send_message_to_appletviewer(proxy_info); - - g_free(decoded_url); - decoded_url = NULL; - g_free(proxy_info); - proxy_info = NULL; - g_free(proxy_scheme); - proxy_scheme = NULL; - g_free(proxy_host); - proxy_host = NULL; - g_free(proxy_port); - proxy_port = NULL; - } - } - else - { - g_print (" Unable to handle message: %s\n", message); - } -} - -void decode_url(const gchar* url, gchar** decoded_url) -{ - // There is no GLib function to decode urls, so we fallback to Mozilla's - // methods - - nsresult rv; - nsCOMPtr<nsINetUtil> net_util = do_GetService(NS_NETUTIL_CONTRACTID, &rv); - - if (!net_util) - printf("Error instantiating NetUtil service.\n"); - - nsDependentCSubstring unescaped_url; - net_util->UnescapeString(nsCString(url), 0, unescaped_url); - - // no need for strn. decoded_url is malloced to sizeof unescaped_url, which - // will always be <= decoded size - strcpy(*decoded_url, nsCString(unescaped_url).get()); -} - -void get_proxy_info(const char* siteAddr, char** proxy_scheme, char** proxy_host, char** proxy_port, GError *error) -{ - nsresult rv; - - // Initialize service variables - nsCOMPtr<nsIProtocolProxyService> proxy_svc = do_GetService("@mozilla.org/network/protocol-proxy-service;1", &rv); - - if (!proxy_svc) { - printf("Cannot initialize proxy service\n"); - error->code = 1; - return; - } - - nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv); - - if (NS_FAILED(rv) || !io_svc) { - printf("Cannot initialize io service\n"); - error->code = 1; - return; - } - - // uri which needs to be accessed - nsCOMPtr<nsIURI> uri; - io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); - - // find the proxy address if any - nsCOMPtr<nsIProxyInfo> info; - proxy_svc->Resolve(uri, 0, getter_AddRefs(info)); - - // if there is no proxy found, return immediately - if (!info) { - PLUGIN_DEBUG_1ARG("%s does not need a proxy\n", siteAddr); - error->code = 1; - return; - } - - // if proxy info is available, extract it - nsCString phost; - PRInt32 pport; - nsCString ptype; - - info->GetHost(phost); - info->GetPort(&pport); - info->GetType(ptype); - - // resolve the proxy address to an IP - nsCOMPtr<nsIDNSService> dns_svc = do_GetService("@mozilla.org/network/dns-service;1", &rv); - - if (!dns_svc) { - printf("Cannot initialize DNS service\n"); - error->code = 1; - return; - } - - nsCOMPtr<nsIDNSRecord> record; - dns_svc->Resolve(phost, 0U, getter_AddRefs(record)); - - // TODO: Add support for multiple ips - nsDependentCString ipAddr; - record->GetNextAddrAsString(ipAddr); - - // pack information in return variables - snprintf(*proxy_scheme, sizeof(char)*32, "%s", ptype.get()); - snprintf(*proxy_host, sizeof(char)*64, "%s", ipAddr.get()); - snprintf(*proxy_port, sizeof(char)*8, "%d", pport); - - PLUGIN_DEBUG_4ARG("Proxy info for %s: %s %s %s\n", siteAddr, *proxy_scheme, *proxy_host, *proxy_port); -} - -// plugin_out_pipe_callback is called when the appletviewer crashes or -// is killed. It may be called after data has been destroyed in which -// case it simply returns FALSE to remove itself from the glib main -// loop. -static gboolean -plugin_out_pipe_callback (GIOChannel* source, - GIOCondition condition, - gpointer plugin_data) -{ - PLUGIN_DEBUG ("plugin_out_pipe_callback"); - - GCJPluginData* data = (GCJPluginData*) plugin_data; - - PLUGIN_DEBUG ("plugin_out_pipe_callback: appletviewer has stopped."); - - PLUGIN_DEBUG ("plugin_out_pipe_callback return"); - - return FALSE; -} - -static NPError -plugin_test_appletviewer () -{ - PLUGIN_DEBUG ("plugin_test_appletviewer"); - NPError error = NPERR_NO_ERROR; - - gchar* command_line[3] = { NULL, NULL, NULL }; - - command_line[0] = g_strdup (appletviewer_executable); - command_line[1] = g_strdup("-version"); - command_line[2] = NULL; - - - if (!g_spawn_async (NULL, command_line, NULL, (GSpawnFlags) 0, - NULL, NULL, NULL, &channel_error)) - { - if (channel_error) - { - PLUGIN_ERROR_TWO ("Failed to spawn applet viewer", - channel_error->message); - g_error_free (channel_error); - channel_error = NULL; - } - else - PLUGIN_ERROR ("Failed to spawn applet viewer"); - error = NPERR_GENERIC_ERROR; - } - - g_free (command_line[0]); - command_line[0] = NULL; - g_free (command_line[1]); - command_line[1] = NULL; - g_free (command_line[2]); - command_line[2] = NULL; - - PLUGIN_DEBUG ("plugin_test_appletviewer return"); - return error; -} - -static NPError -plugin_start_appletviewer (GCJPluginData* data) -{ - PLUGIN_DEBUG ("plugin_start_appletviewer"); - NPError error = NPERR_NO_ERROR; - - gchar* command_line[6] = { NULL, NULL, NULL, NULL, NULL, NULL }; - - command_line[0] = g_strdup (appletviewer_executable); - // Output from plugin's perspective is appletviewer's input. - // Input from plugin's perspective is appletviewer's output. - command_line[1] = g_strdup("-Xdebug"); - command_line[2] = g_strdup("-Xnoagent"); - command_line[3] = g_strdup("-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n"); - command_line[4] = g_strdup("sun.applet.PluginMain"); - command_line[5] = NULL; - - if (!g_spawn_async (NULL, command_line, NULL, (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, - NULL, NULL, &appletviewer_pid, &channel_error)) - { - if (channel_error) - { - PLUGIN_ERROR_TWO ("Failed to spawn applet viewer", - channel_error->message); - g_error_free (channel_error); - channel_error = NULL; - } - else - PLUGIN_ERROR ("Failed to spawn applet viewer"); - error = NPERR_GENERIC_ERROR; - } - - g_free (command_line[0]); - command_line[0] = NULL; - g_free (command_line[1]); - command_line[1] = NULL; - g_free (command_line[2]); - command_line[2] = NULL; - g_free (command_line[3]); - command_line[3] = NULL; - g_free (command_line[4]); - command_line[4] = NULL; - g_free (command_line[5]); - command_line[5] = NULL; - - if (appletviewer_pid) - { - PLUGIN_DEBUG_1ARG("Initialized VM with pid=%d\n", appletviewer_pid); - appletviewer_watch_id = g_child_watch_add(appletviewer_pid, (GChildWatchFunc) appletviewer_monitor, (gpointer) appletviewer_pid); - } - - - PLUGIN_DEBUG ("plugin_start_appletviewer return"); - return error; -} - -// Build up the applet tag string that we'll send to the applet -// viewer. -static gchar* -plugin_create_applet_tag (int16 argc, char* argn[], char* argv[]) -{ - PLUGIN_DEBUG ("plugin_create_applet_tag"); - - gchar* applet_tag = g_strdup ("<EMBED "); - gchar* parameters = g_strdup (""); - - for (int16 i = 0; i < argc; i++) - { - if (!g_ascii_strcasecmp (argn[i], "code")) - { - gchar* code = g_strdup_printf ("CODE=\"%s\" ", argv[i]); - applet_tag = g_strconcat (applet_tag, code, NULL); - g_free (code); - code = NULL; - } - else if (!g_ascii_strcasecmp (argn[i], "codebase")) - { - gchar* codebase = g_strdup_printf ("CODEBASE=\"%s\" ", argv[i]); - applet_tag = g_strconcat (applet_tag, codebase, NULL); - g_free (codebase); - codebase = NULL; - } - else if (!g_ascii_strcasecmp (argn[i], "classid")) - { - gchar* classid = g_strdup_printf ("CLASSID=\"%s\" ", argv[i]); - applet_tag = g_strconcat (applet_tag, classid, NULL); - g_free (classid); - classid = NULL; - } - else if (!g_ascii_strcasecmp (argn[i], "archive")) - { - gchar* archive = g_strdup_printf ("ARCHIVE=\"%s\" ", argv[i]); - applet_tag = g_strconcat (applet_tag, archive, NULL); - g_free (archive); - archive = NULL; - } - else if (!g_ascii_strcasecmp (argn[i], "width")) - { - gchar* width = g_strdup_printf ("width=\"%s\" ", argv[i]); - applet_tag = g_strconcat (applet_tag, width, NULL); - g_free (width); - width = NULL; - } - else if (!g_ascii_strcasecmp (argn[i], "height")) - { - gchar* height = g_strdup_printf ("height=\"%s\" ", argv[i]); - applet_tag = g_strconcat (applet_tag, height, NULL); - g_free (height); - height = NULL; - } - else - { - // Escape the parameter value so that line termination - // characters will pass through the pipe. - if (argv[i] != '\0') - { - gchar* escaped = NULL; - - escaped = g_strescape (argv[i], NULL); - parameters = g_strconcat (parameters, "<PARAM NAME=\"", argn[i], - "\" VALUE=\"", escaped, "\">", NULL); - - g_free (escaped); - escaped = NULL; - } - } - } - - applet_tag = g_strconcat (applet_tag, ">", parameters, "</EMBED>", NULL); - - g_free (parameters); - parameters = NULL; - - PLUGIN_DEBUG ("plugin_create_applet_tag return"); - - return applet_tag; -} - -// plugin_send_message_to_appletviewer must be called while holding -// data->appletviewer_mutex. -static void -plugin_send_message_to_appletviewer (gchar const* message) -{ - PLUGIN_DEBUG ("plugin_send_message_to_appletviewer"); - - if (jvm_up) - { - gchar* newline_message = NULL; - gsize bytes_written = 0; - - // Send message to appletviewer. - newline_message = g_strdup_printf ("%s\n", message); - - // g_io_channel_write_chars will return something other than - // G_IO_STATUS_NORMAL if not all the data is written. In that - // case we fail rather than retrying. - if (g_io_channel_write_chars (out_to_appletviewer, - newline_message, -1, &bytes_written, - &channel_error) - != G_IO_STATUS_NORMAL) - { - if (channel_error) - { - PLUGIN_ERROR_TWO ("Failed to write bytes to output channel", - channel_error->message); - g_error_free (channel_error); - channel_error = NULL; - } - else - PLUGIN_ERROR ("Failed to write bytes to output channel"); - } - - if (g_io_channel_flush (out_to_appletviewer, &channel_error) - != G_IO_STATUS_NORMAL) - { - if (channel_error) - { - PLUGIN_ERROR_TWO ("Failed to flush bytes to output channel", - channel_error->message); - g_error_free (channel_error); - channel_error = NULL; - } - else - PLUGIN_ERROR ("Failed to flush bytes to output channel"); - } - g_free (newline_message); - newline_message = NULL; - - g_print (" PIPE: plugin wrote: %s\n", message); - } - - PLUGIN_DEBUG ("plugin_send_message_to_appletviewer return"); -} - -// Stop the appletviewer process. When this is called the -// appletviewer can be in any of three states: running, crashed or -// hung. If the appletviewer is running then sending it "shutdown" -// will cause it to exit. This will cause -// plugin_out_pipe_callback/plugin_in_pipe_callback to be called and -// the input and output channels to be shut down. If the appletviewer -// has crashed then plugin_out_pipe_callback/plugin_in_pipe_callback -// would already have been called and data->appletviewer_alive cleared -// in which case this function simply returns. If the appletviewer is -// hung then this function will be successful and the input and output -// watches will be removed by plugin_data_destroy. -// plugin_stop_appletviewer must be called with -// data->appletviewer_mutex held. -static void -plugin_stop_appletviewer () -{ - PLUGIN_DEBUG ("plugin_stop_appletviewer"); - - if (jvm_up) - { - // Shut down the appletviewer. - gsize bytes_written = 0; - - if (out_to_appletviewer) - { - if (g_io_channel_write_chars (out_to_appletviewer, "shutdown", - -1, &bytes_written, &channel_error) - != G_IO_STATUS_NORMAL) - { - if (channel_error) - { - PLUGIN_ERROR_TWO ("Failed to write shutdown message to" - " appletviewer", channel_error->message); - g_error_free (channel_error); - channel_error = NULL; - } - else - PLUGIN_ERROR ("Failed to write shutdown message to"); - } - - if (g_io_channel_flush (out_to_appletviewer, &channel_error) - != G_IO_STATUS_NORMAL) - { - if (channel_error) - { - PLUGIN_ERROR_TWO ("Failed to write shutdown message to" - " appletviewer", channel_error->message); - g_error_free (channel_error); - channel_error = NULL; - } - else - PLUGIN_ERROR ("Failed to write shutdown message to"); - } - - if (g_io_channel_shutdown (out_to_appletviewer, - TRUE, &channel_error) - != G_IO_STATUS_NORMAL) - { - if (channel_error) - { - PLUGIN_ERROR_TWO ("Failed to shut down appletviewer" - " output channel", channel_error->message); - g_error_free (channel_error); - channel_error = NULL; - } - else - PLUGIN_ERROR ("Failed to shut down appletviewer"); - } - } - - if (in_from_appletviewer) - { - if (g_io_channel_shutdown (in_from_appletviewer, - TRUE, &channel_error) - != G_IO_STATUS_NORMAL) - { - if (channel_error) - { - PLUGIN_ERROR_TWO ("Failed to shut down appletviewer" - " input channel", channel_error->message); - g_error_free (channel_error); - channel_error = NULL; - } - else - PLUGIN_ERROR ("Failed to shut down appletviewer"); - } - } - } - - jvm_up = FALSE; - sleep(2); /* Needed to prevent crashes during debug (when JDWP port is not freed by the kernel right away) */ - - PLUGIN_DEBUG ("plugin_stop_appletviewer return"); -} - -static void appletviewer_monitor(GPid pid, gint status, gpointer data) -{ - PLUGIN_DEBUG ("appletviewer_monitor"); - jvm_up = FALSE; - pid = -1; - PLUGIN_DEBUG ("appletviewer_monitor return"); -} - -static void -plugin_data_destroy (NPP instance) -{ - PLUGIN_DEBUG ("plugin_data_destroy"); - - GCJPluginData* tofree = (GCJPluginData*) instance->pdata; - - // Remove instance from map - gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance); - - if (id_ptr) - { - gint id = GPOINTER_TO_INT(id_ptr); - g_hash_table_remove(instance_to_id_map, instance); - g_hash_table_remove(id_to_instance_map, id_ptr); - } - - tofree->window_handle = NULL; - tofree->window_height = 0; - tofree->window_width = 0; - - // cleanup_appletviewer_mutex: - g_free (tofree->appletviewer_mutex); - tofree->appletviewer_mutex = NULL; - - // cleanup_instance_string: - g_free (tofree->instance_string); - tofree->instance_string = NULL; - - // cleanup_data: - // Eliminate back-pointer to plugin instance. - tofree->owner = NULL; - (*browserFunctions.memfree) (tofree); - tofree = NULL; - - PLUGIN_DEBUG ("plugin_data_destroy return"); -} - -// FACTORY FUNCTIONS - -// Provides the browser with pointers to the plugin functions that we -// implement and initializes a local table with browser functions that -// we may wish to call. Called once, after browser startup and before -// the first plugin instance is created. -// The field 'initialized' is set to true once this function has -// finished. If 'initialized' is already true at the beginning of -// this function, then it is evident that NP_Initialize has already -// been called. There is no need to call this function more than once and -// this workaround avoids any duplicate calls. -NPError -NP_Initialize (NPNetscapeFuncs* browserTable, NPPluginFuncs* pluginTable) -{ - PLUGIN_DEBUG ("NP_Initialize"); - - if (initialized) - return NPERR_NO_ERROR; - else if ((browserTable == NULL) || (pluginTable == NULL)) - { - PLUGIN_ERROR ("Browser or plugin function table is NULL."); - - return NPERR_INVALID_FUNCTABLE_ERROR; - } - - // Ensure that the major version of the plugin API that the browser - // expects is not more recent than the major version of the API that - // we've implemented. - if ((browserTable->version >> 8) > NP_VERSION_MAJOR) - { - PLUGIN_ERROR ("Incompatible version."); - - return NPERR_INCOMPATIBLE_VERSION_ERROR; - } - - // Ensure that the plugin function table we've received is large - // enough to store the number of functions that we may provide. - if (pluginTable->size < sizeof (NPPluginFuncs)) - { - PLUGIN_ERROR ("Invalid plugin function table."); - - return NPERR_INVALID_FUNCTABLE_ERROR; - } - - // Ensure that the browser function table is large enough to store - // the number of browser functions that we may use. - if (browserTable->size < sizeof (NPNetscapeFuncs)) - { - PLUGIN_ERROR ("Invalid browser function table."); - - return NPERR_INVALID_FUNCTABLE_ERROR; - } - - // Store in a local table the browser functions that we may use. - browserFunctions.version = browserTable->version; - browserFunctions.size = browserTable->size; - browserFunctions.posturl = browserTable->posturl; - browserFunctions.geturl = browserTable->geturl; - browserFunctions.geturlnotify = browserTable->geturlnotify; - browserFunctions.requestread = browserTable->requestread; - browserFunctions.newstream = browserTable->newstream; - browserFunctions.write = browserTable->write; - browserFunctions.destroystream = browserTable->destroystream; - browserFunctions.status = browserTable->status; - browserFunctions.uagent = browserTable->uagent; - browserFunctions.memalloc = browserTable->memalloc; - browserFunctions.memfree = browserTable->memfree; - browserFunctions.memflush = browserTable->memflush; - browserFunctions.reloadplugins = browserTable->reloadplugins; - browserFunctions.getvalue = browserTable->getvalue; - - // Return to the browser the plugin functions that we implement. - pluginTable->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; - pluginTable->size = sizeof (NPPluginFuncs); - pluginTable->newp = NewNPP_NewProc (GCJ_New); - pluginTable->destroy = NewNPP_DestroyProc (GCJ_Destroy); - pluginTable->setwindow = NewNPP_SetWindowProc (GCJ_SetWindow); - pluginTable->newstream = NewNPP_NewStreamProc (GCJ_NewStream); - pluginTable->destroystream = NewNPP_DestroyStreamProc (GCJ_DestroyStream); - pluginTable->asfile = NewNPP_StreamAsFileProc (GCJ_StreamAsFile); - pluginTable->writeready = NewNPP_WriteReadyProc (GCJ_WriteReady); - pluginTable->write = NewNPP_WriteProc (GCJ_Write); - pluginTable->print = NewNPP_PrintProc (GCJ_Print); - pluginTable->urlnotify = NewNPP_URLNotifyProc (GCJ_URLNotify); - pluginTable->getvalue = NewNPP_GetValueProc (GCJ_GetValue); - - // Make sure the plugin data directory exists, creating it if - // necessary. - data_directory = g_strconcat (getenv ("HOME"), "/.icedteaplugin", NULL); - if (!data_directory) - { - PLUGIN_ERROR ("Failed to create data directory name."); - return NPERR_OUT_OF_MEMORY_ERROR; - } - NPError np_error = NPERR_NO_ERROR; - gchar* filename = NULL; - if (!g_file_test (data_directory, - (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) - { - int file_error = 0; - - file_error = g_mkdir (data_directory, 0700); - if (file_error != 0) - { - PLUGIN_ERROR_THREE ("Failed to create data directory", - data_directory, - strerror (errno)); - np_error = NPERR_GENERIC_ERROR; - goto cleanup_data_directory; - } - } - - // Set appletviewer_executable. - Dl_info info; - if (dladdr ((const void*) GCJ_New, &info) == 0) - { - PLUGIN_ERROR_TWO ("Failed to determine plugin shared object filename", - dlerror ()); - np_error = NPERR_GENERIC_ERROR; - goto cleanup_data_directory; - } - filename = g_strdup (info.dli_fname); - if (!filename) - { - PLUGIN_ERROR ("Failed to create plugin shared object filename."); - np_error = NPERR_OUT_OF_MEMORY_ERROR; - goto cleanup_data_directory; - } - appletviewer_executable = g_strdup_printf ("%s/../../bin/java", - dirname (filename)); - if (!appletviewer_executable) - { - PLUGIN_ERROR ("Failed to create appletviewer executable name."); - np_error = NPERR_OUT_OF_MEMORY_ERROR; - goto cleanup_filename; - } - - np_error = plugin_test_appletviewer (); - if (np_error != NPERR_NO_ERROR) - { - plugin_display_failure_dialog (); - goto cleanup_appletviewer_executable; - } - g_free (filename); - - initialized = true; - - // Initialize threads (needed for mutexes). - if (!g_thread_supported ()) - g_thread_init (NULL); - - plugin_instance_mutex = g_mutex_new (); - - PLUGIN_DEBUG_TWO ("NP_Initialize: using", appletviewer_executable); - - PLUGIN_DEBUG ("NP_Initialize return"); - - return NPERR_NO_ERROR; - - cleanup_appletviewer_executable: - if (appletviewer_executable) - { - g_free (appletviewer_executable); - appletviewer_executable = NULL; - } - - cleanup_filename: - if (filename) - { - g_free (filename); - filename = NULL; - } - - cleanup_data_directory: - if (data_directory) - { - g_free (data_directory); - data_directory = NULL; - } - - return np_error; -} - -// Returns a string describing the MIME type that this plugin -// handles. -char* -NP_GetMIMEDescription (void) -{ - PLUGIN_DEBUG ("NP_GetMIMEDescription"); - - PLUGIN_DEBUG ("NP_GetMIMEDescription return"); - - return (char*) PLUGIN_MIME_DESC; -} - -// Returns a value relevant to the plugin as a whole. The browser -// calls this function to obtain information about the plugin. -NPError -NP_GetValue (void* future, NPPVariable variable, void* value) -{ - PLUGIN_DEBUG ("NP_GetValue"); - - NPError result = NPERR_NO_ERROR; - gchar** char_value = (gchar**) value; - - switch (variable) - { - case NPPVpluginNameString: - PLUGIN_DEBUG ("NP_GetValue: returning plugin name."); - *char_value = g_strdup (PLUGIN_NAME " " PACKAGE_VERSION); - break; - - case NPPVpluginDescriptionString: - PLUGIN_DEBUG ("NP_GetValue: returning plugin description."); - *char_value = g_strdup (PLUGIN_DESC); - break; - - default: - PLUGIN_ERROR ("Unknown plugin value requested."); - result = NPERR_GENERIC_ERROR; - break; - } - - PLUGIN_DEBUG ("NP_GetValue return"); - - return result; -} - -// Shuts down the plugin. Called after the last plugin instance is -// destroyed. -NPError -NP_Shutdown (void) -{ - PLUGIN_DEBUG ("NP_Shutdown"); - - // Free mutex. - if (plugin_instance_mutex) - { - g_mutex_free (plugin_instance_mutex); - plugin_instance_mutex = NULL; - } - - if (data_directory) - { - g_free (data_directory); - data_directory = NULL; - } - - if (appletviewer_executable) - { - g_free (appletviewer_executable); - appletviewer_executable = NULL; - } - - // stop the appletviewer - plugin_stop_appletviewer(); - - // remove monitor - if (appletviewer_watch_id != -1) - g_source_remove(appletviewer_watch_id); - - // Removing a source is harmless if it fails since it just means the - // source has already been removed. - g_source_remove (in_watch_source); - in_watch_source = 0; - - // cleanup_in_from_appletviewer: - if (in_from_appletviewer) - g_io_channel_unref (in_from_appletviewer); - in_from_appletviewer = NULL; - - // cleanup_out_watch_source: - g_source_remove (out_watch_source); - out_watch_source = 0; - - // cleanup_out_to_appletviewer: - if (out_to_appletviewer) - g_io_channel_unref (out_to_appletviewer); - out_to_appletviewer = NULL; - - // cleanup_out_pipe: - // Delete output pipe. - PLUGIN_DEBUG_TWO ("NP_Shutdown: deleting output fifo:", out_pipe_name); - unlink (out_pipe_name); - PLUGIN_DEBUG_TWO ("NP_Shutdown: deleted output fifo:", out_pipe_name); - - // cleanup_out_pipe_name: - g_free (out_pipe_name); - out_pipe_name = NULL; - - // cleanup_in_pipe: - // Delete input pipe. - PLUGIN_DEBUG_TWO ("NP_Shutdown: deleting input fifo:", in_pipe_name); - unlink (in_pipe_name); - PLUGIN_DEBUG_TWO ("NP_Shutdown: deleted input fifo:", in_pipe_name); - - // cleanup_in_pipe_name: - g_free (in_pipe_name); - in_pipe_name = NULL; - - initialized = false; - - PLUGIN_DEBUG ("NP_Shutdown return"); - - return NPERR_NO_ERROR; -}
--- a/Makefile.am Fri Jul 10 13:12:21 2009 -0400 +++ b/Makefile.am Fri Jul 10 19:02:10 2009 -0400 @@ -1604,7 +1604,17 @@ # Separate compile and link invocations to ensure intermediate object # is listed before -l options. See: # http://developer.mozilla.org/en/docs/XPCOM_Glue -IcedTeaNPPlugin.o: IcedTeaNPPlugin.cc + +NPPLUGIN_SRC=IcedTeaNPPlugin.cc IcedTeaScriptablePluginObject.cc \ + IcedTeaJavaRequestProcessor.cc IcedTeaPluginRequestProcessor.cc \ + IcedTeaPluginUtils.cc + +NPPLUGIN_OBJECTS=IcedTeaNPPlugin.o IcedTeaScriptablePluginObject.o \ + IcedTeaJavaRequestProcessor.o IcedTeaPluginRequestProcessor.o \ + IcedTeaPluginUtils.o + +IcedTeaNPPlugin_objects: + (cd plugin/icedteanp if [ -e $(abs_top_srcdir)/.hg ] && which $(HG) >/dev/null; then \ revision="-r`(cd $(abs_top_srcdir); $(HG) tip --template '{rev}')`" ; \ fi ; \ @@ -1617,18 +1627,24 @@ $(GLIB_CFLAGS) \ $(GTK_CFLAGS) \ $(MOZILLA_CFLAGS) \ - -fPIC -c -o $@ $< -IcedTeaNPPlugin.so: IcedTeaNPPlugin.o + -fPIC -c $(NPPLUGIN_SRC) + ) + +IcedTeaNPPlugin.so: IcedTeaNPPlugin_objects + (cd plugin/icedteanp $(CXX) $(CXXFLAGS) \ - $< \ + $(NPPLUGIN_OBJECTS) \ $(GLIB_LIBS) \ $(GTK_LIBS) \ $(MOZILLA_LIBS)\ -shared -o $@ + ) clean-IcedTeaNPPlugin: - rm -f IcedTeaNPPlugin.o + (cd plugin/icedteanp + rm -f $(NPPLUGIN_OBJECTS) rm -f IcedTeaNPPlugin.so + ) else if ENABLE_PLUGIN # IcedTeaPlugin.so.
--- a/plugin/icedtea/sun/applet/PluginMessageConsumer.java Fri Jul 10 13:12:21 2009 -0400 +++ b/plugin/icedtea/sun/applet/PluginMessageConsumer.java Fri Jul 10 19:02:10 2009 -0400 @@ -99,7 +99,7 @@ } // If we have less than MAX_WORKERS, create a new worker - if (workers.size() < MAX_WORKERS) { + if (workers.size() <= MAX_WORKERS) { PluginDebug.debug("Cannot find free worker, creating worker " + workers.size()); PluginMessageHandlerWorker worker = new PluginMessageHandlerWorker(streamHandler, workers.size(), as); worker.start();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/IcedTeaJavaRequestProcessor.cc Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,195 @@ +/* IcedTeaJavaRequestProcessor.cc + + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea 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, or (at your option) +any later version. + +IcedTea 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 IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +#include "IcedTeaJavaRequestProcessor.h" + +/* + * This class processes LiveConnect requests from JavaScript to Java. + * + * It sends the requests to Java, gets the return information, and sends it + * back to the browser/JavaScript + */ + +/** + * Processes return information from JavaSide (return messages of requests) + * + * @param message The message request to process + * @return boolean indicating whether the message is serviceable by this object + */ + +bool +JavaRequestProcessor::newMessageOnBus(const char* message) +{ + + // Anything we are waiting for _MUST_ have and instance id and reference # + std::vector<std::string>* message_parts = IcedTeaPluginUtilities::strSplit(message, " "); + + IcedTeaPluginUtilities::printStringVector("JavaRequest::newMessageOnBus:", message_parts); + + if (message_parts->at(0) == "context" && message_parts->at(2) == "reference") + if (atoi(message_parts->at(1).c_str()) == this->instance && atoi(message_parts->at(3).c_str()) == this->reference) + { + // Gather the results + + // GetStringUTFChars + if (message_parts->at(4) == "GetStringUTFChars") + { + // first item is length, and it is radix 10 + int length = strtol(message_parts->at(5).c_str(), NULL, 10); + + IcedTeaPluginUtilities::getUTF8String(length, 6 /* start at */, message_parts, result->return_string); + result_ready = true; + } + else if (message_parts->at(4) == "GetStringChars") // GetStringChars (UTF-16LE/UCS-2) + { + // first item is length, and it is radix 10 + int length = strtol(message_parts->at(5).c_str(), NULL, 10); + + IcedTeaPluginUtilities::getUTF16LEString(length, 6 /* start at */, message_parts, result->return_wstring); + result_ready = true; + } + + delete message_parts; + return true; + } + + delete message_parts; + return false; + +} + +/** + * Constructor. + * + * Initializes the result data structure (heap) + */ + +JavaRequestProcessor::JavaRequestProcessor() +{ + // caller frees this + result = new JavaResultData(); + result->error_msg = new std::string(); + result->return_string = new std::string(); + result->return_wstring = new std::wstring(); + result->error_occured = false; + + result_ready = false; +} + +/** + * Destructor + * + * Frees memory used by the result struct + */ + +JavaRequestProcessor::~JavaRequestProcessor() +{ + if (result) + { + if (result->error_msg) + delete result->error_msg; + + if (result->return_string) + delete result->return_string; + + if (result->return_wstring) + delete result->return_wstring; + + delete result; + } +} + +/** + * Given a string id, fetches the actual string from Java side + * + * @param request_data The JavaRequest struct containing request relevant information + * @return A JavaResultData struct containing the result of the request + */ + +JavaResultData* +JavaRequestProcessor::getString(JavaRequest* request_data) +{ + std::string string_id; + std::string* message; + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + string_id = request_data->data->at(0); + + message = IcedTeaPluginUtilities::constructMessagePrefix(0, reference); + + message->append(" GetStringUTFChars "); // get it in UTF8 + message->append(string_id); + + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + t.tv_sec += 60; // 1 minute timeout + + result_ready = false; + java_to_plugin_bus->subscribe(this); + plugin_to_java_bus->post(message->c_str()); + + // Wait for result to be filled in. + struct timespec curr_t; + + do + { + clock_gettime(CLOCK_REALTIME, &curr_t); + bool timedout = false; + + if (!result_ready && (curr_t.tv_sec < t.tv_sec)) + sleep(1); + else + break; + + } while (1); + + if (curr_t.tv_sec >= t.tv_sec) + { + // Report error + PLUGIN_DEBUG_1ARG("Error: Timed out when waiting for response to %s\n", message->c_str()); + } + + java_to_plugin_bus->unSubscribe(this); + IcedTeaPluginUtilities::releaseReference(); + + delete message; + + return result; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/IcedTeaJavaRequestProcessor.h Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,107 @@ +/* IcedTeaJavaRequestProcessor.h + + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea 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, or (at your option) +any later version. + +IcedTea 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 IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +#ifndef ICEDTEAJAVAREQUEST_H_ +#define ICEDTEAJAVAREQUEST_H_ + +#include <errno.h> +#include <stdlib.h> +#include <vector> + +#include "IcedTeaNPPlugin.h" +#include "IcedTeaPluginUtils.h" + +/* + * This struct holds data specific to a Java operation requested by the plugin + */ +typedef struct java_request +{ + // Instance id (if applicable) + int instance; + + // Context id (if applicable) + int context; + + // request specific data + std::vector<std::string>* data; + + // source of the request + std::string* source; + +} JavaRequest; + +/* + * This struct holds data specific to a Java operation requested by the plugin + */ +typedef struct java_result_data +{ + // Return identifier (if applicable) + int return_identifier; + + // Return string (if applicable) + std::string* return_string; + + // Return wide/mb string (if applicable) + std::wstring* return_wstring; + + // Error message (if an error occurred) + std::string* error_msg; + + // Boolean indicating if an error occurred + bool error_occured; + +} JavaResultData; + +class JavaRequestProcessor : BusSubscriber +{ + private: + // instance and references are constant throughout this objects + // lifecycle + int instance; + int reference; + bool result_ready; + JavaResultData* result; + + public: + JavaRequestProcessor(); + ~JavaRequestProcessor(); + virtual bool newMessageOnBus(const char* message); + JavaResultData* getString(JavaRequest* request_data); +}; + +#endif /* ICEDTEAJAVAREQUESTPROCESSOR_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/IcedTeaNPPlugin.cc Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,2071 @@ +/* IcedTeaNPPlugin.cc -- web browser plugin to execute Java applets + Copyright (C) 2003, 2004, 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 2009 Red Hat + +This file is part of GNU Classpath. + +GNU Classpath 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, or (at your option) +any later version. + +GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +// System includes. +#include <dlfcn.h> +#include <errno.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +// Documentbase retrieval includes. +#include <nsIPluginInstance.h> +#include <nsIPluginInstancePeer.h> +#include <nsIPluginTagInfo2.h> + +// API's into Mozilla +#include <nsCOMPtr.h> +#include <nsICookieService.h> +#include <nsIDNSRecord.h> +#include <nsIDNSService.h> +#include <nsINetUtil.h> +#include <nsIProxyInfo.h> +#include <nsIProtocolProxyService.h> +#include <nsIScriptSecurityManager.h> +#include <nsIIOService.h> +#include <nsIURI.h> +#include <nsNetCID.h> +#include <nsStringAPI.h> +#include <nsServiceManagerUtils.h> + +// Liveconnect extension + #include "IcedTeaScriptablePluginObject.h" +#include "IcedTeaNPPlugin.h" + +// Debugging macros. +#define PLUGIN_DEBUG(message) \ + g_print ("GCJ PLUGIN: thread %p: %s\n", g_thread_self (), message) + +#define PLUGIN_DEBUG_TWO(first, second) \ + g_print ("GCJ PLUGIN: thread %p: %s %s\n", g_thread_self (), \ + first, second) + +// Error reporting macros. +#define PLUGIN_ERROR(message) \ + g_printerr ("%s:%d: thread %p: Error: %s\n", __FILE__, __LINE__, \ + g_thread_self (), message) + +#define PLUGIN_ERROR_TWO(first, second) \ + g_printerr ("%s:%d: thread %p: Error: %s: %s\n", __FILE__, __LINE__, \ + g_thread_self (), first, second) + +#define PLUGIN_ERROR_THREE(first, second, third) \ + g_printerr ("%s:%d: thread %p: Error: %s: %s: %s\n", __FILE__, \ + __LINE__, g_thread_self (), first, second, third) + +// Plugin information passed to about:plugins. +#define PLUGIN_NAME "IcedTea NPR Web Browser Plugin (using IcedTea)" +#define PLUGIN_DESC "The " PLUGIN_NAME PLUGIN_VERSION " executes Java applets." + +#define PLUGIN_MIME_DESC \ + "application/x-java-vm:class,jar:IcedTea;" \ + "application/x-java-applet:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1.3:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.2.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.2.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.3:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.3.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.4:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.4.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.4.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.5:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.6:class,jar:IcedTea;" \ + "application/x-java-applet;jpi-version=1.6.0_00:class,jar:IcedTea;" \ + "application/x-java-bean:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1.3:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.2.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.2.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.3:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.3.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.4:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.4.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.4.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.5:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.6:class,jar:IcedTea;" \ + "application/x-java-bean;jpi-version=1.6.0_00:class,jar:IcedTea;" \ + "application/x-java-vm-npruntime::IcedTea;" + +#define PLUGIN_URL NS_INLINE_PLUGIN_CONTRACTID_PREFIX NS_JVM_MIME_TYPE +#define PLUGIN_MIME_TYPE "application/x-java-vm" +#define PLUGIN_FILE_EXTS "class,jar,zip" +#define PLUGIN_MIME_COUNT 1 + +#define FAILURE_MESSAGE "gcjwebplugin error: Failed to run %s." \ + " For more detail rerun \"firefox -g\" in a terminal window." + +// Documentbase retrieval required definition. +static NS_DEFINE_IID (kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID); + +// Data directory for plugin. +static gchar* data_directory = NULL; + +// Fully-qualified appletviewer executable. +static gchar* appletviewer_executable = NULL; + +// Applet viewer input channel (needs to be static because it is used in plugin_in_pipe_callback) +static GIOChannel* in_from_appletviewer = NULL; + +// Applet viewer input pipe name. +gchar* in_pipe_name; + +// Applet viewer input watch source. +gint in_watch_source; + +// Applet viewer output pipe name. +gchar* out_pipe_name; + +// Applet viewer output watch source. +gint out_watch_source; + +// Applet viewer output channel. +GIOChannel* out_to_appletviewer; + +// Tracks jvm status +gboolean jvm_up = FALSE; + +// Keeps track of initialization. NP_Initialize should only be +// called once. +gboolean initialized = false; + +// browser functions into mozilla +NPNetscapeFuncs browser_functions; + +// Various message buses carrying information to/from Java, and internally +MessageBus* plugin_to_java_bus = new MessageBus(); +MessageBus* java_to_plugin_bus = new MessageBus(); +MessageBus* internal_bus = new MessageBus(); + +// Processor for plugin requests +PluginRequestProcessor* plugin_req_proc = new PluginRequestProcessor(); + +// Sends messages to Java over the bus +JavaMessageSender* java_req_proc = new JavaMessageSender(); + +GQuark ITNP_PLUGIN_ERROR = g_quark_from_string("IcedTeaNPPlugin"); + +// GCJPluginData stores all the data associated with a single plugin +// instance. A separate plugin instance is created for each <APPLET> +// tag. For now, each plugin instance spawns its own applet viewer +// process but this may need to change if we find pages containing +// multiple applets that expect to be running in the same VM. +struct GCJPluginData +{ + // A unique identifier for this plugin window. + gchar* instance_string; + // Mutex to protect appletviewer_alive. + GMutex* appletviewer_mutex; + // Back-pointer to the plugin instance to which this data belongs. + // This should not be freed but instead simply set to NULL. + NPP owner; + // The address of the plugin window. This should not be freed but + // instead simply set to NULL. + gpointer window_handle; + // The last plugin window width sent to us by the browser. + guint32 window_width; + // The last plugin window height sent to us by the browser. + guint32 window_height; + + // The scriptable plugin object + NPObject* scriptable_plugin_object; +}; + +// Documentbase retrieval type-punning union. +typedef union +{ + void** void_field; + nsIPluginTagInfo2** info_field; +} info_union; + +// Static instance helper functions. +// Have the browser allocate a new GCJPluginData structure. +static void plugin_data_new (GCJPluginData** data); +// Retrieve the current document's documentbase. +static gchar* plugin_get_documentbase (NPP instance); +// Notify the user that the appletviewer is not installed correctly. +static void plugin_display_failure_dialog (); +// Callback used to monitor input pipe status. +static gboolean plugin_in_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data); +// Callback used to monitor output pipe status. +static gboolean plugin_out_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data); +static NPError plugin_start_appletviewer (GCJPluginData* data); +static gchar* plugin_create_applet_tag (int16 argc, char* argn[], + char* argv[]); +static void plugin_stop_appletviewer (); +// Uninitialize GCJPluginData structure +static void plugin_data_destroy (NPP instance); + +NS_IMETHODIMP get_cookie_info(const char* siteAddr, char** cookieString); +void get_proxy_info(const char* siteAddr, char** proxy_scheme, char** proxy_host, char** proxy_port, GError *error); +void decode_url(const gchar* url, gchar** decoded_url); +void consume_message(gchar* message); +void start_jvm_if_needed(); +static void appletviewer_monitor(GPid pid, gint status, gpointer data); + +// Global instance counter. +// Mutex to protect plugin_instance_counter. +static GMutex* plugin_instance_mutex = NULL; +// A global variable for reporting GLib errors. This must be free'd +// and set to NULL after each use. +static GError* channel_error = NULL; + +static GHashTable* instance_to_id_map = g_hash_table_new(NULL, NULL); +static GHashTable* id_to_instance_map = g_hash_table_new(NULL, NULL); +static gint instance_counter = 1; +static GPid appletviewer_pid = -1; +static guint appletviewer_watch_id = -1; + +// Functions prefixed by GCJ_ are instance functions. They are called +// by the browser and operate on instances of GCJPluginData. +// Functions prefixed by plugin_ are static helper functions. +// Functions prefixed by NP_ are factory functions. They are called +// by the browser and provide functionality needed to create plugin +// instances. + +// INSTANCE FUNCTIONS + +// Creates a new gcjwebplugin instance. This function creates a +// GCJPluginData* and stores it in instance->pdata. The following +// GCJPluginData fiels are initialized: instance_string, in_pipe_name, +// in_from_appletviewer, in_watch_source, out_pipe_name, +// out_to_appletviewer, out_watch_source, appletviewer_mutex, owner, +// appletviewer_alive. In addition two pipe files are created. All +// of those fields must be properly destroyed, and the pipes deleted, +// by GCJ_Destroy. If an error occurs during initialization then this +// function will free anything that's been allocated so far, set +// instance->pdata to NULL and return an error code. +NPError +GCJ_New (NPMIMEType pluginType, NPP instance, uint16 mode, + int16 argc, char* argn[], char* argv[], + NPSavedData* saved) +{ + PLUGIN_DEBUG ("GCJ_New"); + + NPError np_error = NPERR_NO_ERROR; + GCJPluginData* data = NULL; + + gchar* documentbase = NULL; + gchar* read_message = NULL; + gchar* applet_tag = NULL; + gchar* tag_message = NULL; + gchar* cookie_info = NULL; + + NPObject* npPluginObj = NULL; + + if (!instance) + { + PLUGIN_ERROR ("Browser-provided instance pointer is NULL."); + np_error = NPERR_INVALID_INSTANCE_ERROR; + goto cleanup_done; + } + + // data + plugin_data_new (&data); + if (data == NULL) + { + PLUGIN_ERROR ("Failed to allocate plugin data."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_done; + } + + // start the jvm if needed + start_jvm_if_needed(); + + // Initialize data->instance_string. + // + // instance_string should be unique for this process so we use a + // combination of getpid and plugin_instance_counter. + // + // Critical region. Reference and increment plugin_instance_counter + // global. + g_mutex_lock (plugin_instance_mutex); + + // data->instance_string + data->instance_string = g_strdup_printf ("%d", + instance_counter); + + g_mutex_unlock (plugin_instance_mutex); + + // data->appletviewer_mutex + data->appletviewer_mutex = g_mutex_new (); + + g_mutex_lock (data->appletviewer_mutex); + + // Documentbase retrieval. + documentbase = plugin_get_documentbase (instance); + if (!documentbase) + { + PLUGIN_ERROR ("Documentbase retrieval failed." + " Browser not Mozilla-based?"); + //goto cleanup_appletviewer_mutex; + } + + // Send applet tag message to appletviewer. + applet_tag = plugin_create_applet_tag (argc, argn, argv); + + tag_message = (gchar*) malloc(strlen(applet_tag)*sizeof(gchar) + 1024); + g_sprintf(tag_message, "instance %d tag %s %s", instance_counter, documentbase, applet_tag); + + //plugin_send_message_to_appletviewer (data, data->instance_string); + plugin_send_message_to_appletviewer (tag_message); + + //send cookie information + char* cookie_string; + if (get_cookie_info(documentbase, &cookie_string) == NS_OK) + { + cookie_info = (gchar*) malloc(sizeof(cookie_string) + 1024); + g_sprintf(cookie_info, "instance %d cookie %s", instance_counter, cookie_string); + } + else + { + cookie_info = (gchar*) malloc(1024); + g_sprintf(cookie_info, "instance %d cookie", instance_counter); + } + + plugin_send_message_to_appletviewer (cookie_info); + + g_mutex_unlock (data->appletviewer_mutex); + + // If initialization succeeded entirely then we store the plugin + // data in the instance structure and return. Otherwise we free the + // data we've allocated so far and set instance->pdata to NULL. + + // Set back-pointer to owner instance. + data->owner = instance; + + printf("Creating scriptable object.."); + npPluginObj = get_scriptable_object(instance); + + data->scriptable_plugin_object = npPluginObj; + + instance->pdata = data; + + java_to_plugin_bus->subscribe(plugin_req_proc); + plugin_to_java_bus->subscribe(java_req_proc); + + goto cleanup_done; + + cleanup_appletviewer_mutex: + g_free (data->appletviewer_mutex); + data->appletviewer_mutex = NULL; + + // cleanup_instance_string: + g_free (data->instance_string); + data->instance_string = NULL; + + // cleanup_data: + // Eliminate back-pointer to plugin instance. + data->owner = NULL; + (*browser_functions.memfree) (data); + data = NULL; + + // Initialization failed so return a NULL pointer for the browser + // data. + instance->pdata = NULL; + + cleanup_done: + g_free (tag_message); + tag_message = NULL; + g_free (applet_tag); + applet_tag = NULL; + g_free (read_message); + read_message = NULL; + g_free (documentbase); + documentbase = NULL; + + // store an identifier for this plugin + g_hash_table_insert(instance_to_id_map, instance, GINT_TO_POINTER(instance_counter)); + g_hash_table_insert(id_to_instance_map, GINT_TO_POINTER(instance_counter), instance); + instance_counter++; + + PLUGIN_DEBUG ("GCJ_New return"); + + return np_error; +} + +// Starts the JVM if it is not already running +void start_jvm_if_needed() +{ + + // This is asynchronized function. It must + // have exclusivity when running. + + GMutex *vm_start_mutex = g_mutex_new(); + g_mutex_lock(vm_start_mutex); + + PLUGIN_DEBUG_0ARG("Checking JVM status...\n"); + + // If the jvm is already up, do nothing + if (jvm_up) + { + PLUGIN_DEBUG_0ARG("JVM is up. Returning.\n"); + return; + } + + PLUGIN_DEBUG_0ARG("No JVM is running. Attempting to start one...\n"); + + NPError np_error = NPERR_NO_ERROR; + GCJPluginData* data = NULL; + + // Create appletviewer-to-plugin pipe which we refer to as the input + // pipe. + + // in_pipe_name + in_pipe_name = g_strdup_printf ("%s/icedteanp-appletviewer-to-plugin", + data_directory); + if (!in_pipe_name) + { + PLUGIN_ERROR ("Failed to create input pipe name."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + // If in_pipe_name is NULL then the g_free at + // cleanup_in_pipe_name will simply return. + goto cleanup_in_pipe_name; + } + + // clean up any older pip + unlink (in_pipe_name); + + PLUGIN_DEBUG_TWO ("GCJ_New: creating input fifo:", in_pipe_name); + if (mkfifo (in_pipe_name, 0700) == -1 && errno != EEXIST) + { + PLUGIN_ERROR_TWO ("Failed to create input pipe", strerror (errno)); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_in_pipe_name; + } + PLUGIN_DEBUG_TWO ("GCJ_New: created input fifo:", in_pipe_name); + + // Create plugin-to-appletviewer pipe which we refer to as the + // output pipe. + + // out_pipe_name + out_pipe_name = g_strdup_printf ("%s/icedteanp-plugin-to-appletviewer", + data_directory); + + if (!out_pipe_name) + { + PLUGIN_ERROR ("Failed to create output pipe name."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_out_pipe_name; + } + + // clean up any older pip + unlink (out_pipe_name); + + PLUGIN_DEBUG_TWO ("GCJ_New: creating output fifo:", out_pipe_name); + if (mkfifo (out_pipe_name, 0700) == -1 && errno != EEXIST) + { + PLUGIN_ERROR_TWO ("Failed to create output pipe", strerror (errno)); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_out_pipe_name; + } + PLUGIN_DEBUG_TWO ("GCJ_New: created output fifo:", out_pipe_name); + + // Start a separate appletviewer process for each applet, even if + // there are multiple applets in the same page. We may need to + // change this behaviour if we find pages with multiple applets that + // rely on being run in the same VM. + + np_error = plugin_start_appletviewer (data); + + // Create plugin-to-appletviewer channel. The default encoding for + // the file is UTF-8. + // out_to_appletviewer + out_to_appletviewer = g_io_channel_new_file (out_pipe_name, + "w", &channel_error); + if (!out_to_appletviewer) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to create output channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to create output channel"); + + np_error = NPERR_GENERIC_ERROR; + goto cleanup_out_to_appletviewer; + } + + // Watch for hangup and error signals on the output pipe. + out_watch_source = + g_io_add_watch (out_to_appletviewer, + (GIOCondition) (G_IO_ERR | G_IO_HUP), + plugin_out_pipe_callback, (gpointer) out_to_appletviewer); + + // Create appletviewer-to-plugin channel. The default encoding for + // the file is UTF-8. + // in_from_appletviewer + in_from_appletviewer = g_io_channel_new_file (in_pipe_name, + "r", &channel_error); + if (!in_from_appletviewer) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to create input channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to create input channel"); + + np_error = NPERR_GENERIC_ERROR; + goto cleanup_in_from_appletviewer; + } + + // Watch for hangup and error signals on the input pipe. + in_watch_source = + g_io_add_watch (in_from_appletviewer, + (GIOCondition) (G_IO_IN | G_IO_ERR | G_IO_HUP), + plugin_in_pipe_callback, (gpointer) in_from_appletviewer); + + jvm_up = TRUE; + + goto done; + + // Free allocated data + + cleanup_in_watch_source: + // Removing a source is harmless if it fails since it just means the + // source has already been removed. + g_source_remove (in_watch_source); + in_watch_source = 0; + + cleanup_in_from_appletviewer: + if (in_from_appletviewer) + g_io_channel_unref (in_from_appletviewer); + in_from_appletviewer = NULL; + + // cleanup_out_watch_source: + g_source_remove (out_watch_source); + out_watch_source = 0; + + cleanup_out_to_appletviewer: + if (out_to_appletviewer) + g_io_channel_unref (out_to_appletviewer); + out_to_appletviewer = NULL; + + // cleanup_out_pipe: + // Delete output pipe. + PLUGIN_DEBUG_TWO ("GCJ_New: deleting input fifo:", in_pipe_name); + unlink (out_pipe_name); + PLUGIN_DEBUG_TWO ("GCJ_New: deleted input fifo:", in_pipe_name); + + cleanup_out_pipe_name: + g_free (out_pipe_name); + out_pipe_name = NULL; + + // cleanup_in_pipe: + // Delete input pipe. + PLUGIN_DEBUG_TWO ("GCJ_New: deleting output fifo:", out_pipe_name); + unlink (in_pipe_name); + PLUGIN_DEBUG_TWO ("GCJ_New: deleted output fifo:", out_pipe_name); + + cleanup_in_pipe_name: + g_free (in_pipe_name); + in_pipe_name = NULL; + + done: + + // Now other threads may re-enter.. unlock the mutex + g_mutex_unlock(vm_start_mutex); + +} + +NPError +GCJ_GetValue (NPP instance, NPPVariable variable, void* value) +{ + PLUGIN_DEBUG ("GCJ_GetValue"); + + NPError np_error = NPERR_NO_ERROR; + + switch (variable) + { + // This plugin needs XEmbed support. + case NPPVpluginNeedsXEmbed: + { + PLUGIN_DEBUG ("GCJ_GetValue: returning TRUE for NeedsXEmbed."); + PRBool* bool_value = (PRBool*) value; + *bool_value = PR_TRUE; + } + break; + case NPPVpluginScriptableNPObject: + { + GCJPluginData *data = (GCJPluginData*) instance->pdata; + *(NPObject **)value = data->scriptable_plugin_object; + } + break; + default: + PLUGIN_ERROR ("Unknown plugin value requested."); + np_error = NPERR_GENERIC_ERROR; + break; + } + + PLUGIN_DEBUG ("GCJ_GetValue return"); + + return np_error; +} + +NPError +GCJ_Destroy (NPP instance, NPSavedData** save) +{ + PLUGIN_DEBUG ("GCJ_Destroy"); + + GCJPluginData* data = (GCJPluginData*) instance->pdata; + + if (data) + { + // Free plugin data. + plugin_data_destroy (instance); + } + + PLUGIN_DEBUG ("GCJ_Destroy return"); + + return NPERR_NO_ERROR; +} + +NPError +GCJ_SetWindow (NPP instance, NPWindow* window) +{ + PLUGIN_DEBUG ("GCJ_SetWindow"); + + if (instance == NULL) + { + PLUGIN_ERROR ("Invalid instance."); + + return NPERR_INVALID_INSTANCE_ERROR; + } + + gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance); + + gint id = 0; + if (id_ptr) + { + id = GPOINTER_TO_INT(id_ptr); + } + + GCJPluginData* data = (GCJPluginData*) instance->pdata; + + // Simply return if we receive a NULL window. + if ((window == NULL) || (window->window == NULL)) + { + PLUGIN_DEBUG ("GCJ_SetWindow: got NULL window."); + + return NPERR_NO_ERROR; + } + + if (data->window_handle) + { + // The window already exists. + if (data->window_handle == window->window) + { + // The parent window is the same as in previous calls. + PLUGIN_DEBUG ("GCJ_SetWindow: window already exists."); + + // Critical region. Read data->appletviewer_mutex and send + // a message to the appletviewer. + g_mutex_lock (data->appletviewer_mutex); + + if (jvm_up) + { + + gboolean dim_changed = FALSE; + + // The window is the same as it was for the last + // SetWindow call. + if (window->width != data->window_width) + { + PLUGIN_DEBUG ("GCJ_SetWindow: window width changed."); + // The width of the plugin window has changed. + + // Store the new width. + data->window_width = window->width; + dim_changed = TRUE; + } + + if (window->height != data->window_height) + { + PLUGIN_DEBUG ("GCJ_SetWindow: window height changed."); + // The height of the plugin window has changed. + + // Store the new height. + data->window_height = window->height; + + dim_changed = TRUE; + } + + if (dim_changed) { + gchar* message = g_strdup_printf ("instance 1 width %d height %d", + window->width, window->height); + plugin_send_message_to_appletviewer (message); + g_free (message); + message = NULL; + } + + + } + else + { + // The appletviewer is not running. + PLUGIN_DEBUG ("GCJ_SetWindow: appletviewer is not running."); + } + + g_mutex_unlock (data->appletviewer_mutex); + } + else + { + // The parent window has changed. This branch does run but + // doing nothing in response seems to be sufficient. + PLUGIN_DEBUG ("GCJ_SetWindow: parent window changed."); + } + } + else + { + PLUGIN_DEBUG ("GCJ_SetWindow: setting window."); + + // Critical region. Send messages to appletviewer. + g_mutex_lock (data->appletviewer_mutex); + + gchar *window_message = g_strdup_printf ("instance %d handle %ld", + id, (gulong) window->window); + plugin_send_message_to_appletviewer (window_message); + g_free (window_message); + + window_message = g_strdup_printf ("instance %d width %d height %d", + id, + window->width, + window->height); + plugin_send_message_to_appletviewer (window_message); + g_free (window_message); + window_message = NULL; + + g_mutex_unlock (data->appletviewer_mutex); + + // Store the window handle. + data->window_handle = window->window; + } + + PLUGIN_DEBUG ("GCJ_SetWindow return"); + + return NPERR_NO_ERROR; +} + +NPError +GCJ_NewStream (NPP instance, NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype) +{ + PLUGIN_DEBUG ("GCJ_NewStream"); + + PLUGIN_DEBUG ("GCJ_NewStream return"); + + return NPERR_NO_ERROR; +} + +void +GCJ_StreamAsFile (NPP instance, NPStream* stream, const char* filename) +{ + PLUGIN_DEBUG ("GCJ_StreamAsFile"); + + PLUGIN_DEBUG ("GCJ_StreamAsFile return"); +} + +NPError +GCJ_DestroyStream (NPP instance, NPStream* stream, NPReason reason) +{ + PLUGIN_DEBUG ("GCJ_DestroyStream"); + + PLUGIN_DEBUG ("GCJ_DestroyStream return"); + + return NPERR_NO_ERROR; +} + +int32 +GCJ_WriteReady (NPP instance, NPStream* stream) +{ + PLUGIN_DEBUG ("GCJ_WriteReady"); + + PLUGIN_DEBUG ("GCJ_WriteReady return"); + + return 0; +} + +int32 +GCJ_Write (NPP instance, NPStream* stream, int32 offset, int32 len, + void* buffer) +{ + PLUGIN_DEBUG ("GCJ_Write"); + + PLUGIN_DEBUG ("GCJ_Write return"); + + return 0; +} + +void +GCJ_Print (NPP instance, NPPrint* platformPrint) +{ + PLUGIN_DEBUG ("GCJ_Print"); + + PLUGIN_DEBUG ("GCJ_Print return"); +} + +int16 +GCJ_HandleEvent (NPP instance, void* event) +{ + PLUGIN_DEBUG ("GCJ_HandleEvent"); + + PLUGIN_DEBUG ("GCJ_HandleEvent return"); + + return 0; +} + +void +GCJ_URLNotify (NPP instance, const char* url, NPReason reason, + void* notifyData) +{ + PLUGIN_DEBUG ("GCJ_URLNotify"); + + PLUGIN_DEBUG ("GCJ_URLNotify return"); +} + +jref +GCJ_GetJavaClass (void) +{ + PLUGIN_DEBUG ("GCJ_GetJavaClass"); + + PLUGIN_DEBUG ("GCJ_GetJavaClass return"); + + return 0; +} + +NS_IMETHODIMP +get_cookie_info(const char* siteAddr, char** cookieString) +{ + + nsresult rv; + nsCOMPtr<nsIScriptSecurityManager> sec_man = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); + + if (!sec_man) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv); + + if (NS_FAILED(rv) || !io_svc) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIURI> uri; + io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); + + nsCOMPtr<nsICookieService> cookie_svc = do_GetService("@mozilla.org/cookieService;1", &rv); + + if (NS_FAILED(rv) || !cookie_svc) { + return NS_ERROR_FAILURE; + } + + rv = cookie_svc->GetCookieString(uri, NULL, cookieString); + + if (NS_FAILED(rv) || !*cookieString) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + + +// HELPER FUNCTIONS + +static void +plugin_data_new (GCJPluginData** data) +{ + PLUGIN_DEBUG ("plugin_data_new"); + + *data = (GCJPluginData*) + (*browser_functions.memalloc) (sizeof (struct GCJPluginData)); + + // appletviewer_alive is false until the applet viewer is spawned. + if (*data) + memset (*data, 0, sizeof (struct GCJPluginData)); + + PLUGIN_DEBUG ("plugin_data_new return"); +} + +// Documentbase retrieval. This function gets the current document's +// documentbase. This function relies on browser-private data so it +// will only work when the plugin is loaded in a Mozilla-based +// browser. We could not find a way to retrieve the documentbase +// using the original Netscape plugin API so we use the XPCOM API +// instead. +static gchar* +plugin_get_documentbase (NPP instance) +{ + PLUGIN_DEBUG ("plugin_get_documentbase"); + + nsIPluginInstance* xpcom_instance = NULL; + nsIPluginInstancePeer* peer = NULL; + nsresult result = 0; + nsIPluginTagInfo2* pluginTagInfo2 = NULL; + info_union u = { NULL }; + char const* documentbase = NULL; + gchar* documentbase_copy = NULL; + + xpcom_instance = (nsIPluginInstance*) (instance->ndata); + if (!xpcom_instance) + { + PLUGIN_ERROR ("xpcom_instance is NULL."); + goto cleanup_done; + } + + xpcom_instance->GetPeer (&peer); + if (!peer) + { + PLUGIN_ERROR ("peer is NULL."); + goto cleanup_done; + } + + u.info_field = &pluginTagInfo2; + + result = peer->QueryInterface (kIPluginTagInfo2IID, + u.void_field); + if (result || !pluginTagInfo2) + { + PLUGIN_ERROR ("pluginTagInfo2 retrieval failed."); + goto cleanup_peer; + } + + pluginTagInfo2->GetDocumentBase (&documentbase); + + if (!documentbase) + { + PLUGIN_ERROR ("documentbase is NULL."); + goto cleanup_plugintaginfo2; + } + + documentbase_copy = g_strdup (documentbase); + + // Release references. + cleanup_plugintaginfo2: + NS_RELEASE (pluginTagInfo2); + + cleanup_peer: + NS_RELEASE (peer); + + cleanup_done: + PLUGIN_DEBUG ("plugin_get_documentbase return"); + + return documentbase_copy; +} + +// This function displays an error message if the appletviewer has not +// been installed correctly. +static void +plugin_display_failure_dialog () +{ + GtkWidget* dialog = NULL; + + PLUGIN_DEBUG ("plugin_display_failure_dialog"); + + dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + FAILURE_MESSAGE, + appletviewer_executable); + gtk_widget_show_all (dialog); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + PLUGIN_DEBUG ("plugin_display_failure_dialog return"); +} + + + +// plugin_in_pipe_callback is called when data is available on the +// input pipe, or when the appletviewer crashes or is killed. It may +// be called after data has been destroyed in which case it simply +// returns FALSE to remove itself from the glib main loop. +static gboolean +plugin_in_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data) +{ + PLUGIN_DEBUG ("plugin_in_pipe_callback"); + + gboolean keep_installed = TRUE; + + if (condition & G_IO_IN) + { + gchar* message = NULL; + + if (g_io_channel_read_line (in_from_appletviewer, + &message, NULL, NULL, + &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to read line from input channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to read line from input channel"); + } else + { + consume_message(message); + } + + g_free (message); + message = NULL; + + keep_installed = TRUE; + } + + if (condition & (G_IO_ERR | G_IO_HUP)) + { + PLUGIN_DEBUG ("appletviewer has stopped."); + keep_installed = FALSE; + } + + PLUGIN_DEBUG ("plugin_in_pipe_callback return"); + + return keep_installed; +} + +void consume_message(gchar* message) { + + g_print (" PIPE: plugin read: %s\n", message); + + if (g_str_has_prefix (message, "instance")) + { + + GCJPluginData* data; + gchar** parts = g_strsplit (message, " ", -1); + guint parts_sz = g_strv_length (parts); + + int instance_id = atoi(parts[1]); + NPP instance = (NPP) g_hash_table_lookup(id_to_instance_map, + GINT_TO_POINTER(instance_id)); + + if (instance_id > 0 && !instance) + { + PLUGIN_DEBUG_2ARG("Instance %d is not active. Refusing to consume message \"%s\"\n", instance_id, message); + return; + } + else if (instance) + { + data = (GCJPluginData*) instance->pdata; + } + + if (g_str_has_prefix (parts[2], "url")) + { + // Open the URL in a new browser window. + gchar* decoded_url = (gchar*) malloc(strlen(parts[3])*sizeof(gchar) + sizeof(gchar)); + decode_url(parts[3], &decoded_url); + + PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback: opening URL", decoded_url); + PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback: URL target", parts[4]); + + NPError np_error = + (*browser_functions.geturl) (data->owner, decoded_url, parts[4]); + + + if (np_error != NPERR_NO_ERROR) + PLUGIN_ERROR ("Failed to load URL."); + + g_free(decoded_url); + decoded_url = NULL; + } + else if (g_str_has_prefix (parts[2], "status")) + { + + // clear the "instance X status" parts + sprintf(parts[0], ""); + sprintf(parts[1], ""); + sprintf(parts[2], ""); + + // join the rest + gchar* status_message = g_strjoinv(" ", parts); + PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback: setting status", status_message); + (*browser_functions.status) (data->owner, status_message); + + g_free(status_message); + status_message = NULL; + } + else if (g_str_has_prefix (parts[1], "internal")) + { + internal_bus->post(message); + } + else + { + // All other messages are posted to the bus, and subscribers are + // expected to take care of them. They better! + + java_to_plugin_bus->post(message); + } + + g_strfreev (parts); + parts = NULL; + } + else if (g_str_has_prefix (message, "context")) + { + java_to_plugin_bus->post(message); + } + else if (g_str_has_prefix (message, "plugin ")) + { + // internal plugin related message + gchar** parts = g_strsplit (message, " ", 3); + if (g_str_has_prefix(parts[1], "PluginProxyInfo")) + { + gchar* proxy_scheme = (gchar*) malloc(sizeof(gchar)*32); + gchar* proxy_host = (gchar*) malloc(sizeof(gchar)*64); + gchar* proxy_port = (gchar*) malloc(sizeof(gchar)*8); + + GError *error = g_error_new(ITNP_PLUGIN_ERROR, 0, ""); + + gchar* decoded_url = (gchar*) malloc(strlen(parts[2])*sizeof(gchar) + sizeof(gchar)); + decode_url(parts[2], &decoded_url); + get_proxy_info(decoded_url, &proxy_scheme, &proxy_host, &proxy_port, error); + + gchar* proxy_info; + proxy_info = g_strconcat ("plugin PluginProxyInfo ", NULL); + if (error->code == 0) + { + proxy_info = g_strconcat (proxy_info, proxy_scheme, " ", proxy_host, " ", proxy_port, NULL); + } + + PLUGIN_DEBUG_1ARG("Proxy info: %s\n", proxy_info); + plugin_send_message_to_appletviewer(proxy_info); + + g_free(decoded_url); + decoded_url = NULL; + g_free(proxy_info); + proxy_info = NULL; + g_free(proxy_scheme); + proxy_scheme = NULL; + g_free(proxy_host); + proxy_host = NULL; + g_free(proxy_port); + proxy_port = NULL; + } + } + else + { + g_print (" Unable to handle message: %s\n", message); + } +} + +void get_instance_from_id(int id, NPP& instance) +{ + instance = (NPP) g_hash_table_lookup(id_to_instance_map, + GINT_TO_POINTER(id)); +} + +int get_id_from_instance(NPP* instance) +{ + return GPOINTER_TO_INT(g_hash_table_lookup(instance_to_id_map, + instance)); +} + +void decode_url(const gchar* url, gchar** decoded_url) +{ + // There is no GLib function to decode urls, so we fallback to Mozilla's + // methods + + nsresult rv; + nsCOMPtr<nsINetUtil> net_util = do_GetService(NS_NETUTIL_CONTRACTID, &rv); + + if (!net_util) + printf("Error instantiating NetUtil service.\n"); + + nsDependentCSubstring unescaped_url; + net_util->UnescapeString(nsCString(url), 0, unescaped_url); + + // no need for strn. decoded_url is malloced to sizeof unescaped_url, which + // will always be <= decoded size + strcpy(*decoded_url, nsCString(unescaped_url).get()); +} + +void get_proxy_info(const char* siteAddr, char** proxy_scheme, char** proxy_host, char** proxy_port, GError *error) +{ + nsresult rv; + + // Initialize service variables + nsCOMPtr<nsIProtocolProxyService> proxy_svc = do_GetService("@mozilla.org/network/protocol-proxy-service;1", &rv); + + if (!proxy_svc) { + printf("Cannot initialize proxy service\n"); + error->code = 1; + return; + } + + nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv); + + if (NS_FAILED(rv) || !io_svc) { + printf("Cannot initialize io service\n"); + error->code = 1; + return; + } + + // uri which needs to be accessed + nsCOMPtr<nsIURI> uri; + io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); + + // find the proxy address if any + nsCOMPtr<nsIProxyInfo> info; + proxy_svc->Resolve(uri, 0, getter_AddRefs(info)); + + // if there is no proxy found, return immediately + if (!info) { + PLUGIN_DEBUG_1ARG("%s does not need a proxy\n", siteAddr); + error->code = 1; + return; + } + + // if proxy info is available, extract it + nsCString phost; + PRInt32 pport; + nsCString ptype; + + info->GetHost(phost); + info->GetPort(&pport); + info->GetType(ptype); + + // resolve the proxy address to an IP + nsCOMPtr<nsIDNSService> dns_svc = do_GetService("@mozilla.org/network/dns-service;1", &rv); + + if (!dns_svc) { + printf("Cannot initialize DNS service\n"); + error->code = 1; + return; + } + + nsCOMPtr<nsIDNSRecord> record; + dns_svc->Resolve(phost, 0U, getter_AddRefs(record)); + + // TODO: Add support for multiple ips + nsDependentCString ipAddr; + record->GetNextAddrAsString(ipAddr); + + // pack information in return variables + snprintf(*proxy_scheme, sizeof(char)*32, "%s", ptype.get()); + snprintf(*proxy_host, sizeof(char)*64, "%s", ipAddr.get()); + snprintf(*proxy_port, sizeof(char)*8, "%d", pport); + + PLUGIN_DEBUG_4ARG("Proxy info for %s: %s %s %s\n", siteAddr, *proxy_scheme, *proxy_host, *proxy_port); +} + +// plugin_out_pipe_callback is called when the appletviewer crashes or +// is killed. It may be called after data has been destroyed in which +// case it simply returns FALSE to remove itself from the glib main +// loop. +static gboolean +plugin_out_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data) +{ + PLUGIN_DEBUG ("plugin_out_pipe_callback"); + + GCJPluginData* data = (GCJPluginData*) plugin_data; + + PLUGIN_DEBUG ("plugin_out_pipe_callback: appletviewer has stopped."); + + PLUGIN_DEBUG ("plugin_out_pipe_callback return"); + + return FALSE; +} + +static NPError +plugin_test_appletviewer () +{ + PLUGIN_DEBUG ("plugin_test_appletviewer"); + NPError error = NPERR_NO_ERROR; + + gchar* command_line[3] = { NULL, NULL, NULL }; + + command_line[0] = g_strdup (appletviewer_executable); + command_line[1] = g_strdup("-version"); + command_line[2] = NULL; + + + if (!g_spawn_async (NULL, command_line, NULL, (GSpawnFlags) 0, + NULL, NULL, NULL, &channel_error)) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to spawn applet viewer", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to spawn applet viewer"); + error = NPERR_GENERIC_ERROR; + } + + g_free (command_line[0]); + command_line[0] = NULL; + g_free (command_line[1]); + command_line[1] = NULL; + g_free (command_line[2]); + command_line[2] = NULL; + + PLUGIN_DEBUG ("plugin_test_appletviewer return"); + return error; +} + +static NPError +plugin_start_appletviewer (GCJPluginData* data) +{ + PLUGIN_DEBUG ("plugin_start_appletviewer"); + NPError error = NPERR_NO_ERROR; + + gchar* command_line[6] = { NULL, NULL, NULL, NULL, NULL, NULL }; + + command_line[0] = g_strdup (appletviewer_executable); + // Output from plugin's perspective is appletviewer's input. + // Input from plugin's perspective is appletviewer's output. + command_line[1] = g_strdup("-Xdebug"); + command_line[2] = g_strdup("-Xnoagent"); + command_line[3] = g_strdup("-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n"); + command_line[4] = g_strdup("sun.applet.PluginMain"); + command_line[5] = NULL; + + if (!g_spawn_async (NULL, command_line, NULL, (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &appletviewer_pid, &channel_error)) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to spawn applet viewer", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to spawn applet viewer"); + error = NPERR_GENERIC_ERROR; + } + + g_free (command_line[0]); + command_line[0] = NULL; + g_free (command_line[1]); + command_line[1] = NULL; + g_free (command_line[2]); + command_line[2] = NULL; + g_free (command_line[3]); + command_line[3] = NULL; + g_free (command_line[4]); + command_line[4] = NULL; + g_free (command_line[5]); + command_line[5] = NULL; + + if (appletviewer_pid) + { + PLUGIN_DEBUG_1ARG("Initialized VM with pid=%d\n", appletviewer_pid); + appletviewer_watch_id = g_child_watch_add(appletviewer_pid, (GChildWatchFunc) appletviewer_monitor, (gpointer) appletviewer_pid); + } + + + PLUGIN_DEBUG ("plugin_start_appletviewer return"); + return error; +} + +// Build up the applet tag string that we'll send to the applet +// viewer. +static gchar* +plugin_create_applet_tag (int16 argc, char* argn[], char* argv[]) +{ + PLUGIN_DEBUG ("plugin_create_applet_tag"); + + gchar* applet_tag = g_strdup ("<EMBED "); + gchar* parameters = g_strdup (""); + + for (int16 i = 0; i < argc; i++) + { + if (!g_ascii_strcasecmp (argn[i], "code")) + { + gchar* code = g_strdup_printf ("CODE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, code, NULL); + g_free (code); + code = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "codebase")) + { + gchar* codebase = g_strdup_printf ("CODEBASE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, codebase, NULL); + g_free (codebase); + codebase = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "classid")) + { + gchar* classid = g_strdup_printf ("CLASSID=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, classid, NULL); + g_free (classid); + classid = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "archive")) + { + gchar* archive = g_strdup_printf ("ARCHIVE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, archive, NULL); + g_free (archive); + archive = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "width")) + { + gchar* width = g_strdup_printf ("width=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, width, NULL); + g_free (width); + width = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "height")) + { + gchar* height = g_strdup_printf ("height=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, height, NULL); + g_free (height); + height = NULL; + } + else + { + // Escape the parameter value so that line termination + // characters will pass through the pipe. + if (argv[i] != '\0') + { + gchar* escaped = NULL; + + escaped = g_strescape (argv[i], NULL); + parameters = g_strconcat (parameters, "<PARAM NAME=\"", argn[i], + "\" VALUE=\"", escaped, "\">", NULL); + + g_free (escaped); + escaped = NULL; + } + } + } + + applet_tag = g_strconcat (applet_tag, ">", parameters, "</EMBED>", NULL); + + g_free (parameters); + parameters = NULL; + + PLUGIN_DEBUG ("plugin_create_applet_tag return"); + + return applet_tag; +} + +// plugin_send_message_to_appletviewer must be called while holding +// data->appletviewer_mutex. +void +plugin_send_message_to_appletviewer (gchar const* message) +{ + PLUGIN_DEBUG ("plugin_send_message_to_appletviewer"); + + if (jvm_up) + { + gchar* newline_message = NULL; + gsize bytes_written = 0; + + // Send message to appletviewer. + newline_message = g_strdup_printf ("%s\n", message); + + // g_io_channel_write_chars will return something other than + // G_IO_STATUS_NORMAL if not all the data is written. In that + // case we fail rather than retrying. + if (g_io_channel_write_chars (out_to_appletviewer, + newline_message, -1, &bytes_written, + &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to write bytes to output channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to write bytes to output channel"); + } + + if (g_io_channel_flush (out_to_appletviewer, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to flush bytes to output channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to flush bytes to output channel"); + } + g_free (newline_message); + newline_message = NULL; + + g_print (" PIPE: plugin wrote: %s\n", message); + } + + PLUGIN_DEBUG ("plugin_send_message_to_appletviewer return"); +} + +// Stop the appletviewer process. When this is called the +// appletviewer can be in any of three states: running, crashed or +// hung. If the appletviewer is running then sending it "shutdown" +// will cause it to exit. This will cause +// plugin_out_pipe_callback/plugin_in_pipe_callback to be called and +// the input and output channels to be shut down. If the appletviewer +// has crashed then plugin_out_pipe_callback/plugin_in_pipe_callback +// would already have been called and data->appletviewer_alive cleared +// in which case this function simply returns. If the appletviewer is +// hung then this function will be successful and the input and output +// watches will be removed by plugin_data_destroy. +// plugin_stop_appletviewer must be called with +// data->appletviewer_mutex held. +static void +plugin_stop_appletviewer () +{ + PLUGIN_DEBUG ("plugin_stop_appletviewer"); + + if (jvm_up) + { + // Shut down the appletviewer. + gsize bytes_written = 0; + + if (out_to_appletviewer) + { + if (g_io_channel_write_chars (out_to_appletviewer, "shutdown", + -1, &bytes_written, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to write shutdown message to" + " appletviewer", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to write shutdown message to"); + } + + if (g_io_channel_flush (out_to_appletviewer, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to write shutdown message to" + " appletviewer", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to write shutdown message to"); + } + + if (g_io_channel_shutdown (out_to_appletviewer, + TRUE, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to shut down appletviewer" + " output channel", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to shut down appletviewer"); + } + } + + if (in_from_appletviewer) + { + if (g_io_channel_shutdown (in_from_appletviewer, + TRUE, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to shut down appletviewer" + " input channel", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to shut down appletviewer"); + } + } + } + + jvm_up = FALSE; + sleep(2); /* Needed to prevent crashes during debug (when JDWP port is not freed by the kernel right away) */ + + PLUGIN_DEBUG ("plugin_stop_appletviewer return"); +} + +static void appletviewer_monitor(GPid pid, gint status, gpointer data) +{ + PLUGIN_DEBUG ("appletviewer_monitor"); + jvm_up = FALSE; + pid = -1; + PLUGIN_DEBUG ("appletviewer_monitor return"); +} + +static void +plugin_data_destroy (NPP instance) +{ + PLUGIN_DEBUG ("plugin_data_destroy"); + + GCJPluginData* tofree = (GCJPluginData*) instance->pdata; + + // Remove instance from map + gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance); + + if (id_ptr) + { + gint id = GPOINTER_TO_INT(id_ptr); + g_hash_table_remove(instance_to_id_map, instance); + g_hash_table_remove(id_to_instance_map, id_ptr); + } + + tofree->window_handle = NULL; + tofree->window_height = 0; + tofree->window_width = 0; + + // cleanup_appletviewer_mutex: + g_free (tofree->appletviewer_mutex); + tofree->appletviewer_mutex = NULL; + + // cleanup_instance_string: + g_free (tofree->instance_string); + tofree->instance_string = NULL; + + // cleanup_data: + // Eliminate back-pointer to plugin instance. + tofree->owner = NULL; + (*browser_functions.memfree) (tofree); + tofree = NULL; + + PLUGIN_DEBUG ("plugin_data_destroy return"); +} + +// FACTORY FUNCTIONS + +// Provides the browser with pointers to the plugin functions that we +// implement and initializes a local table with browser functions that +// we may wish to call. Called once, after browser startup and before +// the first plugin instance is created. +// The field 'initialized' is set to true once this function has +// finished. If 'initialized' is already true at the beginning of +// this function, then it is evident that NP_Initialize has already +// been called. There is no need to call this function more than once and +// this workaround avoids any duplicate calls. +NPError +NP_Initialize (NPNetscapeFuncs* browserTable, NPPluginFuncs* pluginTable) +{ + PLUGIN_DEBUG ("NP_Initialize"); + + if (initialized) + return NPERR_NO_ERROR; + else if ((browserTable == NULL) || (pluginTable == NULL)) + { + PLUGIN_ERROR ("Browser or plugin function table is NULL."); + + return NPERR_INVALID_FUNCTABLE_ERROR; + } + + // Ensure that the major version of the plugin API that the browser + // expects is not more recent than the major version of the API that + // we've implemented. + if ((browserTable->version >> 8) > NP_VERSION_MAJOR) + { + PLUGIN_ERROR ("Incompatible version."); + + return NPERR_INCOMPATIBLE_VERSION_ERROR; + } + + // Ensure that the plugin function table we've received is large + // enough to store the number of functions that we may provide. + if (pluginTable->size < sizeof (NPPluginFuncs)) + { + PLUGIN_ERROR ("Invalid plugin function table."); + + return NPERR_INVALID_FUNCTABLE_ERROR; + } + + // Ensure that the browser function table is large enough to store + // the number of browser functions that we may use. + if (browserTable->size < sizeof (NPNetscapeFuncs)) + { + PLUGIN_ERROR ("Invalid browser function table."); + + return NPERR_INVALID_FUNCTABLE_ERROR; + } + + // Store in a local table the browser functions that we may use. + browser_functions.size = browserTable->size; + browser_functions.version = browserTable->version; + browser_functions.geturlnotify = browserTable->geturlnotify; + browser_functions.geturl = browserTable->geturl; + browser_functions.posturlnotify = browserTable->posturlnotify; + browser_functions.posturl = browserTable->posturl; + browser_functions.requestread = browserTable->requestread; + browser_functions.newstream = browserTable->newstream; + browser_functions.write = browserTable->write; + browser_functions.destroystream = browserTable->destroystream; + browser_functions.status = browserTable->status; + browser_functions.uagent = browserTable->uagent; + browser_functions.memalloc = browserTable->memalloc; + browser_functions.memfree = browserTable->memfree; + browser_functions.memflush = browserTable->memflush; + browser_functions.reloadplugins = browserTable->reloadplugins; + browser_functions.getJavaEnv = browserTable->getJavaEnv; + browser_functions.getJavaPeer = browserTable->getJavaPeer; + browser_functions.getvalue = browserTable->getvalue; + browser_functions.setvalue = browserTable->setvalue; + browser_functions.invalidaterect = browserTable->invalidaterect; + browser_functions.invalidateregion = browserTable->invalidateregion; + browser_functions.forceredraw = browserTable->forceredraw; + browser_functions.getstringidentifier = browserTable->getstringidentifier; + browser_functions.getstringidentifiers = browserTable->getstringidentifiers; + browser_functions.getintidentifier = browserTable->getintidentifier; + browser_functions.identifierisstring = browserTable->identifierisstring; + browser_functions.utf8fromidentifier = browserTable->utf8fromidentifier; + browser_functions.intfromidentifier = browserTable->intfromidentifier; + browser_functions.createobject = browserTable->createobject; + browser_functions.retainobject = browserTable->retainobject; + browser_functions.releaseobject = browserTable->releaseobject; + browser_functions.invoke = browserTable->invoke; + browser_functions.invokeDefault = browserTable->invokeDefault; + browser_functions.evaluate = browserTable->evaluate; + browser_functions.getproperty = browserTable->getproperty; + browser_functions.setproperty = browserTable->setproperty; + browser_functions.removeproperty = browserTable->removeproperty; + browser_functions.hasproperty = browserTable->hasproperty; + browser_functions.hasmethod = browserTable->hasmethod; + browser_functions.releasevariantvalue = browserTable->releasevariantvalue; + browser_functions.setexception = browserTable->setexception; + + // Return to the browser the plugin functions that we implement. + pluginTable->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; + pluginTable->size = sizeof (NPPluginFuncs); + pluginTable->newp = NewNPP_NewProc (GCJ_New); + pluginTable->destroy = NewNPP_DestroyProc (GCJ_Destroy); + pluginTable->setwindow = NewNPP_SetWindowProc (GCJ_SetWindow); + pluginTable->newstream = NewNPP_NewStreamProc (GCJ_NewStream); + pluginTable->destroystream = NewNPP_DestroyStreamProc (GCJ_DestroyStream); + pluginTable->asfile = NewNPP_StreamAsFileProc (GCJ_StreamAsFile); + pluginTable->writeready = NewNPP_WriteReadyProc (GCJ_WriteReady); + pluginTable->write = NewNPP_WriteProc (GCJ_Write); + pluginTable->print = NewNPP_PrintProc (GCJ_Print); + pluginTable->urlnotify = NewNPP_URLNotifyProc (GCJ_URLNotify); + pluginTable->getvalue = NewNPP_GetValueProc (GCJ_GetValue); + + // Make sure the plugin data directory exists, creating it if + // necessary. + data_directory = g_strconcat (getenv ("HOME"), "/.icedteaplugin", NULL); + if (!data_directory) + { + PLUGIN_ERROR ("Failed to create data directory name."); + return NPERR_OUT_OF_MEMORY_ERROR; + } + NPError np_error = NPERR_NO_ERROR; + gchar* filename = NULL; + if (!g_file_test (data_directory, + (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) + { + int file_error = 0; + + file_error = g_mkdir (data_directory, 0700); + if (file_error != 0) + { + PLUGIN_ERROR_THREE ("Failed to create data directory", + data_directory, + strerror (errno)); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_data_directory; + } + } + + // Set appletviewer_executable. + Dl_info info; + if (dladdr ((const void*) GCJ_New, &info) == 0) + { + PLUGIN_ERROR_TWO ("Failed to determine plugin shared object filename", + dlerror ()); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_data_directory; + } + filename = g_strdup (info.dli_fname); + if (!filename) + { + PLUGIN_ERROR ("Failed to create plugin shared object filename."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_data_directory; + } + appletviewer_executable = g_strdup_printf ("%s/../../bin/java", + dirname (filename)); + if (!appletviewer_executable) + { + PLUGIN_ERROR ("Failed to create appletviewer executable name."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_filename; + } + + np_error = plugin_test_appletviewer (); + if (np_error != NPERR_NO_ERROR) + { + plugin_display_failure_dialog (); + goto cleanup_appletviewer_executable; + } + g_free (filename); + + initialized = true; + + // Initialize threads (needed for mutexes). + if (!g_thread_supported ()) + g_thread_init (NULL); + + plugin_instance_mutex = g_mutex_new (); + + PLUGIN_DEBUG_TWO ("NP_Initialize: using", appletviewer_executable); + + PLUGIN_DEBUG ("NP_Initialize return"); + + return NPERR_NO_ERROR; + + cleanup_appletviewer_executable: + if (appletviewer_executable) + { + g_free (appletviewer_executable); + appletviewer_executable = NULL; + } + + cleanup_filename: + if (filename) + { + g_free (filename); + filename = NULL; + } + + cleanup_data_directory: + if (data_directory) + { + g_free (data_directory); + data_directory = NULL; + } + + return np_error; +} + +// Returns a string describing the MIME type that this plugin +// handles. +char* +NP_GetMIMEDescription (void) +{ + PLUGIN_DEBUG ("NP_GetMIMEDescription"); + + PLUGIN_DEBUG ("NP_GetMIMEDescription return"); + + return (char*) PLUGIN_MIME_DESC; +} + +// Returns a value relevant to the plugin as a whole. The browser +// calls this function to obtain information about the plugin. +NPError +NP_GetValue (void* future, NPPVariable variable, void* value) +{ + PLUGIN_DEBUG ("NP_GetValue"); + + NPError result = NPERR_NO_ERROR; + gchar** char_value = (gchar**) value; + + switch (variable) + { + case NPPVpluginNameString: + PLUGIN_DEBUG ("NP_GetValue: returning plugin name."); + *char_value = g_strdup (PLUGIN_NAME " " PACKAGE_VERSION); + break; + + case NPPVpluginDescriptionString: + PLUGIN_DEBUG ("NP_GetValue: returning plugin description."); + *char_value = g_strdup (PLUGIN_DESC); + break; + + default: + PLUGIN_ERROR ("Unknown plugin value requested."); + result = NPERR_GENERIC_ERROR; + break; + } + + PLUGIN_DEBUG ("NP_GetValue return"); + + return result; +} + +// Shuts down the plugin. Called after the last plugin instance is +// destroyed. +NPError +NP_Shutdown (void) +{ + PLUGIN_DEBUG ("NP_Shutdown"); + + // Free mutex. + if (plugin_instance_mutex) + { + g_mutex_free (plugin_instance_mutex); + plugin_instance_mutex = NULL; + } + + if (data_directory) + { + g_free (data_directory); + data_directory = NULL; + } + + if (appletviewer_executable) + { + g_free (appletviewer_executable); + appletviewer_executable = NULL; + } + + // stop the appletviewer + plugin_stop_appletviewer(); + + // remove monitor + if (appletviewer_watch_id != -1) + g_source_remove(appletviewer_watch_id); + + // Removing a source is harmless if it fails since it just means the + // source has already been removed. + g_source_remove (in_watch_source); + in_watch_source = 0; + + // cleanup_in_from_appletviewer: + if (in_from_appletviewer) + g_io_channel_unref (in_from_appletviewer); + in_from_appletviewer = NULL; + + // cleanup_out_watch_source: + g_source_remove (out_watch_source); + out_watch_source = 0; + + // cleanup_out_to_appletviewer: + if (out_to_appletviewer) + g_io_channel_unref (out_to_appletviewer); + out_to_appletviewer = NULL; + + // cleanup_out_pipe: + // Delete output pipe. + PLUGIN_DEBUG_TWO ("NP_Shutdown: deleting output fifo:", out_pipe_name); + unlink (out_pipe_name); + PLUGIN_DEBUG_TWO ("NP_Shutdown: deleted output fifo:", out_pipe_name); + + // cleanup_out_pipe_name: + g_free (out_pipe_name); + out_pipe_name = NULL; + + // cleanup_in_pipe: + // Delete input pipe. + PLUGIN_DEBUG_TWO ("NP_Shutdown: deleting input fifo:", in_pipe_name); + unlink (in_pipe_name); + PLUGIN_DEBUG_TWO ("NP_Shutdown: deleted input fifo:", in_pipe_name); + + // cleanup_in_pipe_name: + g_free (in_pipe_name); + in_pipe_name = NULL; + + initialized = false; + + PLUGIN_DEBUG ("NP_Shutdown return"); + + return NPERR_NO_ERROR; +} + +NPObject* +get_scriptable_object(NPP instance) +{ + + NPObject* scriptable_object; + + NPClass* np_class = new NPClass(); + np_class->structVersion = NP_CLASS_STRUCT_VERSION; + np_class->allocate = allocate_scriptable_object; + np_class->deallocate = IcedTeaScriptablePluginObject::deAllocate; + np_class->invalidate = IcedTeaScriptablePluginObject::invalidate; + np_class->hasMethod = IcedTeaScriptablePluginObject::hasMethod; + np_class->invoke = IcedTeaScriptablePluginObject::invoke; + np_class->invokeDefault = IcedTeaScriptablePluginObject::invokeDefault; + np_class->hasProperty = IcedTeaScriptablePluginObject::hasProperty; + np_class->getProperty = IcedTeaScriptablePluginObject::getProperty; + np_class->setProperty = IcedTeaScriptablePluginObject::setProperty; + np_class->removeProperty = IcedTeaScriptablePluginObject::removeProperty; + np_class->enumerate = IcedTeaScriptablePluginObject::enumerate; + np_class->construct = IcedTeaScriptablePluginObject::construct; + + scriptable_object = browser_functions.createobject(instance, np_class); + PLUGIN_DEBUG_1ARG("Returning new scriptable class: %p\n", scriptable_object); + + return scriptable_object; +} + +NPObject* +allocate_scriptable_object(NPP npp, NPClass *aClass) +{ + PLUGIN_DEBUG_0ARG("Allocating new scriptable object\n"); + return new IcedTeaScriptablePluginObject(npp); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/IcedTeaNPPlugin.h Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,90 @@ +/* IcedTeaNPPlugin.h + + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea 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, or (at your option) +any later version. + +IcedTea 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 IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +#ifndef __ICEDTEANPPLUGIN_H__ +#define __ICEDTEANPPLUGIN_H__ + +// Netscape plugin API includes. +#include <npapi.h> +#include <npupp.h> +#include <nsThreadUtils.h> + +// GLib includes. +#include <glib.h> +#include <glib/gstdio.h> + +// GTK includes. +#include <gtk/gtk.h> + +#include "IcedTeaPluginUtils.h" +#include "IcedTeaPluginRequestProcessor.h" + +// Browser function table. +extern NPNetscapeFuncs browser_functions; + +// messages to the java side +extern MessageBus* plugin_to_java_bus; + +// messages from the java side +extern MessageBus* java_to_plugin_bus; + +// internal messages (e.g ones that need processing in main thread) +extern MessageBus* internal_bus; + +// subscribes to plugin_to_java_bus and sends messages over the link +extern JavaMessageSender java_request_processor; + +// processes requests made to the plugin +extern PluginRequestProcessor plugin_request_processor; + +/* Given an instance pointer, return its id */ +void get_instance_from_id(int id, NPP& instance); + +/* Given an instance id, return its pointer */ +int get_id_from_instance(NPP* instance); + +/* Sends a message to the appletviewer */ +void plugin_send_message_to_appletviewer (gchar const* message); + +/* Returns a scriptable npobject */ +NPObject* get_scriptable_object(NPP instance); + +/* Creates a new scriptable plugin object and returns it */ +NPObject* allocate_scriptable_object(NPP npp, NPClass *aClass); + +#endif /* __ICEDTEANPPLUGIN_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/IcedTeaPluginRequestProcessor.cc Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,420 @@ +/* IcedTeaPluginRequestProcessor.cc + + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea 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, or (at your option) +any later version. + +IcedTea 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 IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +#include "IcedTeaNPPlugin.h" +#include "IcedTeaPluginRequestProcessor.h" + +/* + * This class processes requests made by Java. The requests include pointer + * information, script execution and variable get/set + */ + +/** + * Given the window pointer, returns the instance associated with it + * + * @param member_ptr The pointer key + * @return The associated instance + */ + +NPP +getInstanceFromMemberPtr(void* member_ptr) +{ + + NPP instance = NULL; + PLUGIN_DEBUG_1ARG("getInstanceFromMemberPtr looking for %p\n", member_ptr); + + std::map<void*, NPP>::iterator iterator = instance_map->find(member_ptr); + + if (iterator != instance_map->end()) + { + instance = instance_map->find(member_ptr)->second; + PLUGIN_DEBUG_2ARG("getInstanceFromMemberPtr found %p. Instance = %p\n", member_ptr, instance); + } + + return instance; +} + +/** + * Stores a window pointer <-> instance mapping + * + * @param member_ptr The pointer key + * @param instance The instance to associate with this pointer + */ + +void +storeInstanceID(void* member_ptr, NPP instance) +{ + PLUGIN_DEBUG_2ARG("Storing instance %p with key %p\n", instance, member_ptr); + instance_map->insert(std::make_pair(member_ptr, instance)); +} + +/** + * PluginRequestProcessor constructor. + * + * Initializes various complex data structures used by the class. + */ + +PluginRequestProcessor::PluginRequestProcessor() +{ + this->pendingRequests = new std::map<pthread_t, uintmax_t>(); + instance_map = new std::map<void*, NPP>(); +} + +/** + * PluginRequestProcessor destructor. + * + * Frees memory used by complex objects. + */ + +PluginRequestProcessor::~PluginRequestProcessor() +{ + if (pendingRequests) + delete pendingRequests; + + if (instance_map) + delete instance_map; + +} + +/** + * Processes plugin (C++ side) requests from the Java side, and internally. + * + * @param message The message request to process + * @return boolean indicating whether the message is serviceable by this object + */ + +bool +PluginRequestProcessor::newMessageOnBus(const char* message) +{ + PLUGIN_DEBUG_1ARG("PluginRequestProcessor processing %s\n", message); + + std::string type; + std::string command; + int counter = 0; + + std::vector<std::string>* message_parts = IcedTeaPluginUtilities::strSplit(message, " "); + + std::vector<std::string>::iterator the_iterator; + the_iterator = message_parts->begin(); + + IcedTeaPluginUtilities::printStringVector("PluginRequestProcessor::newMessageOnBus:", message_parts); + + // Prioritize internal requests + if (message_parts->at(0) == "internal") + { + if (message_parts->at(1) == "SendMember") + { + // first item is length, and it is radix 10 + int length = strtol(message_parts->at(3).c_str(), NULL, 10); + + std::vector<std::string*>* send_string_req = new std::vector<std::string*>(); + std::string* member_name = new std::string(); + + // pack parent id and member name + send_string_req->push_back(&(message_parts->at(0))); + IcedTeaPluginUtilities::getUTF8String(length, 4 /* start at */, message_parts, member_name); + send_string_req->push_back(member_name); + + // make the internal request + this->_sendMember(send_string_req); + + // free the memory + delete send_string_req; + delete message_parts; + + return true; // request processed + } + } + + type = message_parts->at(0); + command = message_parts->at(2); + + if (type == "instance") + { + if (command == "GetWindow") + { + // Window can be queried from the main thread only. And this call + // returns immediately, so we do it in the same thread. + this->sendWindow(message_parts); + return true; + } else if (command == "GetMember") + { + this->dispatch(&sendMember, message_parts, NULL); + return true; + } + } + + // If we got here, it means we couldn't process the message. Let the caller know. + return false; +} + +/** + * Delegates further processing to a new thread. + * + * @param func_ptr Pointer to the function to delegate this request to + * @param message_parts The full message, as a vector of strings + * @param src The source URL of the request (may be NULL) + * + * The functions called by dispatch() are started in a new thread, and provided + * a "ThreadData" struct. Since timings are not guaranteed, the struct is + * allocated on the heap and it is upto the called functions to free the + * associated memory when it is safe to do so. + */ + +void PluginRequestProcessor::dispatch(void* func_ptr (void*), std::vector<std::string>* message_parts, std::string* src) +{ + pthread_t thread; + ThreadData* tdata = new ThreadData(); + + IcedTeaPluginUtilities::printStringVector("PluginRequestProcessor::dispatch:", message_parts); + + tdata->source = src; + tdata->message_parts = message_parts; + + pthread_create (&thread, NULL, func_ptr, (void*) tdata); + uintmax_t start_time = (uintmax_t) time(NULL); +} + +/** + * Sends the window pointer to the Java side. + * + * @param message_parts The request message. + */ + +void +PluginRequestProcessor::sendWindow(std::vector<std::string>* message_parts) +{ + std::string type; + std::string command; + std::string* response; + std::string* window_ptr_str; + int id; + + type = message_parts->at(0); + id = atoi(message_parts->at(1).c_str()); + command = message_parts->at(2); + + NPP instance; + get_instance_from_id(id, instance); + + static NPObject *window_ptr; + browser_functions.getvalue(instance, NPNVWindowNPObject, &window_ptr); + PLUGIN_DEBUG_3ARG("ID=%d, Instance=%p, WindowPTR = %p\n", id, instance, window_ptr); + + window_ptr_str = IcedTeaPluginUtilities::JSIDToString(window_ptr); + + // We need the context 0 for backwards compatibility with the Java side + response = IcedTeaPluginUtilities::constructMessagePrefix(0); + *response += " JavaScriptGetWindow "; + *response += *window_ptr_str; + + plugin_to_java_bus->post(response->c_str()); + + delete response; + delete window_ptr_str; + delete message_parts; + + // store the instance pointer for future reference + storeInstanceID(window_ptr, instance); +} + +/** + * Sends request member pointer to the Java side. + * + * This is a static function, called in another thread. Since certain data + * can only be requested from the main thread in Mozilla, this function + * does whatever it can seperately, and then makes an internal request that + * causes _sendMember to do the rest of the work. + * + * @param tdata A ThreadData structure holding information needed by this function. + */ + +void* +sendMember(void* tdata) +{ + // member initialization + std::vector<std::string>* message_parts; + std::vector<std::string>* compound_data; + JavaRequestProcessor* java_request; + JavaRequest* java_request_data; + std::string* member_id = new std::string(); + std::string* parent_id = new std::string(); + std::string* member_name_utf = new std::string(); + std::string* internal_request = new std::string(); + int id; + + /** Data extraction **/ + + // extract data passed from parent thread + ThreadData *data; + data = (ThreadData*) tdata; + message_parts = data->message_parts; + + // debug printout of parent thread data + IcedTeaPluginUtilities::printStringVector("PluginRequestProcessor::getMember:", message_parts); + + // store info in local variables for easy access + id = atoi(message_parts->at(1).c_str()); + *parent_id += message_parts->at(3); + *member_id += message_parts->at(4); + + /** Request data from Java **/ + + // create a compound request data structure to pass to getString + compound_data = new std::vector<std::string>(); + compound_data->push_back(*member_id); + + // make a new request for getString, to get the name of the identifier + java_request = new JavaRequestProcessor(); + java_request_data = new JavaRequest(); + java_request_data->instance = id; + java_request_data->data = compound_data; + java_request_data->source = data->source != NULL ? data->source : new std::string("file://"); + JavaResultData* result = java_request->getString(java_request_data); + + // the result we want is in result_string (assuming there was no error) + if (result->error_occured) + { + printf("Unable to process getMember request. Error occurred: %s\n", result->error_msg); + goto cleanup; + } + + /** Make an internal request for the main thread to handle**/ + IcedTeaPluginUtilities::convertStringToUTF8(result->return_string, member_name_utf); + + // Ask main thread to do the send + *internal_request = "internal SendMember "; + *internal_request += *parent_id; + *internal_request += " "; + *internal_request += *member_name_utf; + + java_to_plugin_bus->post(internal_request->c_str()); + + // Now be a good citizen and help keep the heap free of garbage + cleanup: + delete data; // thread data that caller allocated for us + delete java_request; // request object + delete java_request_data; // request data + delete member_id; // member id string + delete compound_data; // compound data object + delete message_parts; // message_parts vector that was allocated by the caller + delete internal_request; // delete the string that held the internal request data +} + +/** + * Given the parent id and the member name, sends the member pointer to Java. + * + * @param message_parts Vector containing the parent pointer and member name + */ + +void +PluginRequestProcessor::_sendMember(std::vector<std::string*>* message_parts) +{ + std::string* member_ptr_str; + std::string* member; + std::string* parent; + + NPObject *window_ptr; + NPVariant member_ptr; + NPP instance; + NPIdentifier identifier; + std::string* response; + + IcedTeaPluginUtilities::printStringPtrVector(" - ", message_parts); + + parent = message_parts->at(0); + member = message_parts->at(1); + + std::cout << "MEMBER=" << *member << std::endl; + + // Get the corresponding windowId + identifier = browser_functions.getstringidentifier(member->c_str()); + + // Get the window pointer + window_ptr = reinterpret_cast <NPObject*> (IcedTeaPluginUtilities::stringToJSID(parent->c_str())); + + // Get the associated instance + instance = getInstanceFromMemberPtr(window_ptr); + + printf("[2] Instance=%p, window=%p, identifier=%p -- %s::%s\n", instance, window_ptr, identifier, parent->c_str(), member->c_str()); + + // Get the NPVariant corresponding to this member + browser_functions.getproperty(instance, window_ptr, identifier, &member_ptr); + + PLUGIN_DEBUG_4ARG("[3] Instance=%p, window=%p, identifier=%p, variant=%p\n", instance, window_ptr, identifier, &member_ptr); + + if (NPVARIANT_IS_VOID(member_ptr)) + { + printf("VOID %d\n", member_ptr); + } + else if (NPVARIANT_IS_NULL(member_ptr)) + { + printf("NULL\n", member_ptr); + } + else if (NPVARIANT_IS_BOOLEAN(member_ptr)) + { + printf("BOOL: %d\n", NPVARIANT_TO_BOOLEAN(member_ptr)); + } + else if (NPVARIANT_IS_INT32(member_ptr)) + { + printf("INT32: %d\n", NPVARIANT_TO_INT32(member_ptr)); + } + else if (NPVARIANT_IS_DOUBLE(member_ptr)) + { + printf("DOUBLE: %f\n", NPVARIANT_TO_DOUBLE(member_ptr)); + } + else if (NPVARIANT_IS_STRING(member_ptr)) + { + printf("STRING: %s\n", NPVARIANT_TO_STRING(member_ptr).utf8characters); + } + else + { + printf("OBJ: %p\n", NPVARIANT_TO_OBJECT(member_ptr)); + } + + member_ptr_str = IcedTeaPluginUtilities::JSIDToString(&member_ptr); + + response = IcedTeaPluginUtilities::constructMessagePrefix(0); + *response += " JavaScriptGetMember "; + *response += *member_ptr_str; + + plugin_to_java_bus->post(response->c_str()); + + delete member_ptr_str; + delete response; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/IcedTeaPluginRequestProcessor.h Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,108 @@ +/* IcedTeaPluginRequestProcessor.h + + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea 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, or (at your option) +any later version. + +IcedTea 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 IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +#ifndef __ICEDTEAPLUGINREQUESTPROCESSOR_H__ +#define __ICEDTEAPLUGINREQUESTPROCESSOR_H__ + +#include <map> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <time.h> + +#include <npapi.h> +#include <npupp.h> + +#include "IcedTeaPluginUtils.h" +#include "IcedTeaJavaRequestProcessor.h" + +/** + * Data structure passed to functions called in a new thread. + */ + +typedef struct struct_thread_data +{ + std::vector<std::string>* message_parts; + std::string* source; +} ThreadData; + +/* Map holding window pointer<->instance relationships */ +static std::map<void*, NPP>* instance_map; + +// JS request processor methods +static NPP getInstanceFromMemberPtr(void* member_ptr); +static void storeInstanceID(void* member_ptr, NPP instance); +static void* requestFromMainThread(); +static void* sendMember(void* tdata); +static void* setMember(void* tdata); +static void* getSlot(void* tdata); +static void* setSlot(void* tdata); +static void* eval(void* tdata); +static void* removeMember(void* tdata); +static void* call(void* tdata); +static void* finalize(void* tdata); +static void* toString(void* tdata); + +/** + * Processes requests made TO the plugin (by java or anyone else) + */ +class PluginRequestProcessor : public BusSubscriber +{ + private: + + /* Requests that are still pending */ + std::map<pthread_t, uintmax_t>* pendingRequests; + + /* Dispatch request processing to a new thread for asynch. processing */ + void dispatch(void* func_ptr (void*), std::vector<std::string>* message, std::string* src); + + /* Send main window pointer to Java */ + void sendWindow(std::vector<std::string>* message); + + /* Given parent id and member name, send member pointer to java */ + void _sendMember(std::vector<std::string*>* message_parts); + + public: + PluginRequestProcessor(); /*Constructor */ + ~PluginRequestProcessor(); /* Destructor */ + + /* Process new requests (if applicable) */ + virtual bool newMessageOnBus(const char* message); +}; + +#endif // __ICEDTEAPLUGINREQUESTPROCESSOR_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/IcedTeaPluginUtils.cc Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,583 @@ +/* IcedTeaPluginUtils.cc + + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea 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, or (at your option) +any later version. + +IcedTea 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 IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +#include "IcedTeaNPPlugin.h" +#include "IcedTeaPluginUtils.h" + +/** + * Misc. utility functions used by the plugin + */ + +/*********************************************** + * Begin IcedTeaPluginUtilities implementation * +************************************************/ + +// Initialize static variables +int IcedTeaPluginUtilities::reference = 0; +pthread_mutex_t IcedTeaPluginUtilities::reference_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** + * Given a context number, constructs a message prefix to send to Java + * + * @param context The context of the request + * @return The string prefix (allocated on heap) + */ + +std::string* +IcedTeaPluginUtilities::constructMessagePrefix(int context) +{ + std::string* result = new std::string(); + char* context_str = itoa(context); + + *result += "context "; + result->append(context_str); + *result += " reference -1"; + + free(context_str); + + return result; +} + +/** + * Given a context number, and reference number, constructs a message prefix to + * send to Java + * + * @param context The context of the request + * @param rerefence The reference number of the request + * @return The string prefix (allocated on heap) + */ + +std::string* +IcedTeaPluginUtilities::constructMessagePrefix(int context, int reference) +{ + std::string* result = new std::string(); + char* context_str = itoa(context); + char* reference_str = itoa(reference); + + *result += "context "; + result->append(context_str); + *result += " reference "; + result->append(reference_str); + + free(context_str); + free(reference_str); + + return result; +} + +/** + * Given a context number, reference number, and source location, constructs + * a message prefix to send to Java + * + * @param context The context of the request + * @param rerefence The reference number of the request + * @param address The address for the script that made the request + * @return The string prefix (allocated on heap) + */ + +std::string* +IcedTeaPluginUtilities::constructMessagePrefix(int context, int reference, + char* address) +{ + std::string* result = new std::string(); + char* context_str = itoa(context); + char* reference_str = itoa(reference); + + *result += "context "; + result->append(context_str); + *result += " reference "; + result->append(reference_str); + *result += " src "; + result->append(address); + + free(context_str); + free(reference_str); + + return result; +} + +/** + * Returns a string representation of a void pointer + * + * @param id The pointer + * @return The string representation (Allocated on heap) + */ + +std::string* +IcedTeaPluginUtilities::JSIDToString(void* id) +{ + + std::string* result = new std::string(); + char* id_str = (char*) malloc(sizeof(char)*20); // max = long long = 8446744073709551615 == 19 chars + + if (sizeof(void*) == sizeof(long long)) + sprintf(id_str, "%llu", id); + else if (sizeof(void*) == sizeof(long)) + sprintf(id_str, "%lu", id); + else // else addresses are int (32-bit) + sprintf(id_str, "%du", id); + + *result += id_str; + + printf("Converting pointer %p to %s\n", id, id_str); + free(id_str); + + return result; +} + +/** + * Returns a void pointer from a string representation + * + * @param id_str The string representation + * @return The pointer + */ + +void* +IcedTeaPluginUtilities::stringToJSID(std::string id_str) +{ + void* ptr; + if (sizeof(void*) == sizeof(long long)) + { + printf("Casting \"%s\" -- %llu\n", id_str.c_str(), atol(id_str.c_str())); + ptr = reinterpret_cast <void*> ((unsigned long) atol(id_str.c_str())); + } else if (sizeof(void*) == sizeof(long)) + { + printf("Casting \"%s\" -- %lu\n", id_str.c_str(), atoi(id_str.c_str())); + ptr = reinterpret_cast <void*> ((unsigned int) atoi(id_str.c_str())); + } else + { + printf("Casting \"%s\" -- %du\n", id_str.c_str(), atoi(id_str.c_str())); + ptr = reinterpret_cast <void*> ((unsigned int) atoi(id_str.c_str())); + } + + printf("Casted: %p\n", ptr); + + return ptr; +} + +/** + * Increments the global reference number and returns it. + * + * This function is thread-safe. + */ +int +IcedTeaPluginUtilities::getReference() +{ + pthread_mutex_lock(&reference_mutex); + reference++; + pthread_mutex_unlock(&reference_mutex); + + return reference; +} + +/** + * Decrements the global reference number. + * + * This function is thread-safe. + */ +void +IcedTeaPluginUtilities::releaseReference() +{ + pthread_mutex_lock(&reference_mutex); + reference--; + pthread_mutex_unlock(&reference_mutex); +} + +/** + * Converts integer to char* + * + * @param i The integer to convert to ascii + * @return The converted string (allocated on heap) + */ +char* +IcedTeaPluginUtilities::itoa(int i) +{ + // largest possible integer is 10 digits long + char* result = (char*) malloc(sizeof(char)*11); + sprintf(result, "%d", i); + + return result; +} + +/** + * Frees memory from a string* vector + * + * The vector deconstructor will only delete string pointers upon being + * called. This function frees the associated string memory as well. + * + * @param v The vector whose strings are to be freed + */ +void +IcedTeaPluginUtilities::freeStringPtrVector(std::vector<std::string*>* v) +{ + if (v) + { + for (int i=0; i < v->size(); i++) + delete v->at(i); + + delete v; + } + +} + +/** + * Given a string, splits it on the given delimiters. + * + * @param str The string to split + * @param The delimiters to split on + * @return A string vector containing the aplit components + */ + +std::vector<std::string>* +IcedTeaPluginUtilities::strSplit(const char* str, const char* delim) +{ + std::vector<std::string>* v = new std::vector<std::string>(); + char* copy; + + // Tokening is done on a copy + copy = (char*) malloc (sizeof(char)*strlen(str) + 1); + strcpy(copy, str); + + char* tok_ptr; + tok_ptr = strtok (copy, delim); + + while (tok_ptr != NULL) + { + std::string* s = new std::string(); + s->append(tok_ptr); + v->push_back(*s); + tok_ptr = strtok (NULL, " "); + } + + return v; +} + +/** + * Given a unicode byte array, converts it to a UTF8 string + * + * The actual contents in the array may be surrounded by other data. + * + * e.g. with length 5, begin = 3, + * unicode_byte_array = "37 28 5 48 45 4c 4c 4f 9e 47": + * + * We'd start at 3 i.e. "48" and go on for 5 i.e. upto and including "4f". + * So we convert "48 45 4c 4c 4f" which is "hello" + * + * @param length The length of the string + * @param begin Where in the array to begin conversion + * @param result_unicode_str The return variable in which the + * converted string is placed + */ + +void +IcedTeaPluginUtilities::getUTF8String(int length, int begin, std::vector<std::string>* unicode_byte_array, std::string* result_unicode_str) +{ + result_unicode_str->clear(); + for (int i = begin; i < begin+length; i++) + result_unicode_str->push_back((char) strtol(unicode_byte_array->at(i).c_str(), NULL, 16)); + + PLUGIN_DEBUG_2ARG("Converted UTF-8 string: %s. Length=%d\n", result_unicode_str->c_str(), result_unicode_str->length()); +} + +/** + * Given a UTF8 string, converts it to a space delimited string of hex characters + * + * The first element in the return array is the length of the string + * + * e.g. "hello" would convert to: "5 48 45 4c 4c 4f" + * + * @param str The string to convert + * @param urt_str The result + */ + +void +IcedTeaPluginUtilities::convertStringToUTF8(std::string* str, std::string* utf_str) +{ + std::ostringstream ostream; + + char* length = itoa(str->length()); + ostream << length; + free(length); + + // UTF-8 characters are 4-bytes max + space + '\0' + char* hex_value = (char*) malloc(sizeof(char)*10); + + for (int i = 0; i < str->length(); i++) + { + sprintf(hex_value, " %x", (*str)[i]); + ostream << hex_value; + } + + utf_str->clear(); + *utf_str = ostream.str(); + + free(hex_value); + PLUGIN_DEBUG_2ARG("Converted %s to UTF-8 string %s\n", str->c_str(), utf_str->c_str()); +} + +/** + * Given a unicode byte array, converts it to a UTF16LE/UCS-2 string + * + * This works in a manner similar to getUTF8String, except that it reads 2 + * slots for each byte. + * + * @param length The length of the string + * @param begin Where in the array to begin conversion + * @param result_unicode_str The return variable in which the + * converted string is placed + */ +void +IcedTeaPluginUtilities::getUTF16LEString(int length, int begin, std::vector<std::string>* unicode_byte_array, std::wstring* result_unicode_str) +{ + + wchar_t c; + + if (plugin_debug) printf("Converted UTF-16LE string: "); + + result_unicode_str->clear(); + for (int i = begin; i < begin+length; i+=2) + { + int low = strtol(unicode_byte_array->at(i).c_str(), NULL, 16); + int high = strtol(unicode_byte_array->at(i+1).c_str(), NULL, 16); + + c = ((high << 8) | low); + + if ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9')) + { + if (plugin_debug) printf("%c", c); + } + + result_unicode_str->push_back(c); + } + + // not routing via debug print macros due to wide-string issues + if (plugin_debug) printf(". Length=%d\n", result_unicode_str->length()); +} + +/* + * Prints the given string vector (if debug is true) + * + * @param prefix The prefix to print before printing the vector contents + * @param cv The string vector whose contents are to be printed + */ +void +IcedTeaPluginUtilities::printStringVector(const char* prefix, std::vector<std::string>* str_vector) +{ + std::string* str = new std::string(); + *str += "{ "; + for (int i=0; i < str_vector->size(); i++) + { + *str += str_vector->at(i); + + if (i != str_vector->size() - 1) + *str += ", "; + } + + *str += " }"; + + PLUGIN_DEBUG_2ARG("%s %s\n", prefix, str->c_str()); + + delete str; +} + +/* + * Similar to printStringVector, but takes a vector of string pointers instead + * + * @param prefix The prefix to print before printing the vector contents + * @param cv The string* vector whose contents are to be printed + */ + +void +IcedTeaPluginUtilities::printStringPtrVector(const char* prefix, std::vector<std::string*>* str_ptr_vector) +{ + std::string* str = new std::string(); + *str += "{ "; + for (int i=0; i < str_ptr_vector->size(); i++) + { + *str += *(str_ptr_vector->at(i)); + + if (i != str_ptr_vector->size() - 1) + *str += ", "; + } + + *str += " }"; + + PLUGIN_DEBUG_2ARG("%s %s\n", prefix, str->c_str()); + + delete str; +} + +/****************************************** + * Begin JavaMessageSender implementation * + ****************************************** + * + * This implementation is very simple and is therefore folded into this file + * rather than a new one. + */ + +/** + * Sends to the Java side + * + * @param message The message to send. + * @param returns whether the message was consumable (always true) + */ + +bool +JavaMessageSender::newMessageOnBus(const char* message) +{ + char* msg = (char*) malloc(sizeof(char)*strlen(message) + 1); + strcpy(msg, message); + plugin_send_message_to_appletviewer(msg); + + free(msg); + msg = NULL; + + // Always successful + return true; +} + +/*********************************** + * Begin MessageBus implementation * + ***********************************/ + +/** + * Constructor. + * + * Initializes the mutexes needed by the other functions. + */ +MessageBus::MessageBus() +{ + int ret; + + ret = pthread_mutex_init(&subscriber_mutex, NULL); + + if(ret) + PLUGIN_DEBUG_1ARG("Error: Unable to initialize subscriber mutex: %d\n", ret); + + ret = pthread_mutex_init(&msg_queue_mutex, NULL); + if(ret) + PLUGIN_DEBUG_1ARG("Error: Unable to initialize message queue mutex: %d\n", ret); + + PLUGIN_DEBUG_2ARG("Mutexs %p and %p initialized\n", &subscriber_mutex, &msg_queue_mutex); +} + +/** + * Destructor. + * + * Destroy the mutexes initialized by the constructor. + */ + +MessageBus::~MessageBus() +{ + int ret; + + ret = pthread_mutex_destroy(&subscriber_mutex); + if(ret) + PLUGIN_DEBUG_1ARG("Error: Unable to destroy subscriber mutex: %d\n", ret); + + ret = pthread_mutex_destroy(&msg_queue_mutex); + if(ret) + PLUGIN_DEBUG_1ARG("Error: Unable to destroy message queue mutex: %d\n", ret); +} + +/** + * Adds the given BusSubscriber as a subscriber to self + * + * @param b The BusSubscriber to subscribe + */ +void +MessageBus::subscribe(BusSubscriber* b) +{ + // Applets may initialize in parallel. So lock before pushing. + + PLUGIN_DEBUG_2ARG("Subscribing %p to bus %p\n", b, this); + pthread_mutex_lock(&subscriber_mutex); + subscribers.push_back(b); + pthread_mutex_unlock(&subscriber_mutex); +} + +/** + * Removes the given BusSubscriber from the subscriber list + * + * @param b The BusSubscriber to ubsubscribe + */ +void +MessageBus::unSubscribe(BusSubscriber* b) +{ + // Applets may initialize in parallel. So lock before pushing. + + PLUGIN_DEBUG_2ARG("Un-subscribing %p from bus %p\n", b, this); + pthread_mutex_lock(&subscriber_mutex); + subscribers.remove(b); + pthread_mutex_unlock(&subscriber_mutex); +} + +/** + * Notifies all subscribers with the given message + * + * @param message The message to send to the subscribers + */ +void +MessageBus::post(const char* message) +{ + char* msg = (char*) malloc(sizeof(char)*strlen(message) + 1); + bool message_consumed = false; + + // consumer frees this memory + strcpy(msg, message); + + PLUGIN_DEBUG_1ARG("Trying to lock %p...\n", &msg_queue_mutex); + pthread_mutex_lock(&msg_queue_mutex); + + PLUGIN_DEBUG_1ARG("Message %s received on bus. Notifying subscribers.\n", msg); + + std::list<BusSubscriber*>::const_iterator i; + for( i = subscribers.begin(); i != subscribers.end() && !message_consumed; ++i ) { + PLUGIN_DEBUG_2ARG("Notifying subscriber %p of %s\n", *i, msg); + message_consumed = ((BusSubscriber*) *i)->newMessageOnBus(msg); + } + + pthread_mutex_unlock(&msg_queue_mutex); + + if (!message_consumed) + PLUGIN_DEBUG_1ARG("Warning: No consumer found for message %s\n", msg); + + PLUGIN_DEBUG_1ARG("%p unlocked...\n", &msg_queue_mutex); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/IcedTeaPluginUtils.h Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,244 @@ +/* IcedTeaPluginUtils.h + + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea 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, or (at your option) +any later version. + +IcedTea 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 IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +/** + * Utility classes for the IcedTeaPlugin + */ + +#ifndef __ICEDTEAPLUGINUTILS_H__ +#define __ICEDTEAPLUGINUTILS_H__ + +#include <pthread.h> +#include <stdio.h> + +#include <cstring> +#include <iostream> +#include <list> +#include <queue> +#include <sstream> +#include <string> +#include <vector> + +/* Debug output macros */ +static int plugin_debug = 1; + +#define PLUGIN_DEBUG_0ARG(str) \ + do \ + { \ + if (plugin_debug) \ + { \ + fprintf(stderr, "GCJ PLUGIN: thread %p: ", pthread_self()); \ + fprintf(stderr, str); \ + } \ + } while (0) + +#define PLUGIN_DEBUG_1ARG(str, arg1) \ + do \ + { \ + if (plugin_debug) \ + { \ + fprintf(stderr, "GCJ PLUGIN: thread %p: ", pthread_self()); \ + fprintf(stderr, str, arg1); \ + } \ + } while (0) + +#define PLUGIN_DEBUG_2ARG(str, arg1, arg2) \ + do \ + { \ + if (plugin_debug) \ + { \ + fprintf(stderr, "GCJ PLUGIN: thread %p: ", pthread_self()); \ + fprintf(stderr, str, arg1, arg2); \ + } \ + } while (0) + +#define PLUGIN_DEBUG_3ARG(str, arg1, arg2, arg3) \ + do \ + { \ + if (plugin_debug) \ + { \ + fprintf(stderr, "GCJ PLUGIN: thread %p: ", pthread_self()); \ + fprintf(stderr, str, arg1, arg2, arg3); \ + } \ + } while (0) + +#define PLUGIN_DEBUG_4ARG(str, arg1, arg2, arg3, arg4) \ + do \ + { \ + if (plugin_debug) \ + { \ + fprintf(stderr, "GCJ PLUGIN: thread %p: ", pthread_self()); \ + fprintf(stderr, str, arg1, arg2, arg3, arg4); \ + } \ + } while (0) + +/* + * Misc. utility functions + * + * This class is never instantiated and should contain static functions only + */ + +class IcedTeaPluginUtilities +{ + + private: + static int reference; /* Reference count */ + + /* Mutex lock for updating reference count */ + static pthread_mutex_t reference_mutex; + + public: + + /* Constructs message prefix with given context */ + static std::string* constructMessagePrefix(int context); + + /* Constructs message prefix with given context and reference */ + static std::string* constructMessagePrefix(int context, int reference); + + /* Constructs message prefix with given context, reference and src */ + static std::string* constructMessagePrefix(int context, int reference, + char* address); + + /* Converts given pointer to a string representation */ + static std::string* JSIDToString(void* id); + + /* Converts the given string representation to a pointer */ + static void* stringToJSID(std::string id_str); + + /* Increments reference count and returns it */ + static int getReference(); + + /* Decrements reference count */ + static void releaseReference(); + + /* Converts the given interget to char* array */ + static char* itoa(int i); + + /* Frees the given vector and the strings that its contents point to */ + static void freeStringPtrVector(std::vector<std::string*>* v); + + /* Splits the given string based on the delimiter provided */ + static std::vector<std::string>* strSplit(const char* str, + const char* delim); + + /* Converts given unicode integer byte array to UTF8 string */ + static void getUTF8String(int length, int begin, + std::vector<std::string>* unicode_byte_array, + std::string* result_unicode_str); + + /* Converts given UTF8 string to unicode integer byte array */ + static void convertStringToUTF8(std::string* str, + std::string* utf_str); + + /* Converts given unicode integer byte array to UTF16LE/UCS-2 string */ + static void getUTF16LEString(int length, int begin, + std::vector<std::string>* unicode_byte_array, + std::wstring* result_unicode_str); + + /* Prints contents of given string vector */ + static void printStringVector(const char* prefix, std::vector<std::string>* cv); + + /* Prints contents of given string pointer vector */ + static void printStringPtrVector(const char* prefix, std::vector<std::string*>* cv); +}; + +/* + * A bus subscriber interface. Implementors must implement the newMessageOnBus + * method. + */ +class BusSubscriber +{ + private: + + public: + BusSubscriber() {} + + /* Notifies this subscriber that a new message as arrived */ + virtual bool newMessageOnBus(const char* message) = 0; +}; + +/* + * This implementation is very simple and is therefore folded into this file + * rather than a new one. + */ +class JavaMessageSender : public BusSubscriber +{ + private: + public: + + /* Sends given message to Java side */ + virtual bool newMessageOnBus(const char* message); +}; + +/* + * Represents a message bus. + * The bus can also have subscribers who are notified when a new message + * arrives. + */ +class MessageBus +{ + private: + /* Mutex for locking the message queue */ + pthread_mutex_t msg_queue_mutex; + + /* Mutex used when adjusting subscriber list */ + pthread_mutex_t subscriber_mutex; + + /* Subscriber list */ + std::list<BusSubscriber*> subscribers; + + /* Queued messages */ + std::queue<char*> msgQueue; + + ~MessageBus(); + + public: + MessageBus(); + + /* subscribe to this bus */ + void subscribe(BusSubscriber* b); + + /* unsubscribe from this bus */ + void unSubscribe(BusSubscriber* b); + + /* Post a message on to the bus (it is safe to free the message pointer + after this function returns) */ + void post(const char* message); +}; + +#endif // __ICEDTEAPLUGINUTILS_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/IcedTeaScriptablePluginObject.cc Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,113 @@ +/* IcedTeaScriptablePluginObject.cc + + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea 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, or (at your option) +any later version. + +IcedTea 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 IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +#include "IcedTeaScriptablePluginObject.h" + +IcedTeaScriptablePluginObject::IcedTeaScriptablePluginObject(NPP instance) +{ + this->instance = instance; +} + +void +IcedTeaScriptablePluginObject::deAllocate(NPObject *npobj) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::deAllocate %p\n", npobj); +} + +void +IcedTeaScriptablePluginObject::invalidate(NPObject *npobj) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::invalidate %p\n", npobj); +} + +bool +IcedTeaScriptablePluginObject::hasMethod(NPObject *npobj, NPIdentifier name) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::hasMethod %p\n", npobj); +} + +bool +IcedTeaScriptablePluginObject::invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, + uint32_t argCount,NPVariant *result) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::invoke %p\n", npobj); +} + +bool +IcedTeaScriptablePluginObject::invokeDefault(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::invokeDefault %p\n", npobj); +} + +bool +IcedTeaScriptablePluginObject::hasProperty(NPObject *npobj, NPIdentifier name) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::hasProperty %p\n", npobj); +} + +bool +IcedTeaScriptablePluginObject::getProperty(NPObject *npobj, NPIdentifier name, NPVariant *result) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::getProperty %p\n", npobj); +} + +bool +IcedTeaScriptablePluginObject::setProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::setProperty %p\n", npobj); +} + +bool +IcedTeaScriptablePluginObject::removeProperty(NPObject *npobj, NPIdentifier name) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::removeProperty %p\n", npobj); +} + +bool +IcedTeaScriptablePluginObject::enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::enumerate %p\n", npobj); +} + +bool +IcedTeaScriptablePluginObject::construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, + NPVariant *result) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::construct %p\n", npobj); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/IcedTeaScriptablePluginObject.h Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,87 @@ +/* IcedTeaScriptablePluginObject.h + + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea 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, or (at your option) +any later version. + +IcedTea 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 IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +#ifndef __ICEDTEASCRIPTABLEPLUGINOBJECT_H_ +#define __ICEDTEASCRIPTABLEPLUGINOBJECT_H_ + +#include "npupp.h" + +/** + * IcedTeaScriptablePluginObject, an extended NPObject that implements + * static functions whose pointers are supplied to NPClass. + */ + +class IcedTeaScriptablePluginObject: public NPObject +{ + + private: + NPP instance; + + public: + IcedTeaScriptablePluginObject(NPP instance); + + static void deAllocate(NPObject *npobj); + + static void invalidate(NPObject *npobj); + + static bool hasMethod(NPObject *npobj, NPIdentifier name); + + static bool invoke(NPObject *npobj, NPIdentifier name, + const NPVariant *args, uint32_t argCount, NPVariant *result); + + static bool invokeDefault(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); + + static bool hasProperty(NPObject *npobj, NPIdentifier name); + + static bool getProperty(NPObject *npobj, NPIdentifier name, + NPVariant *result); + + static bool setProperty(NPObject *npobj, NPIdentifier name, + const NPVariant *value); + + static bool removeProperty(NPObject *npobj, NPIdentifier name); + + static bool enumerate(NPObject *npobj, NPIdentifier **value, + uint32_t *count); + + static bool construct(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); +}; + +#endif /* __ICEDTEASCRIPTABLEPLUGINOBJECT_H_ */
--- a/plugin/icedteanp/sun/applet/PluginAppletSecurityContext.java Fri Jul 10 13:12:21 2009 -0400 +++ b/plugin/icedteanp/sun/applet/PluginAppletSecurityContext.java Fri Jul 10 19:02:10 2009 -0400 @@ -248,7 +248,7 @@ // an applet will be loaded at some point, we should make it the SM // that JNLPRuntime will try to install if (System.getSecurityManager() == null) { - JNLPRuntime.initialize(false); + JNLPRuntime.initialize(/* isApplication */ false); } JNLPRuntime.disableExit();
--- a/plugin/icedteanp/sun/applet/PluginAppletViewer.java Fri Jul 10 13:12:21 2009 -0400 +++ b/plugin/icedteanp/sun/applet/PluginAppletViewer.java Fri Jul 10 19:02:10 2009 -0400 @@ -60,9 +60,9 @@ * have any questions. */ -package sun.applet; + package sun.applet; -import java.applet.Applet; + import java.applet.Applet; import java.applet.AppletContext; import java.applet.AudioClip; import java.awt.Dimension; @@ -84,6 +84,7 @@ import java.io.PrintStream; import java.io.Reader; import java.io.StringReader; +import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.SocketPermission; @@ -145,6 +146,8 @@ */ private static String defaultSaveFile = "Applet.ser"; + private static enum PAV_INIT_STATUS {PRE_INIT, ACTIVE, INACTIVE}; + /** * The panel in which the applet is being displayed. */ @@ -168,22 +171,22 @@ int identifier; - private static HashMap<Integer, PluginParseRequest> requests = new HashMap(); + private static HashMap<Integer, PluginParseRequest> requests = + new HashMap(); // Instance identifier -> PluginAppletViewer object. - private static HashMap<Integer, PluginAppletViewer> applets = new HashMap(); + private static HashMap<Integer, PluginAppletViewer> applets = + new HashMap(); private static PluginStreamHandler streamhandler; private static PluginCallRequestFactory requestFactory; - - private static HashMap<Integer, String> siteCookies = new HashMap<Integer,String>(); - + + private static HashMap<Integer, PAV_INIT_STATUS> status = + new HashMap<Integer,PAV_INIT_STATUS>(); + private double proposedHeightFactor; private double proposedWidthFactor; - - private enum AppletStatus { INITIALIZING, INITIALIZED, FAILED }; - private AppletStatus status = null; /** * Null constructor to allow instantiation via newInstance() @@ -198,10 +201,9 @@ final Hashtable atts, PrintStream statusMsgStream, PluginAppletViewerFactory factory) { super(handle, true); - this.factory = factory; - this.statusMsgStream = statusMsgStream; + this.factory = factory; + this.statusMsgStream = statusMsgStream; this.identifier = identifier; - this.status = AppletStatus.INITIALIZING; // FIXME: when/where do we remove this? PluginDebug.debug ("PARSING: PUTTING " + identifier + " " + this); applets.put(identifier, this); @@ -222,7 +224,7 @@ AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { - panel = new NetxPanel(doc, siteCookies.get(identifier), atts, false); + panel = new NetxPanel(doc, atts, false); AppletViewerPanel.debug("Using NetX panel"); PluginDebug.debug(atts.toString()); } catch (Exception ex) { @@ -316,7 +318,7 @@ Applet a; while ((a = panel.getApplet()) == null && ((NetxPanel) panel).isAlive()) { try { - Thread.sleep(2000); + Thread.sleep(1000); PluginDebug.debug("Waiting for applet to initialize... "); } catch (InterruptedException ie) { ie.printStackTrace(); @@ -326,16 +328,14 @@ // Still null? if (panel.getApplet() == null) { this.streamhandler.write("instance " + identifier + " reference " + -1 + " fatalError " + "Initialization failed"); - this.status = AppletStatus.FAILED; return; } - - this.status = AppletStatus.INITIALIZED; PluginDebug.debug("Applet initialized"); // Applet initialized. Find out it's classloader and add it to the list - String codeBase = doc.getProtocol() + "://" + doc.getHost(); + String portComponent = doc.getPort() != -1 ? ":" + doc.getPort() : ""; + String codeBase = doc.getProtocol() + "://" + doc.getHost() + portComponent; if (atts.get("codebase") != null) { try { @@ -380,6 +380,21 @@ // may happen in independent threads synchronized(requests) { + + // Check if we should proceed with init + // (=> no if destroy was called after tag, but before + // handle) + if (status.containsKey(identifier) && + status.get(identifier).equals(PAV_INIT_STATUS.INACTIVE)) { + + PluginDebug.debug("Inactive flag set. Refusing to initialize instance " + identifier); + requests.remove(identifier); + return; + + } + + status.put(identifier, PAV_INIT_STATUS.PRE_INIT); + PluginParseRequest request = requests.get(identifier); if (request == null) { request = new PluginParseRequest(); @@ -400,6 +415,17 @@ new StringReader(request.tag), new URL(request.documentbase)); requests.remove(identifier); + + // Panel initialization cannot be aborted mid-way. + // Once it is initialized, double check to see if this + // panel needs to stay around.. + if (status.get(identifier).equals(PAV_INIT_STATUS.INACTIVE)) { + PluginDebug.debug("Inactive flag set. Destroying applet instance " + identifier); + applets.get(identifier).handleMessage(-1, "destroy"); + } else { + status.put(identifier, PAV_INIT_STATUS.ACTIVE); + } + } else { PluginDebug.debug ("REQUEST HANDLE NOT SET: " + request.handle + ". BYPASSING"); } @@ -407,6 +433,21 @@ } else if (message.startsWith("handle")) { synchronized(requests) { + + // Check if we should proceed with init + // (=> no if destroy was called after handle, but before + // tag) + if (status.containsKey(identifier) && + status.get(identifier).equals(PAV_INIT_STATUS.INACTIVE)) { + + PluginDebug.debug("Inactive flag set. Refusing to initialize instance " + identifier); + requests.remove(identifier); + return; + + } + + status.put(identifier, PAV_INIT_STATUS.PRE_INIT); + PluginParseRequest request = requests.get(identifier); if (request == null) { request = new PluginParseRequest(); @@ -425,30 +466,73 @@ requests.remove(identifier); PluginDebug.debug ("REQUEST HANDLE, DONE PARSING " + Thread.currentThread()); + + // Panel initialization cannot be aborted mid-way. + // Once it is initialized, double check to see if this + // panel needs to stay around.. + if (status.get(identifier).equals(PAV_INIT_STATUS.INACTIVE)) { + PluginDebug.debug("Inactive flag set. Destroying applet instance " + identifier); + applets.get(identifier).handleMessage(-1, "destroy"); + } else { + status.put(identifier, PAV_INIT_STATUS.ACTIVE); + } + } else { PluginDebug.debug ("REQUEST TAG NOT SET: " + request.tag + ". BYPASSING"); } } - } else if (message.startsWith("cookie")) { - - int cookieStrIndex = message.indexOf(" "); - String cookieStr = null; + } else { + PluginDebug.debug ("Handling message: " + message + " instance " + identifier + " " + Thread.currentThread()); + + // Destroy may be called while initialization is still going + // on. We therefore special case it. + if (!applets.containsKey(identifier) && message.equals("destroy")) { + + // Set the status to inactive right away. Doesn't matter if it + // gets clobbered during init. due to a race. That is what the + // double check below is for. + PluginDebug.debug("Destroy called during initialization. Delaying destruction."); + status.put(identifier, PAV_INIT_STATUS.INACTIVE); - if (cookieStrIndex > 0) - cookieStr = message.substring(cookieStrIndex); + // We have set the flags. We now lock what stage 1 and 2 + // lock on, and force a synchronous status check+action. + synchronized (requests) { + // re-check (inside lock) if the applet is + // initialized at this point. + if (applets.containsKey(identifier)) { + PluginDebug.debug("Init done. destroying normally."); + applets.get(identifier).handleMessage(reference, message); + } else { + } + } // unlock - // Always set the cookie -- even if it is null - siteCookies.put(identifier, cookieStr); - } else { - PluginDebug.debug ("HANDLING MESSAGE " + message + " instance " + identifier + " " + Thread.currentThread()); + // we're done here + return; + } + + // For messages other than destroy, wait till initialization finishes + while (!applets.containsKey(identifier) && + ( + !status.containsKey(identifier) || + status.get(identifier).equals(PAV_INIT_STATUS.PRE_INIT) + ) + ); - while (!applets.containsKey(identifier) || applets.get(identifier).status == AppletStatus.INITIALIZING); + // don't bother processing further for inactive applets + if (status.get(identifier).equals(PAV_INIT_STATUS.INACTIVE)) + return; applets.get(identifier).handleMessage(reference, message); } } catch (Exception e) { - throw new RuntimeException("Failed to handle message: " + message + " " + - Thread.currentThread(), e); + + // If an exception happened during pre-init, we need to update status + if (status.get(identifier).equals(PAV_INIT_STATUS.PRE_INIT)) + status.put(identifier, PAV_INIT_STATUS.INACTIVE); + + throw new RuntimeException("Failed to handle message: " + + message + " for instance " + identifier + " " + + Thread.currentThread(), e); } } @@ -497,6 +581,7 @@ } } else if (message.startsWith("destroy")) { dispose(); + status.put(identifier, PAV_INIT_STATUS.INACTIVE); } else if (message.startsWith("GetJavaObject")) { // FIXME: how do we determine what security context this // object should belong to? @@ -745,7 +830,6 @@ * applets on this page. */ public Enumeration getApplets() { - AppletSecurity security = (AppletSecurity)System.getSecurityManager(); Vector v = new Vector(); SocketPermission panelSp = new SocketPermission(panel.getCodeBase().getHost(), "connect"); @@ -799,7 +883,7 @@ // streamhandler.pluginOutputStream has been closed. } } - + public long getWindow() { PluginDebug.debug ("STARTING getWindow"); PluginCallRequest request = requestFactory.getPluginCallRequest("window", @@ -1016,6 +1100,40 @@ return request.getObject(); } + public static Object requestPluginCookieInfo(URI uri) { + + PluginCallRequest request; + try + { + String encodedURI = UrlUtil.encode(uri.toString(), "UTF-8"); + request = requestFactory.getPluginCallRequest("cookieinfo", + "plugin PluginCookieInfo " + encodedURI, + "plugin PluginCookieInfo " + encodedURI); + + } catch (UnsupportedEncodingException e) + { + e.printStackTrace(); + return null; + } + + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait cookieinfo request 1"); + synchronized(request) { + PluginDebug.debug ("wait cookieinfo request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait cookieinfo request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for cookieinfo request.", + e); + } + PluginDebug.debug (" Cookieinfo DONE"); + return request.getObject(); + } + public static Object requestPluginProxyInfo(URI uri) { String requestURI = null; @@ -1301,26 +1419,28 @@ * the last applet. */ void appletClose() { - - // The caller thread is event dispatch thread, so - // spawn a new thread to avoid blocking the event queue - // when calling appletShutdown. - // - final AppletPanel p = panel; - - new Thread(new Runnable() - { - public void run() - { - appletShutdown(p); - appletPanels.removeElement(p); - dispose(); - - if (countApplets() == 0) { - appletSystemExit(); - } - } - }).start(); + + // The caller thread is event dispatch thread, so + // spawn a new thread to avoid blocking the event queue + // when calling appletShutdown. + // + final AppletPanel p = panel; + + new Thread(new Runnable() + { + public void run() + { + appletShutdown(p); + appletPanels.removeElement(p); + dispose(); + + if (countApplets() == 0) { + appletSystemExit(); + } + } + }).start(); + + status.put(identifier, PAV_INIT_STATUS.INACTIVE); } /** @@ -1524,10 +1644,6 @@ public static void parse(int identifier, long handle, Reader in, URL url) throws IOException { - // wait until cookie is set (even if cookie is null, it needs to be - // "set" to that first - while (!siteCookies.containsKey(identifier)); - final int fIdentifier = identifier; final long fHandle = handle; final Reader fIn = in;
--- a/plugin/icedteanp/sun/applet/PluginCallRequestFactory.java Fri Jul 10 13:12:21 2009 -0400 +++ b/plugin/icedteanp/sun/applet/PluginCallRequestFactory.java Fri Jul 10 19:02:10 2009 -0400 @@ -51,6 +51,8 @@ return new GetWindowPluginCallRequest(message, returnString); } else if (id == "proxyinfo") { return new PluginProxyInfoRequest(message, returnString); + } else if (id == "cookieinfo") { + return new PluginCookieInfoRequest(message, returnString); } else { throw new RuntimeException ("Unknown plugin call request type requested from factory"); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/sun/applet/PluginCookieInfoRequest.java Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,122 @@ +/* PluginCookieInfoRequest -- Object representing a request for cookie information from the browser + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea 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, or (at your option) +any later version. + +IcedTea 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 IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.net.HttpCookie; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import com.sun.jndi.toolkit.url.UrlUtil; + +/** + * This class represents a request object for cookie information for a given URI + */ + +public class PluginCookieInfoRequest extends PluginCallRequest { + + List<HttpCookie> cookieObjects = new ArrayList<HttpCookie>(); + + public PluginCookieInfoRequest(String message, String returnString) { + super(message, returnString); + } + + public void parseReturn(String cookieInfo) { + + // try to parse the proxy information. If things go wrong, do nothing .. + // this will keep internal = null which forces a direct connection + + PluginDebug.debug ("PluginCookieInfoRequest GOT: " + cookieInfo); + + String encodedURI = cookieInfo.split(" ")[2]; + + // Skip the first 3 components. We are guaranteed 3 components, + // so no index -1 to worry about + cookieInfo = cookieInfo.substring(cookieInfo.indexOf(' ')+1); + cookieInfo = cookieInfo.substring(cookieInfo.indexOf(' ')+1); + cookieInfo = cookieInfo.substring(cookieInfo.indexOf(' ')+1); + + URI siteURI; + try + { + siteURI = new URI(UrlUtil.decode(encodedURI, "UTF-8")); + } catch (Exception e) + { + e.printStackTrace(); + return; + } + + if (cookieInfo != null && cookieInfo.length() > 0) + { + String[] cookies = cookieInfo.split(";"); + + for (int i = 0; i < cookies.length; i++) + { + ArrayList l = new ArrayList(); + + String cookie = cookies[i]; + cookie = cookie.trim(); + String cookieName = cookie.substring(0, cookie.indexOf("=")); + String cookieValue = cookie.substring(cookie.indexOf("=")+1); + + HttpCookie httpCookieObj = new HttpCookie(cookieName, cookieValue); + httpCookieObj.setPath(siteURI.getPath()); + httpCookieObj.setVersion(0); // force v0 + + PluginDebug.debug("Adding cookie info COOKIEN=" + cookieName + " and COOKIEV=" + cookieValue); + cookieObjects.add(httpCookieObj); + } + } + + setDone(true); + } + + /** + * Returns whether the given message is serviceable by this object + * + * @param message The message to service + * @return boolean indicating if message is serviceable + */ + public boolean serviceable(String message) { + return message.startsWith(returnString); + } + + public List<HttpCookie> getObject() { + return this.cookieObjects; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/sun/applet/PluginCookieStore.java Fri Jul 10 19:02:10 2009 -0400 @@ -0,0 +1,73 @@ +/* PluginCookieStore -- Storage for cookie information + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea 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, or (at your option) +any later version. + +IcedTea 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 IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.net.HttpCookie; +import java.net.URI; +import java.util.List; + +import sun.net.www.protocol.http.InMemoryCookieStore; + +public class PluginCookieStore extends InMemoryCookieStore +{ + public List<HttpCookie> get(URI uri) + { + List<HttpCookie> cookies; + + // Try to fetch it from the plugin, but if something goes + // wrong, fall back. Don't crash! + try + { + cookies = (List<HttpCookie>) PluginAppletViewer.requestPluginCookieInfo(uri); + + // If cookies is null, something went wrong. Fall back. + if (cookies == null) throw new NullPointerException("Null cookie"); + + } catch (Exception e) + { + PluginDebug.debug("Unable to fetch cookie information from plugin. " + + "Falling back to default."); + e.printStackTrace(); + cookies = super.get(uri); + } + + PluginDebug.debug("Returning cookies " + cookies + " for site: " + uri); + + return cookies; + } +}
--- a/plugin/icedteanp/sun/applet/PluginMain.java Fri Jul 10 13:12:21 2009 -0400 +++ b/plugin/icedteanp/sun/applet/PluginMain.java Fri Jul 10 19:02:10 2009 -0400 @@ -68,10 +68,11 @@ import java.io.IOException; import java.io.PrintStream; import java.net.Authenticator; +import java.net.CookieHandler; +import java.net.CookieManager; import java.net.PasswordAuthentication; import java.net.ProxySelector; import java.util.Enumeration; -import java.util.HashMap; import java.util.Properties; import javax.net.ssl.HttpsURLConnection; @@ -218,6 +219,9 @@ // plug in a custom authenticator and proxy selector Authenticator.setDefault(new CustomAuthenticator()); ProxySelector.setDefault(new PluginProxySelector()); + + CookieManager ckManager = new CookieManager(new PluginCookieStore(), null); + CookieHandler.setDefault(ckManager); } static boolean messageAvailable() {
--- a/plugin/icedteanp/sun/applet/PluginMessageConsumer.java Fri Jul 10 13:12:21 2009 -0400 +++ b/plugin/icedteanp/sun/applet/PluginMessageConsumer.java Fri Jul 10 19:02:10 2009 -0400 @@ -44,7 +44,7 @@ class PluginMessageConsumer { - int MAX_WORKERS = 30; + int MAX_WORKERS = 20; LinkedList<String> readQueue = new LinkedList<String>(); ArrayList<PluginMessageHandlerWorker> workers = new ArrayList<PluginMessageHandlerWorker>(); PluginStreamHandler streamHandler = null;
--- a/rt/net/sourceforge/jnlp/tools/JarSigner.java Fri Jul 10 13:12:21 2009 -0400 +++ b/rt/net/sourceforge/jnlp/tools/JarSigner.java Fri Jul 10 19:02:10 2009 -0400 @@ -199,7 +199,7 @@ public void verifyJars(List<JARDesc> jars, ResourceTracker tracker) throws Exception { - certs = new ArrayList<CertPath>(); + certs = new ArrayList<CertPath>(); for (int i = 0; i < jars.size(); i++) { JARDesc jar = (JARDesc) jars.get(i); @@ -238,24 +238,27 @@ public boolean verifyJar(String jarName) throws Exception { boolean anySigned = false; boolean hasUnsignedEntry = false; - JarInputStream jis = null; + JarFile jarFile = null; // certs could be uninitialized if one calls this method directly if (certs == null) certs = new ArrayList<CertPath>(); try { - jis = new JarInputStream(new FileInputStream(jarName), true); + jarFile = new JarFile(jarName, true); Vector<JarEntry> entriesVec = new Vector<JarEntry>(); byte[] buffer = new byte[8192]; JarEntry je; - while ((je = jis.getNextJarEntry()) != null) { + Enumeration<JarEntry> entries = jarFile.entries(); + while (entries.hasMoreElements()) { + je = entries.nextElement(); entriesVec.addElement(je); - InputStream is = null; + + InputStream is = jarFile.getInputStream(je); try { int n; - while ((n = jis.read(buffer, 0, buffer.length)) != -1) { + while ((n = is.read(buffer, 0, buffer.length)) != -1) { // we just read. this will throw a SecurityException // if a signature/digest check fails. } @@ -266,9 +269,7 @@ } } - Manifest man = jis.getManifest(); - - if (man != null) { + if (jarFile.getManifest() != null) { if (verbose) System.out.println(); Enumeration<JarEntry> e = entriesVec.elements(); @@ -350,8 +351,8 @@ e.printStackTrace(); throw e; } finally { // close the resource - if (jis != null) { - jis.close(); + if (jarFile != null) { + jarFile.close(); } }