# HG changeset patch # User Andrew John Hughes # Date 1242851424 -3600 # Node ID 933d372e9deda4120969dfe3371186ae6290dd27 # Parent 8dc304404e01df747fd1a9e4b5f4640e6eb51ade Merge shiny new plugin. 2009-05-15 Deepak Bhole * Makefile.am: Wire in new plugin build. * configure.ac: Wire in new plugin (switch --enable-npplugin). * IcedTeaNPPlugin.cc: New file - based on GCJ Web Plugin. * plugin/icedteanp/netscape/javascript/JSException.java: New file, copied from the IcedTea plugin Java backend. * plugin/icedteanp/netscape/javascript/JSObject.java: Same. * plugin/icedteanp/netscape/javascript/JSProxy.java: Same. * plugin/icedteanp/netscape/javascript/JSRunnable.java: Same. * plugin/icedteanp/netscape/javascript/JSUtil.java: Same. * plugin/icedteanp/sun/applet/AppletSecurityContextManager.java: Same. * plugin/icedteanp/sun/applet/GetMemberPluginCallRequest.java: Same. * plugin/icedteanp/sun/applet/GetWindowPluginCallRequest.java: Same. * plugin/icedteanp/sun/applet/JavaConsole.java: Same. * plugin/icedteanp/sun/applet/PasswordAuthenticationDialog.java: Same. * plugin/icedteanp/sun/applet/PluginAppletSecurityContext.java: Same. * plugin/icedteanp/sun/applet/PluginAppletViewer.java: Same. * plugin/icedteanp/sun/applet/PluginCallRequest.java: Same. * plugin/icedteanp/sun/applet/PluginCallRequestFactory.java: Same. * plugin/icedteanp/sun/applet/PluginClassLoader.java: Same. * plugin/icedteanp/sun/applet/PluginDebug.java: Same. * plugin/icedteanp/sun/applet/PluginException.java: Same. * plugin/icedteanp/sun/applet/PluginMain.java: Same. * plugin/icedteanp/sun/applet/PluginMessageConsumer.java: Same. * plugin/icedteanp/sun/applet/PluginMessageHandlerWorker.java: Same. * plugin/icedteanp/sun/applet/PluginObjectStore.java: Same. * plugin/icedteanp/sun/applet/PluginProxyInfoRequest.java: Same. * plugin/icedteanp/sun/applet/PluginProxySelector.java: Same. * plugin/icedteanp/sun/applet/PluginStreamHandler.java: Same. * plugin/icedteanp/sun/applet/RequestQueue.java: Same. * plugin/icedteanp/sun/applet/TestEnv.java: Same. * plugin/icedteanp/sun/applet/VoidPluginCallRequest.java: Same. diff -r 8dc304404e01 -r 933d372e9ded ChangeLog --- a/ChangeLog Wed May 20 21:17:02 2009 +0100 +++ b/ChangeLog Wed May 20 21:30:24 2009 +0100 @@ -1,3 +1,37 @@ +2009-05-15 Deepak Bhole + + * Makefile.am: Wire in new plugin build. + * configure.ac: Wire in new plugin (switch --enable-npplugin). + * IcedTeaNPPlugin.cc: New file - based on GCJ Web Plugin. + * plugin/icedteanp/netscape/javascript/JSException.java: New file, copied + from the IcedTea plugin Java backend. + * plugin/icedteanp/netscape/javascript/JSObject.java: Same. + * plugin/icedteanp/netscape/javascript/JSProxy.java: Same. + * plugin/icedteanp/netscape/javascript/JSRunnable.java: Same. + * plugin/icedteanp/netscape/javascript/JSUtil.java: Same. + * plugin/icedteanp/sun/applet/AppletSecurityContextManager.java: Same. + * plugin/icedteanp/sun/applet/GetMemberPluginCallRequest.java: Same. + * plugin/icedteanp/sun/applet/GetWindowPluginCallRequest.java: Same. + * plugin/icedteanp/sun/applet/JavaConsole.java: Same. + * plugin/icedteanp/sun/applet/PasswordAuthenticationDialog.java: Same. + * plugin/icedteanp/sun/applet/PluginAppletSecurityContext.java: Same. + * plugin/icedteanp/sun/applet/PluginAppletViewer.java: Same. + * plugin/icedteanp/sun/applet/PluginCallRequest.java: Same. + * plugin/icedteanp/sun/applet/PluginCallRequestFactory.java: Same. + * plugin/icedteanp/sun/applet/PluginClassLoader.java: Same. + * plugin/icedteanp/sun/applet/PluginDebug.java: Same. + * plugin/icedteanp/sun/applet/PluginException.java: Same. + * plugin/icedteanp/sun/applet/PluginMain.java: Same. + * plugin/icedteanp/sun/applet/PluginMessageConsumer.java: Same. + * plugin/icedteanp/sun/applet/PluginMessageHandlerWorker.java: Same. + * plugin/icedteanp/sun/applet/PluginObjectStore.java: Same. + * plugin/icedteanp/sun/applet/PluginProxyInfoRequest.java: Same. + * plugin/icedteanp/sun/applet/PluginProxySelector.java: Same. + * plugin/icedteanp/sun/applet/PluginStreamHandler.java: Same. + * plugin/icedteanp/sun/applet/RequestQueue.java: Same. + * plugin/icedteanp/sun/applet/TestEnv.java: Same. + * plugin/icedteanp/sun/applet/VoidPluginCallRequest.java: Same. + 2009-05-13 Omair Majid * overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/JNLPSplashScreen.java: New file. This new class diff -r 8dc304404e01 -r 933d372e9ded IcedTeaNPPlugin.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/IcedTeaNPPlugin.cc Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,2006 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include + +// Netscape plugin API includes. +#include +#include + +// GLib includes. +#include +#include + +// GTK includes. +#include + +// Documentbase retrieval includes. +#include +#include +#include + +// API's into Mozilla +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// 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 +// 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 sec_man = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); + + if (!sec_man) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv); + + if (NS_FAILED(rv) || !io_svc) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr uri; + io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); + + nsCOMPtr 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 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 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 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 uri; + io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); + + // find the proxy address if any + nsCOMPtr 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 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 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 ("", NULL); + + g_free (escaped); + escaped = NULL; + } + } + } + + applet_tag = g_strconcat (applet_tag, ">", parameters, "", 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; +} diff -r 8dc304404e01 -r 933d372e9ded Makefile.am --- a/Makefile.am Wed May 20 21:17:02 2009 +0100 +++ b/Makefile.am Wed May 20 21:30:24 2009 +0100 @@ -22,6 +22,12 @@ NETBEANS_PROFILER_MD5SUM = a6756a62291ebea5c0e2431389b31380 VISUALVM_MD5SUM = a289739f4a5bcdb9a2c642cfcc1e83fe +if ENABLE_NPPLUGIN +ICEDTEAPLUGIN_CLEAN = clean-IcedTeaNPPlugin +ICEDTEAPLUGIN_TARGET = IcedTeaNPPlugin.so +PLUGIN_PATCH = patches/icedtea-liveconnect.patch +LIVECONNECT_DIR = -C lib/rt netscape +else if ENABLE_PLUGIN ICEDTEAPLUGIN_CLEAN = clean-IcedTeaPlugin ICEDTEAPLUGIN_TARGET = IcedTeaPlugin.so @@ -33,6 +39,7 @@ PLUGIN_PATCH = patches/icedtea-plugin.patch LIVECONNECT_DIR = endif +endif if ENABLE_PULSE_JAVA # include the makefile in pulseaudio subdir @@ -75,6 +82,9 @@ if ENABLE_PLUGIN rm -f IcedTeaPlugin.o IcedTeaPlugin.so endif +if ENABLE_NPPLUGIN + rm -f IcedTeaNPPlugin.o IcedTeaNPPlugin.so +endif if BUILD_CACAO rm -rf cacao endif @@ -99,7 +109,8 @@ clean-icedtea icedtea-against-icedtea clean-icedtea-ecj clean-plugs \ clean-tools-jar clean-shared-objects clean-visualvm clean-nbplatform \ clean-copy clean-hotspot-tools clean-rt $(ICEDTEAPLUGIN_CLEAN) \ - hotspot hotspot-helper clean-extra clean-jtreg clean-jtreg-reports + $(ICEDTEANPPLUGIN_CLEAN) hotspot hotspot-helper clean-extra clean-jtreg \ + clean-jtreg-reports GENERATED_FILES = generated/com/sun/java/swing/plaf/gtk/resources/gtk_it.java \ generated/com/sun/java/swing/plaf/gtk/resources/gtk_de.java \ @@ -1379,6 +1390,7 @@ jconsole.desktop policytool.desktop \ $(JTREG_SRCS) \ IcedTeaPlugin.cc \ + IcedTeaNPPlugin.cc \ HACKING $(PULSEAUDIO_SRCS) fsg.sh \ plugin hotspot.map autogen.sh @@ -2170,6 +2182,11 @@ exit 2; \ fi +if ENABLE_NPPLUGIN + cp -a $(abs_top_srcdir)/plugin/icedteanp/sun/applet/*java openjdk/jdk/src/share/classes/sun/applet/ + mkdir -p rt + cp -a $(abs_top_srcdir)/plugin/icedteanp/netscape rt/ +endif if ENABLE_PLUGIN cp -a $(abs_top_srcdir)/plugin/icedtea/sun/applet/*java openjdk/jdk/src/share/classes/sun/applet/ mkdir -p rt @@ -2542,7 +2559,8 @@ stamps/icedtea.stamp: stamps/bootstrap-directory-symlink.stamp \ stamps/hotspot-tools.stamp stamps/plugs.stamp \ stamps/ports.stamp stamps/patch.stamp stamps/overlay.stamp \ - $(ICEDTEAPLUGIN_TARGET) stamps/jibx.stamp stamps/cacao.stamp \ + $(ICEDTEAPLUGIN_TARGET) $(ICEDTEANPPLUGIN_TARGET) \ + stamps/jibx.stamp stamps/cacao.stamp \ stamps/pulse-java.stamp stamps/visualvm.stamp $(ARCH_PREFIX) $(MAKE) \ $(ICEDTEA_ENV) \ @@ -2552,6 +2570,12 @@ mkdir -p $(BUILD_OUTPUT_DIR)/j2re-image/lib/$(INSTALL_ARCH_DIR) mkdir -p $(BUILD_OUTPUT_DIR)/j2sdk-image/jre/lib/ext mkdir -p $(BUILD_OUTPUT_DIR)/j2re-image/lib/ext +if ENABLE_NPPLUGIN + cp -pPRf IcedTeaNPPlugin.so \ + $(BUILD_OUTPUT_DIR)/j2sdk-image/jre/lib/$(INSTALL_ARCH_DIR) + cp -pPRf IcedTeaNPPlugin.so \ + $(BUILD_OUTPUT_DIR)/j2re-image/lib/$(INSTALL_ARCH_DIR) +endif if ENABLE_PLUGIN cp -pPRf IcedTeaPlugin.so \ $(BUILD_OUTPUT_DIR)/j2sdk-image/jre/lib/$(INSTALL_ARCH_DIR) @@ -2619,7 +2643,8 @@ stamps/icedtea-debug.stamp: stamps/bootstrap-directory-symlink.stamp \ stamps/hotspot-tools.stamp stamps/plugs.stamp \ stamps/ports.stamp stamps/patch.stamp stamps/overlay.stamp \ - $(ICEDTEAPLUGIN_TARGET) stamps/jibx.stamp stamps/cacao.stamp \ + $(ICEDTEAPLUGIN_TARGET) $(ICEDTEANPPLUGIN_TARGET) \ + stamps/jibx.stamp stamps/cacao.stamp \ stamps/pulse-java.stamp stamps/visualvm.stamp $(ARCH_PREFIX) $(MAKE) \ $(ICEDTEA_ENV) \ @@ -2629,6 +2654,12 @@ mkdir -p $(BUILD_OUTPUT_DIR)-debug/j2re-image/lib/$(INSTALL_ARCH_DIR) mkdir -p $(BUILD_OUTPUT_DIR)-debug/j2sdk-image/jre/lib/ext mkdir -p $(BUILD_OUTPUT_DIR)-debug/j2re-image/lib/ext +if ENABLE_NPPLUGIN + cp -pPRf IcedTeaNPPlugin.so \ + $(BUILD_OUTPUT_DIR)-debug/j2sdk-image/jre/lib/$(INSTALL_ARCH_DIR) + cp -pPRf IcedTeaNPPlugin.so \ + $(BUILD_OUTPUT_DIR)-debug/j2re-image/lib/$(INSTALL_ARCH_DIR) +endif if ENABLE_PLUGIN cp -pPRf IcedTeaPlugin.so \ $(BUILD_OUTPUT_DIR)-debug/j2sdk-image/jre/lib/$(INSTALL_ARCH_DIR) @@ -3039,6 +3070,37 @@ rm -rf cacao rm -f stamps/cacao.stamp +if ENABLE_NPPLUGIN +# IcedTeaNPPlugin.so. +# 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 + if [ -e $(abs_top_srcdir)/.hg ] && which $(HG) >/dev/null; then \ + revision="-r`(cd $(abs_top_srcdir); $(HG) tip --template '{rev}')`" ; \ + fi ; \ + if [ -n "$(PKGVERSION)" ]; then plugin_version=" ($(PKGVERSION))"; fi; \ + plugin_version=" $(PACKAGE_VERSION)$$revision$$plugin_version"; \ + $(CXX) $(CXXFLAGS) \ + -DPACKAGE_VERSION="\"$(PACKAGE_VERSION)\"" \ + -DPLUGIN_VERSION="\"$$plugin_version\"" \ + -DMOZILLA_VERSION_COLLAPSED="$(MOZILLA_VERSION_COLLAPSED)" \ + $(GLIB_CFLAGS) \ + $(GTK_CFLAGS) \ + $(MOZILLA_CFLAGS) \ + -fPIC -c -o $@ $< +IcedTeaNPPlugin.so: IcedTeaNPPlugin.o + $(CXX) $(CXXFLAGS) \ + $< \ + $(GLIB_LIBS) \ + $(GTK_LIBS) \ + $(MOZILLA_LIBS)\ + -shared -o $@ + +clean-IcedTeaNPPlugin: + rm -f IcedTeaNPPlugin.o + rm -f IcedTeaNPPlugin.so +endif if ENABLE_PLUGIN # IcedTeaPlugin.so. # Separate compile and link invocations to ensure intermediate object diff -r 8dc304404e01 -r 933d372e9ded configure.ac --- a/configure.ac Wed May 20 21:17:02 2009 +0100 +++ b/configure.ac Wed May 20 21:30:24 2009 +0100 @@ -161,6 +161,14 @@ AM_CONDITIONAL(ENABLE_PLUGIN, test "x${enable_plugin}" = "xyes") AC_MSG_RESULT(${enable_plugin}) +AC_MSG_CHECKING([whether to build the new experimental browser plugin based on npruntime]) +AC_ARG_ENABLE([npplugin], + [AS_HELP_STRING([--enable-npplugin], + [Enable compilation of browser plugin (automatically disables default plugin)])], + [enable_npplugin="${enableval}"], [enable_npplugin="no"]) +AM_CONDITIONAL(ENABLE_NPPLUGIN, test "x${enable_npplugin}" = "xyes") +AC_MSG_RESULT(${enable_npplugin}) + AC_MSG_CHECKING([whether to include PulseAudio support]) AC_ARG_ENABLE([pulse-java], [AS_HELP_STRING([--enable-pulse-java], @@ -509,6 +517,21 @@ PKG_CHECK_MODULES(GTK, gtk+-2.0) IT_XULRUNNER_VERSION fi +if test "x${enable_npplugin}" = "xyes" +then + PKG_CHECK_MODULES(MOZILLA, \ + mozilla-plugin libxul-unstable) + PKG_CHECK_MODULES(GTK, gtk+-2.0) + PKG_CHECK_MODULES(GLIB, glib-2.0) + + AC_SUBST(MOZILLA_CFLAGS) + AC_SUBST(MOZILLA_LIBS) + AC_SUBST(GLIB_CFLAGS) + AC_SUBST(GLIB_LIBS) + AC_SUBST(GTK_CFLAGS) + AC_SUBST(GTK_LIBS) +fi + AC_MSG_CHECKING(for --with-additional-vms) AC_ARG_WITH(additional-vms, diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/netscape/javascript/JSException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/netscape/javascript/JSException.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,140 @@ +/* -*- Mode: Java; tab-width: 8; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package netscape.javascript; + +/** + * JSException is an exception which is thrown when JavaScript code + * returns an error. + */ + +public +class JSException extends RuntimeException { + public static final int EXCEPTION_TYPE_EMPTY = -1; + public static final int EXCEPTION_TYPE_VOID = 0; + public static final int EXCEPTION_TYPE_OBJECT = 1; + public static final int EXCEPTION_TYPE_FUNCTION = 2; + public static final int EXCEPTION_TYPE_STRING = 3; + public static final int EXCEPTION_TYPE_NUMBER = 4; + public static final int EXCEPTION_TYPE_BOOLEAN = 5; + public static final int EXCEPTION_TYPE_ERROR = 6; + + public String filename; + public int lineno; + public String source; + public int tokenIndex; + public int wrappedExceptionType; + public Object wrappedException; + + /** + * Constructs a JSException without a detail message. + * A detail message is a String that describes this particular exception. + * + * @deprecated Not for public use in future versions. + */ + public JSException() { + super(); + filename = "unknown"; + lineno = 0; + source = ""; + tokenIndex = 0; + wrappedExceptionType = EXCEPTION_TYPE_EMPTY; + } + + /** + * Constructs a JSException with a detail message. + * A detail message is a String that describes this particular exception. + * @param s the detail message + * + * @deprecated Not for public use in future versions. + */ + public JSException(String s) { + super(s); + filename = "unknown"; + lineno = 0; + source = ""; + tokenIndex = 0; + wrappedExceptionType = EXCEPTION_TYPE_EMPTY; + } + + /** + * Constructs a JSException with a wrapped JavaScript exception object. + * This constructor needs to be public so that Java users can throw + * exceptions to JS cleanly. + */ + public JSException(int wrappedExceptionType, Object wrappedException) { + super(); + this.wrappedExceptionType = wrappedExceptionType; + this.wrappedException = wrappedException; + } + + /** + * Constructs a JSException with a detail message and all the + * other info that usually comes with a JavaScript error. + * @param s the detail message + * + * @deprecated Not for public use in future versions. + */ + public JSException(String s, String filename, int lineno, + String source, int tokenIndex) { + super(s); + this.filename = filename; + this.lineno = lineno; + this.source = source; + this.tokenIndex = tokenIndex; + wrappedExceptionType = EXCEPTION_TYPE_EMPTY; + } + + /** + * Instance method getWrappedExceptionType returns the int mapping of the + * type of the wrappedException Object. + */ + public int getWrappedExceptionType() { + return wrappedExceptionType; + } + + /** + * Instance method getWrappedException. + */ + public Object getWrappedException() { + return wrappedException; + } + +} + diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/netscape/javascript/JSObject.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/netscape/javascript/JSObject.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,263 @@ +/* -*- Mode: Java; tab-width: 8; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* more doc todo: + * threads + * gc + * + * + */ + +package netscape.javascript; + +import java.applet.Applet; + +import sun.applet.PluginAppletViewer; +import sun.applet.PluginDebug; + + + +/** + * JSObject allows Java to manipulate objects that are + * defined in JavaScript. + * Values passed from Java to JavaScript are converted as + * follows:
    + *
  • JSObject is converted to the original JavaScript object + *
  • Any other Java object is converted to a JavaScript wrapper, + * which can be used to access methods and fields of the java object. + * Converting this wrapper to a string will call the toString method + * on the original object, converting to a number will call the + * doubleValue method if possible and fail otherwise. Converting + * to a boolean will try to call the booleanValue method in the + * same way. + *
  • Java arrays are wrapped with a JavaScript object that understands + * array.length and array[index] + *
  • A Java boolean is converted to a JavaScript boolean + *
  • Java byte, char, short, int, long, float, and double are converted + * to JavaScript numbers + *
+ * Values passed from JavaScript to Java are converted as follows:
    + *
  • objects which are wrappers around java objects are unwrapped + *
  • other objects are wrapped with a JSObject + *
  • strings, numbers and booleans are converted to String, Double, + * and Boolean objects respectively + *
+ * This means that all JavaScript values show up as some kind + * of java.lang.Object in Java. In order to make much use of them, + * you will have to cast them to the appropriate subclass of Object, + * e.g. (String) window.getMember("name"); or + * (JSObject) window.getMember("document");. + */ +public final class JSObject { + /* the internal object data */ + private long internal; + + /** + * initialize + */ + private static void initClass() { + PluginDebug.debug ("JSObject.initClass"); + } + + static { + PluginDebug.debug ("JSObject INITIALIZER"); + } + + /** + * it is illegal to construct a JSObject manually + */ + // FIXME: make private! + public JSObject(int jsobj_addr) { + PluginDebug.debug ("JSObject int CONSTRUCTOR"); + internal = jsobj_addr; + } + + public JSObject(long jsobj_addr) { + PluginDebug.debug ("JSObject long CONSTRUCTOR"); + internal = jsobj_addr; + } + + /** + * Retrieves a named member of a JavaScript object. + * Equivalent to "this.name" in JavaScript. + */ + public Object getMember(String name) + { + PluginDebug.debug ("JSObject.getMember " + name); + + Object o = PluginAppletViewer.getMember(internal, name); + PluginDebug.debug ("JSObject.getMember GOT " + o); + return o; + } + + + /** + * Retrieves an indexed member of a JavaScript object. + * Equivalent to "this[index]" in JavaScript. + */ + // public Object getMember(int index) { return getSlot(index); } + public Object getSlot(int index) + { + PluginDebug.debug ("JSObject.getSlot " + index); + + return PluginAppletViewer.getSlot(internal, index); + } + + + /** + * Sets a named member of a JavaScript object. + * Equivalent to "this.name = value" in JavaScript. + */ + public void setMember(String name, Object value) + { + PluginDebug.debug ("JSObject.setMember " + name + " " + value); + + PluginAppletViewer.setMember(internal, name, value); + } + + /** + * Sets an indexed member of a JavaScript object. + * Equivalent to "this[index] = value" in JavaScript. + */ + // public void setMember(int index, Object value) { + // setSlot(index, value); + // } + public void setSlot(int index, Object value) + { + PluginDebug.debug ("JSObject.setSlot " + index + " " + value); + + PluginAppletViewer.setSlot(internal, index, value); + } + + + // TODO: toString, finalize. + + /** + * Removes a named member of a JavaScript object. + */ + public void removeMember(String name) + { + PluginDebug.debug ("JSObject.removeMember " + name); + + PluginAppletViewer.removeMember(internal, name); + } + + + /** + * Calls a JavaScript method. + * Equivalent to "this.methodName(args[0], args[1], ...)" in JavaScript. + */ + public Object call(String methodName, Object args[]) + { + if (args == null) + args = new Object[0]; + + PluginDebug.debug ("JSObject.call " + methodName); + for (int i = 0; i < args.length; i++) + PluginDebug.debug (" " + args[i]); + PluginDebug.debug(""); + return PluginAppletViewer.call(internal, methodName, args); + } + + + /** + * Evaluates a JavaScript expression. The expression is a string + * of JavaScript source code which will be evaluated in the context + * given by "this". + */ + public Object eval(String s) + { + PluginDebug.debug("JSObject.eval " + s); + return PluginAppletViewer.eval(internal, s); + } + + + /** + * Converts a JSObject to a String. + */ + public String toString() + { + PluginDebug.debug("JSObject.toString"); + return PluginAppletViewer.javascriptToString(internal); + } + + + // should use some sort of identifier rather than String + // is "property" the right word? + // native String[] listProperties(); + + + /** + * get a JSObject for the window containing the given applet + */ + public static JSObject getWindow(Applet applet) + { + PluginDebug.debug("JSObject.getWindow"); + // FIXME: handle long case as well. + long internal = 0; + internal = ((PluginAppletViewer) + applet.getAppletContext()).getWindow(); + PluginDebug.debug ("GOT IT: " + internal); + return new JSObject(internal); + } + + + /** + * Finalization decrements the reference count on the corresponding + * JavaScript object. + */ + protected void finalize() + { + PluginDebug.debug("JSObject.finalize "); + PluginAppletViewer.JavaScriptFinalize(internal); + } + + + /** + * Override java.lang.Object.equals() because identity is not preserved + * with instances of JSObject. + */ + public boolean equals(Object obj) + { + PluginDebug.debug("JSObject.equals " + obj); + + return false; + } + +} diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/netscape/javascript/JSProxy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/netscape/javascript/JSProxy.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,58 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * The JSProxy interface allows applets and plugins to + * share javascript contexts. + */ + +package netscape.javascript; +import java.applet.Applet; + +public interface JSProxy { + Object getMember(JSObject jso, String name); + Object getSlot(JSObject jso, int index); + void setMember(JSObject jso, String name, Object value); + void setSlot(JSObject jso, int index, Object value); + void removeMember(JSObject jso, String name); + Object call(JSObject jso, String methodName, Object args[]); + Object eval(JSObject jso, String s); + String toString(JSObject jso); + JSObject getWindow(Applet applet); +} diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/netscape/javascript/JSRunnable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/netscape/javascript/JSRunnable.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,72 @@ +/* -*- Mode: Java; tab-width: 8; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package netscape.javascript; + +import sun.applet.PluginDebug; + +/** + * Runs a JavaScript object with a run() method in a separate thread. + */ +public class JSRunnable implements Runnable { + private JSObject runnable; + + public JSRunnable(JSObject runnable) { + this.runnable = runnable; + synchronized(this) { + new Thread(this).start(); + try { + this.wait(); + } catch (InterruptedException ie) { + } + } + } + + public void run() { + try { + runnable.call("run", null); + synchronized(this) { + notifyAll(); + } + } catch (Throwable t) { + PluginDebug.debug(t.toString()); + t.printStackTrace(System.err); + } + } +} diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/netscape/javascript/JSUtil.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/netscape/javascript/JSUtil.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,59 @@ +/* -*- Mode: Java; tab-width: 8; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* ** */ + +package netscape.javascript; +import java.io.*; + +public class JSUtil { + + /* Return the stack trace of an exception or error as a String */ + public static String getStackTrace(Throwable t) { + ByteArrayOutputStream captureStream; + PrintWriter p; + + captureStream = new ByteArrayOutputStream(); + p = new PrintWriter(captureStream); + + t.printStackTrace(p); + p.flush(); + + return captureStream.toString(); + } +} diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/sun/applet/AppletSecurityContextManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/sun/applet/AppletSecurityContextManager.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,71 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 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.security.AccessControlContext; +import java.util.HashMap; + +public class AppletSecurityContextManager { + + // Context identifier -> PluginAppletSecurityContext object. + // FIXME: make private + private static HashMap contexts = new HashMap(); + + public static void addContext(int identifier, PluginAppletSecurityContext context) { + contexts.put(identifier, context); + } + + public static PluginAppletSecurityContext getSecurityContext(int identifier) { + return contexts.get(identifier); + } + + public static void dumpStore(int identifier) { + contexts.get(identifier).dumpStore(); + } + + public static void handleMessage(int identifier, int reference, String src, String[] privileges, String message) { + PluginDebug.debug(identifier + " -- " + src + " -- " + reference + " -- " + message + " CONTEXT= " + contexts.get(identifier)); + AccessControlContext callContext = null; + + privileges = privileges != null ? privileges : new String[0]; + callContext = contexts.get(identifier).getAccessControlContext(privileges, src); + + contexts.get(identifier).handleMessage(reference, src, callContext, message); + } +} diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/sun/applet/GetMemberPluginCallRequest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/sun/applet/GetMemberPluginCallRequest.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,76 @@ +/* GetMemberPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 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; + + +public class GetMemberPluginCallRequest extends PluginCallRequest { + Object object = null; + + public GetMemberPluginCallRequest(String message, String returnString) { + super(message, returnString); + PluginDebug.debug ("GetMEMBerPLUGINCAlL " + message + " " + returnString); + } + + public void parseReturn(String message) { + PluginDebug.debug ("GetMEMBerparseReturn GOT: " + message); + String[] args = message.split(" "); + // FIXME: add thread ID to messages to support multiple + // threads using the netscape.javascript package. + object = AppletSecurityContextManager.getSecurityContext(0).getObject(Integer.parseInt(args[1])); + 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.contains("JavaScriptCall") || + message.contains("JavaScriptEval") || + message.contains("JavaScriptGetMember") || + message.contains("JavaScriptGetSlot") || + message.contains("JavaScriptToString"); + } + + public Object getObject() { + return this.object; + } +} + diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/sun/applet/GetWindowPluginCallRequest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/sun/applet/GetWindowPluginCallRequest.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,75 @@ +/* GetWindowPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 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.security.AccessControlContext; +import java.security.ProtectionDomain; + + + +public class GetWindowPluginCallRequest extends PluginCallRequest { + // FIXME: look into int vs long JavaScript internal values. + long internal; + + public GetWindowPluginCallRequest(String message, String returnString) { + super(message, returnString); + } + + public void parseReturn(String message) { + PluginDebug.debug ("GetWINDOWparseReturn GOT: " + message); + String[] args = message.split(" "); + // FIXME: add thread ID to messages to support multiple + // threads using the netscape.javascript package. + internal = Long.parseLong(args[1]); + 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.contains("JavaScriptGetWindow"); + } + + public Long getObject() { + return this.internal; + } +} diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/sun/applet/JavaConsole.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/sun/applet/JavaConsole.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,365 @@ +/* JavaConsole -- A java console for the plugin + 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.awt.Dimension; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +/** + * A simple Java console for IcedTeaPlugin + * + */ +public class JavaConsole { + + private boolean initialized = false; + + JFrame consoleWindow; + JTextArea stdErrText; + JTextArea stdOutText; + + /** + * Initialize the console + */ + public void initialize() { + + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + e.printStackTrace(); + } + + consoleWindow = new JFrame("Java Console"); + + JPanel contentPanel = new JPanel(); + contentPanel.setLayout(new GridBagLayout()); + + GridBagConstraints c; + + Font monoSpace = new Font("Monospaced", Font.PLAIN, 12); + + /* std out */ + + stdOutText = new JTextArea(); + JScrollPane stdOutScrollPane = new JScrollPane(stdOutText); + stdOutScrollPane.setBorder(new TitledBorder( + new EmptyBorder(5, 5, 5, 5), "System.out")); + stdOutText.setEditable(false); + stdOutText.setFont(monoSpace); + + TextAreaUpdater stdOutUpdater = new TextAreaUpdater(new File( + PluginMain.PLUGIN_STDOUT_FILE), stdOutText); + stdOutUpdater.setName("IcedteaPlugin Console Thread(System.out)"); + + /* std err */ + + stdErrText = new JTextArea(); + JScrollPane stdErrScrollPane = new JScrollPane(stdErrText); + stdErrScrollPane.setBorder(new TitledBorder( + new EmptyBorder(5, 5, 5, 5), "System.err")); + stdErrText.setEditable(false); + stdErrText.setFont(monoSpace); + + TextAreaUpdater stdErrUpdater = new TextAreaUpdater(new File( + PluginMain.PLUGIN_STDERR_FILE), stdErrText); + stdErrUpdater.setName("IcedteaPlugin Console Thread(System.err)"); + + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + stdOutScrollPane, stdErrScrollPane); + + c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.gridheight = 10; + c.weighty = 1; + + contentPanel.add(splitPane, c); + + /* buttons */ + + c = new GridBagConstraints(); + c.gridy = 10; + c.gridheight = 1; + c.weightx = 0.5; + c.weighty = 0; + + JPanel buttonPanel = new JPanel(); + contentPanel.add(buttonPanel, c); + + JButton gcButton = new JButton("Run GC"); + buttonPanel.add(gcButton); + gcButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + printMemoryInfo(); + System.out.print("Performing Garbage Collection...."); + System.gc(); + System.out.println("Done"); + printMemoryInfo(); + } + + }); + + JButton finalizersButton = new JButton("Run Finalizers"); + buttonPanel.add(finalizersButton); + finalizersButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + printMemoryInfo(); + System.out.print("Running finalization...."); + Runtime.getRuntime().runFinalization(); + System.out.println("Done"); + printMemoryInfo(); + } + }); + + JButton memoryButton = new JButton("Memory Info"); + buttonPanel.add(memoryButton); + memoryButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + printMemoryInfo(); + } + + }); + + JButton systemPropertiesButton = new JButton("System Properties"); + buttonPanel.add(systemPropertiesButton); + systemPropertiesButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + printSystemProperties(); + } + + }); + + JButton classloadersButton = new JButton("Classloaders"); + buttonPanel.add(classloadersButton); + classloadersButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + printClassLoaders(); + } + + }); + + JButton threadListButton = new JButton("Thread List"); + buttonPanel.add(threadListButton); + threadListButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + printThreadInfo(); + } + + }); + + JButton closeButton = new JButton("Close"); + buttonPanel.add(closeButton); + closeButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + hideConsole(); + } + }); + } + }); + + stdOutUpdater.start(); + stdErrUpdater.start(); + + consoleWindow.add(contentPanel); + consoleWindow.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + consoleWindow.pack(); + consoleWindow.setSize(new Dimension(900, 600)); + consoleWindow.setMinimumSize(new Dimension(900, 300)); + + initialized = true; + + splitPane.setDividerLocation(0.5); + splitPane.setResizeWeight(0.5); + } + + public void showConsole() { + + if (!initialized) { + initialize(); + } + + consoleWindow.setVisible(true); + } + + public void hideConsole() { + consoleWindow.setVisible(false); + } + + protected void printSystemProperties() { + + System.out.println(" ----"); + System.out.println("System Properties:"); + System.out.println(); + Properties p = System.getProperties(); + Set keys = p.keySet(); + for (Object key : keys) { + System.out.println(key.toString() + ": " + p.get(key)); + } + + System.out.println(" ----"); + } + + private void printClassLoaders() { + System.out.println(" ----"); + System.out.println("Available Classloaders: "); + Set loaders = PluginAppletSecurityContext.classLoaders.keySet(); + for (ClassLoader loader: loaders) { + System.out.println(loader.getClass().getName() + "\n" + + " codebase = " + + PluginAppletSecurityContext.classLoaders.get(loader)); + } + System.out.println(" ----"); + } + + private void printMemoryInfo() { + System.out.println(" ----- "); + System.out.println(" Memory Info:"); + System.out.println(" Max Memory: " + + String.format("%1$10d", Runtime.getRuntime().maxMemory())); + System.out.println(" Total Memory: " + + String.format("%1$10d", Runtime.getRuntime().totalMemory())); + System.out.println(" Free Memory: " + + String.format("%1$10d", Runtime.getRuntime().freeMemory())); + System.out.println(" ----"); + + } + + private void printThreadInfo() { + Map map = Thread.getAllStackTraces(); + Set keys = map.keySet(); + for (Thread key : keys) { + System.out.println("Thread " + key.getId() + ": " + key.getName()); + for (StackTraceElement element : map.get(key)) { + System.out.println(" " + element); + } + + } + } + + public static void main(String[] args) { + + final JavaConsole console = new JavaConsole(); + + boolean toShowConsole = false; + + for (int i = 0; i < args.length; i++) { + if (args[i] == "--show-console") { + toShowConsole = true; + } + } + + if (toShowConsole) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + console.showConsole(); + } + }); + } + + } + + /** + * This thread updates the text on a JTextArea based on the text in a file + */ + class TextAreaUpdater extends Thread { + + File fileToRead; + JTextArea outputTextArea; + + public TextAreaUpdater(File file, JTextArea textArea) { + fileToRead = file; + outputTextArea = textArea; + setDaemon(true); + } + + public void run() { + + try { + BufferedReader reader = new BufferedReader(new FileReader( + fileToRead)); + String line; + while (true) { + while ((line = reader.readLine()) != null) { + outputTextArea.insert(line + "\n", outputTextArea + .getDocument().getLength()); + outputTextArea.setCaretPosition(outputTextArea + .getText().length()); + } + Thread.sleep(1000); + } + + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + + } + + } + +} diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/sun/applet/PasswordAuthenticationDialog.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/sun/applet/PasswordAuthenticationDialog.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,241 @@ +/* PasswordAuthenticationDialog -- requests authentication information from users + 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.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.PasswordAuthentication; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +/** + * Modal non-minimizable dialog to request http authentication credentials + */ + +public class PasswordAuthenticationDialog extends JDialog { + + private JLabel jlInfo = new JLabel(""); + private JTextField jtfUserName = new JTextField(); + private JPasswordField jpfPassword = new JPasswordField(); + private boolean userCancelled; + + public PasswordAuthenticationDialog() { + initialize(); + } + + /** + * Initialized the dialog components + */ + + public void initialize() { + + setTitle("IcedTea Java Plugin - Authorization needed to proceed"); + + setLayout(new GridBagLayout()); + + JLabel jlUserName = new JLabel("Username: "); + JLabel jlPassword = new JLabel("Password: "); + JButton jbOK = new JButton("OK"); + JButton jbCancel = new JButton("Cancel"); + + jtfUserName.setSize(20, 10); + jpfPassword.setSize(20, 10); + + GridBagConstraints c; + + c = new GridBagConstraints(); + c.fill = c.HORIZONTAL; + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + c.insets = new Insets(10, 5, 3, 3); + add(jlInfo, c); + + c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 1; + c.insets = new Insets(10, 5, 3, 3); + add(jlUserName, c); + + c = new GridBagConstraints(); + c.fill = c.HORIZONTAL; + c.gridx = 1; + c.gridy = 1; + c.insets = new Insets(10, 5, 3, 3); + c.weightx = 1.0; + add(jtfUserName, c); + + + c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 2; + c.insets = new Insets(5, 5, 3, 3); + add(jlPassword, c); + + c = new GridBagConstraints(); + c.fill = c.HORIZONTAL; + c.gridx = 1; + c.gridy = 2; + c.insets = new Insets(5, 5, 3, 3); + c.weightx = 1.0; + add(jpfPassword, c); + + c = new GridBagConstraints(); + c.anchor = c.SOUTHEAST; + c.gridx = 1; + c.gridy = 3; + c.insets = new Insets(5, 5, 3, 70); + c.weightx = 0.0; + add(jbCancel, c); + + c = new GridBagConstraints(); + c.anchor = c.SOUTHEAST; + c.gridx = 1; + c.gridy = 3; + c.insets = new Insets(5, 5, 3, 3); + c.weightx = 0.0; + add(jbOK, c); + + setMinimumSize(new Dimension(400,150)); + setMaximumSize(new Dimension(1024,150)); + setAlwaysOnTop(true); + + setSize(400,150); + setLocationRelativeTo(null); + + // OK => read supplied info and pass it on + jbOK.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = false; + dispose(); + } + }); + + // Cancel => discard supplied info and pass on an empty auth + jbCancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = true; + dispose(); + } + }); + + // "return" key in either user or password field => OK + + jtfUserName.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = false; + dispose(); + } + }); + + jpfPassword.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = false; + dispose(); + } + }); + } + + /** + * Present a dialog to the user asking them for authentication information + * + * @param hostThe host for with authentication is needed + * @param port The port being accessed + * @param prompt The prompt (realm) as presented by the server + * @param type The type of server (proxy/web) + * @return PasswordAuthentication containing the credentials (empty credentials if user cancelled) + */ + protected PasswordAuthentication askUser(String host, int port, String prompt, String type) { + PasswordAuthentication auth = null; + + host += port != -1 ? ":" + port : ""; + + // This frame is reusable. So reset everything first. + userCancelled = true; + jlInfo.setText("The " + type + " server at " + host + " is requesting authentication. It says \"" + prompt + "\""); + + try { + SwingUtilities.invokeAndWait( new Runnable() { + public void run() { + // show dialog to user + setVisible(true); + } + }); + + PluginDebug.debug("password dialog shown"); + + // wait until dialog is gone + while (this.isShowing()) { + try { + Thread.sleep(200); + } catch (InterruptedException ie) { + } + } + + PluginDebug.debug("password dialog closed"); + + if (!userCancelled) { + auth = new PasswordAuthentication(jtfUserName.getText(), jpfPassword.getText().toCharArray()); + } + } catch (Exception e) { + e.printStackTrace(); + + // Nothing else we can do. Empty auth will be returned + } + + return auth; + } + + public static void main(String[] args) { + PasswordAuthenticationDialog frame = new PasswordAuthenticationDialog(); + + PasswordAuthentication auth = frame.askUser("127.0.0.1", 3128, "Password for local proxy", "proxy"); + + System.err.println("Auth info: " + auth.getUserName() + ":" + new String(auth.getPassword())); + System.exit(0); + } +} diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/sun/applet/PluginAppletSecurityContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/sun/applet/PluginAppletSecurityContext.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,1307 @@ +/* PluginAppletSecurityContext -- execute plugin JNI messages + Copyright (C) 2008 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.io.File; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.AccessControlContext; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.AllPermission; +import java.security.BasicPermission; +import java.security.CodeSource; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + +import net.sourceforge.jnlp.runtime.JNLPRuntime; + + + +class Signature { + private String signature; + private int currentIndex; + private List typeList; + private static final char ARRAY = '['; + private static final char OBJECT = 'L'; + private static final char SIGNATURE_ENDCLASS = ';'; + private static final char SIGNATURE_FUNC = '('; + private static final char SIGNATURE_ENDFUNC = ')'; + private static final char VOID = 'V'; + private static final char BOOLEAN = 'Z'; + private static final char BYTE = 'B'; + private static final char CHARACTER = 'C'; + private static final char SHORT = 'S'; + private static final char INTEGER = 'I'; + private static final char LONG = 'J'; + private static final char FLOAT = 'F'; + private static final char DOUBLE = 'D'; + + private String nextTypeName() { + char key = signature.charAt(currentIndex++); + + switch (key) { + case ARRAY: + return nextTypeName() + "[]"; + + case OBJECT: + int endClass = signature.indexOf(SIGNATURE_ENDCLASS, currentIndex); + String retVal = signature.substring(currentIndex, endClass); + retVal = retVal.replace('/', '.'); + currentIndex = endClass + 1; + return retVal; + + // FIXME: generated bytecode with classes named after + // primitives will not work in this scheme -- those + // classes will be incorrectly treated as primitive + // types. + case VOID: + return "void"; + case BOOLEAN: + return "boolean"; + case BYTE: + return "byte"; + case CHARACTER: + return "char"; + case SHORT: + return "short"; + case INTEGER: + return "int"; + case LONG: + return "long"; + case FLOAT: + return "float"; + case DOUBLE: + return "double"; + + case SIGNATURE_ENDFUNC: + case SIGNATURE_FUNC: + return nextTypeName(); + + default: + throw new IllegalArgumentException( + "Invalid JNI signature character '" + key + "'"); + } + } + + public Signature(String signature, ClassLoader cl) { + this.signature = signature; + currentIndex = 0; + typeList = new ArrayList(10); + + String elem; + while (currentIndex < signature.length()) { + elem = nextTypeName(); + // System.out.println ("NEXT TYPE: " + elem); + Class primitive = primitiveNameToType(elem); + if (primitive != null) + typeList.add(primitive); + else { + // System.out.println ("HERE1"); + int dimsize = 0; + int n = elem.indexOf('['); + if (n != -1) { + // System.out.println ("HERE2"); + String arrayType = elem.substring(0, n); + dimsize++; + n = elem.indexOf('[', n + 1); + // System.out.println ("HERE2.5"); + while (n != -1) { + dimsize++; + n = elem.indexOf('[', n + 1); + // System.out.println ("HERE2.8"); + } + int[] dims = new int[dimsize]; + primitive = primitiveNameToType(arrayType); + // System.out.println ("HERE3"); + if (primitive != null) { + typeList.add(Array.newInstance(primitive, dims) + .getClass()); + // System.out.println ("HERE4"); + } else + typeList.add(Array.newInstance( + getClass(arrayType, cl), dims).getClass()); + } else { + typeList.add(getClass(elem, cl)); + } + } + } + if (typeList.size() == 0) { + throw new IllegalArgumentException("Invalid JNI signature '" + + signature + "'"); + } + } + + public static Class getClass(String name, ClassLoader cl) { + + Class c = null; + + try { + c = Class.forName(name); + } catch (ClassNotFoundException cnfe) { + + PluginDebug.debug("Class " + name + " not found in primordial loader. Looking in " + cl); + try { + c = cl.loadClass(name); + } catch (ClassNotFoundException e) { + throw (new RuntimeException(new ClassNotFoundException("Unable to find class " + name))); + } + } + + return c; + } + + public static Class primitiveNameToType(String name) { + if (name.equals("void")) + return Void.TYPE; + else if (name.equals("boolean")) + return Boolean.TYPE; + else if (name.equals("byte")) + return Byte.TYPE; + else if (name.equals("char")) + return Character.TYPE; + else if (name.equals("short")) + return Short.TYPE; + else if (name.equals("int")) + return Integer.TYPE; + else if (name.equals("long")) + return Long.TYPE; + else if (name.equals("float")) + return Float.TYPE; + else if (name.equals("double")) + return Double.TYPE; + else + return null; + } + + public Class[] getClassArray() { + return typeList.subList(0, typeList.size() - 1).toArray(new Class[] {}); + } +} + +public class PluginAppletSecurityContext { + + public static Hashtable classLoaders = new Hashtable(); + + // FIXME: make private + public PluginObjectStore store = new PluginObjectStore(); + private Throwable throwable = null; + private ClassLoader liveconnectLoader = ClassLoader.getSystemClassLoader(); + int identifier = 0; + + public static PluginStreamHandler streamhandler; + + long startTime = 0; + + public PluginAppletSecurityContext(int identifier) { + this.identifier = identifier; + + // also, override the basedir, use a different one for the plugin + File f = new File(System.getProperty("user.home") + "/.icedteaplugin/"); + f.mkdir(); + JNLPRuntime.setBaseDir(f); + + // We need a security manager.. and since there is a good chance that + // 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.disableExit(); + + this.classLoaders.put(liveconnectLoader, "file://"); + } + + private static V parseCall(String s, ClassLoader cl, Class c) { + if (c == Integer.class) + return (V) new Integer(s); + else if (c == String.class) + return (V) new String(s); + else if (c == Signature.class) + return (V) new Signature(s, cl); + else + throw new RuntimeException("Unexpected call value."); + } + + private Object parseArgs(String s, Class c) { + if (c == Boolean.TYPE || c == Boolean.class) + return new Boolean(s); + else if (c == Byte.TYPE || c == Byte.class) + return new Byte(s); + else if (c == Character.TYPE || c == Character.class) { + String[] bytes = s.split("_"); + int low = Integer.parseInt(bytes[0]); + int high = Integer.parseInt(bytes[1]); + int full = ((high << 8) & 0x0ff00) | (low & 0x0ff); + return new Character((char) full); + } else if (c == Short.TYPE || c == Short.class) + return new Short(s); + else if (c == Integer.TYPE || c == Integer.class) + return new Integer(s); + else if (c == Long.TYPE || c == Long.class) + return new Long(s); + else if (c == Float.TYPE || c == Float.class) + return new Float(s); + else if (c == Double.TYPE || c == Double.class) + return new Double(s); + else + return store.getObject(new Integer(s)); + } + + public void associateSrc(ClassLoader cl, String src) { + PluginDebug.debug("Associating " + cl + " with " + src); + this.classLoaders.put(cl, src); + } + + public static void setStreamhandler(PluginStreamHandler sh) { + streamhandler = sh; + } + + public void handleMessage(int reference, String src, AccessControlContext callContext, String message) { + + startTime = new java.util.Date().getTime(); + + try { + if (message.startsWith("FindClass")) { + ClassLoader cl = null; + Class c = null; + cl = liveconnectLoader; + String className = message.substring("FindClass".length() + 1) + .replace('/', '.'); + + try { + c = cl.loadClass(className); + store.reference(c); + write(reference, "FindClass " + store.getIdentifier(c)); + } catch (ClassNotFoundException cnfe) { + write(reference, "FindClass 0"); + } + + } else if (message.startsWith("GetStaticMethodID") + || message.startsWith("GetMethodID")) { + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + String methodName = parseCall(args[2], null, String.class); + Signature signature = parseCall(args[3], ((Class) store.getObject(classID)).getClassLoader(), Signature.class); + Object[] a = signature.getClassArray(); + + Class c = (Class) store.getObject(classID); + Method m = null; + Constructor cs = null; + Object o = null; + if (methodName.equals("") + || methodName.equals("")) { + o = cs = c.getConstructor(signature.getClassArray()); + store.reference(cs); + } else { + o = m = c.getMethod(methodName, signature.getClassArray()); + store.reference(m); + } + PluginDebug.debug(o + " has id " + store.getIdentifier(o)); + write(reference, args[0] + " " + store.getIdentifier(o)); + } else if (message.startsWith("GetStaticFieldID") + || message.startsWith("GetFieldID")) { + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + String fieldName = parseCall(args[2], null, String.class); + Signature signature = parseCall(args[3], ((Class) store.getObject(classID)).getClassLoader(), Signature.class); + + Class c = (Class) store.getObject(classID); + Field f = null; + f = c.getField(fieldName); + + store.reference(f); + + write(reference, "GetStaticFieldID " + store.getIdentifier(f)); + } else if (message.startsWith("GetStaticField")) { + String[] args = message.split(" "); + String type = parseCall(args[1], null, String.class); + Integer classID = parseCall(args[1], null, Integer.class); + Integer fieldID = parseCall(args[2], null, Integer.class); + + final Class c = (Class) store.getObject(classID); + final Field f = (Field) store.getObject(fieldID); + + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + checkPermission(src, c, acc); + + Object ret = AccessController.doPrivileged(new PrivilegedAction () { + public Object run() { + try { + return f.get(c); + } catch (Throwable t) { + return t; + } + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + // System.out.println ("FIELD VALUE: " + ret); + if (ret == null) { + write(reference, "GetStaticField 0"); + } else if (f.getType() == Boolean.TYPE + || f.getType() == Byte.TYPE + || f.getType() == Character.TYPE + || f.getType() == Short.TYPE + || f.getType() == Integer.TYPE + || f.getType() == Long.TYPE + || f.getType() == Float.TYPE + || f.getType() == Double.TYPE) { + write(reference, "GetStaticField " + ret); + } else { + // Track returned object. + store.reference(ret); + write(reference, "GetStaticField " + + store.getIdentifier(ret)); + } + } else if (message.startsWith("SetStaticField")) { + String[] args = message.split(" "); + String type = parseCall(args[1], null, String.class); + Integer classID = parseCall(args[2], null, Integer.class); + Integer fieldID = parseCall(args[3], null, Integer.class); + + Object value = null; + if (Signature.primitiveNameToType(type) != null) { + value = parseArgs(args[4], Signature + .primitiveNameToType(type)); + // System.out.println ("HERE1: " + value); + } else { + value = parseArgs(args[3], Object.class); + // System.out.println ("HERE2: " + value); + } + + final Class c = (Class) store.getObject(classID); + final Field f = (Field) store.getObject(fieldID); + + final Object fValue = value; + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + checkPermission(src, c, acc); + + Object ret = AccessController.doPrivileged(new PrivilegedAction () { + public Object run() { + try { + f.set(c, fValue); + } catch (Throwable t) { + return t; + } + + return null; + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + write(reference, "SetStaticField"); + } else if (message.startsWith("SetField")) { + String[] args = message.split(" "); + String type = parseCall(args[1], null, String.class); + Integer objectID = parseCall(args[2], null, Integer.class); + Integer fieldID = parseCall(args[3], null, Integer.class); + + Object value = null; + if (Signature.primitiveNameToType(type) != null) { + value = parseArgs(args[4], Signature + .primitiveNameToType(type)); + // System.out.println ("HERE1: " + value); + } else { + value = parseArgs(args[3], Object.class); + // System.out.println ("HERE2: " + value); + } + + final Object o = (Object) store.getObject(objectID); + final Field f = (Field) store.getObject(fieldID); + + final Object fValue = value; + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + checkPermission(src, o.getClass(), acc); + + Object ret = AccessController.doPrivileged(new PrivilegedAction () { + public Object run() { + try { + f.set(o, fValue); + } catch (Throwable t) { + return t; + } + + return null; + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + write(reference, "SetField"); + } else if (message.startsWith("GetObjectArrayElement")) { + String[] args = message.split(" "); + Integer arrayID = parseCall(args[1], null, Integer.class); + Integer index = parseCall(args[2], null, Integer.class); + + Object[] o = (Object[]) store.getObject(arrayID); + Object ret = null; + + ret = o[index]; + + // Track returned object. + store.reference(ret); + // System.out.println ("array element: " + index + " " + ret); + write(reference, "GetObjectArrayElement " + + store.getIdentifier(ret)); + } else if (message.startsWith("SetObjectArrayElement")) { + String[] args = message.split(" "); + Integer arrayID = parseCall(args[1], null, Integer.class); + Integer index = parseCall(args[2], null, Integer.class); + Integer objectID = parseCall(args[3], null, Integer.class); + + Object[] o = (Object[]) store.getObject(arrayID); + Object toSet = (Object) store.getObject(objectID); + + o[index] = toSet; + + write(reference, "SetObjectArrayElement"); + } else if (message.startsWith("GetArrayLength")) { + String[] args = message.split(" "); + Integer arrayID = parseCall(args[1], null, Integer.class); + + //System.out.println("ARRAYID: " + arrayID); + Object o = (Object) store.getObject(arrayID); + int len = 0; + len = Array.getLength(o); + // System.out.println ("Returning array length: " + len); + + // System.out.println ("array length: " + o + " " + len); + write(reference, "GetArrayLength " + Array.getLength(o)); + } else if (message.startsWith("GetField")) { + String[] args = message.split(" "); + String type = parseCall(args[1], null, String.class); + Integer objectID = parseCall(args[1], null, Integer.class); + Integer fieldID = parseCall(args[2], null, Integer.class); + + final Object o = (Object) store.getObject(objectID); + final Field f = (Field) store.getObject(fieldID); + + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + checkPermission(src, o.getClass(), acc); + + Object ret = AccessController.doPrivileged(new PrivilegedAction () { + public Object run() { + try { + return f.get(o); + } catch (Throwable t) { + return t; + } + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + // System.out.println ("FIELD VALUE: " + ret); + if (ret == null) { + write(reference, "GetField 0"); + } else if (f.getType() == Boolean.TYPE + || f.getType() == Byte.TYPE + || f.getType() == Character.TYPE + || f.getType() == Short.TYPE + || f.getType() == Integer.TYPE + || f.getType() == Long.TYPE + || f.getType() == Float.TYPE + || f.getType() == Double.TYPE) { + write(reference, "GetField " + ret); + } else { + // Track returned object. + store.reference(ret); + write(reference, "GetField " + store.getIdentifier(ret)); + } + } else if (message.startsWith("GetObjectClass")) { + int oid = Integer.parseInt(message.substring("GetObjectClass" + .length() + 1)); + // System.out.println ("GETTING CLASS FOR: " + oid); + Class c = store.getObject(oid).getClass(); + // System.out.println (" OBJ: " + store.getObject(oid)); + // System.out.println (" CLS: " + c); + store.reference(c); + + write(reference, "GetObjectClass " + store.getIdentifier(c)); + } else if (message.startsWith("CallStaticMethod")) { + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + Integer methodID = parseCall(args[2], null, Integer.class); + + PluginDebug.debug("GETTING: " + methodID); + final Method m = (Method) store.getObject(methodID); + PluginDebug.debug("GOT: " + m); + Class[] argTypes = m.getParameterTypes(); + + Object[] arguments = new Object[argTypes.length]; + for (int i = 0; i < argTypes.length; i++) { + arguments[i] = parseArgs(args[3 + i], argTypes[i]); + // System.out.println ("GOT ARG: " + argTypes[i] + " " + + // arguments[i]); + } + + // System.out.println ("Calling " + m); + + final Object[] fArguments = arguments; + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + Class c = (Class) store.getObject(classID); + checkPermission(src, c, acc); + + Object ret = AccessController.doPrivileged(new PrivilegedAction () { + public Object run() { + try { + return m.invoke(null, fArguments); + } catch (Throwable t) { + return t; + } + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + // if (ret != null) + // System.out.println ("RETURN VALUE: " + ret + " " + + // ret.getClass()); + // else + // System.out.println ("RETURN VALUE: " + ret); + if (ret == null) { + write(reference, "CallStaticMethod void"); + } else if (m.getReturnType() == Boolean.TYPE + || m.getReturnType() == Byte.TYPE + || m.getReturnType() == Short.TYPE + || m.getReturnType() == Integer.TYPE + || m.getReturnType() == Long.TYPE + || m.getReturnType() == Float.TYPE + || m.getReturnType() == Double.TYPE) { + write(reference, "CallStaticMethod " + ret); + } else if (m.getReturnType() == Character.TYPE) { + char ch = (Character) ret; + int high = (((int) ch) >> 8) & 0x0ff; + int low = ((int) ch) & 0x0ff; + write(reference, "CallStaticMethod " + low + "_" + high); + } else { + // Track returned object. + store.reference(ret); + write(reference, "CallStaticMethod " + + store.getIdentifier(ret)); + } + } else if (message.startsWith("CallMethod")) { + String[] args = message.split(" "); + Integer objectID = parseCall(args[1], null, Integer.class); + Integer methodID = parseCall(args[2], null, Integer.class); + + final Object o = (Object) store.getObject(objectID); + final Method m = (Method) store.getObject(methodID); + Class[] argTypes = m.getParameterTypes(); + + Object[] arguments = new Object[argTypes.length]; + for (int i = 0; i < argTypes.length; i++) { + arguments[i] = parseArgs(args[3 + i], argTypes[i]); + PluginDebug.debug("GOT ARG: " + argTypes[i] + " " + + arguments[i]); + } + + String collapsedArgs = ""; + for (String s : args) { + collapsedArgs += " " + s; + } + + PluginDebug.debug("Calling method " + m + " on object " + o + + " with " + arguments); + + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + checkPermission(src, o.getClass(), acc); + + final Object[] fArguments = arguments; + Object ret = AccessController.doPrivileged(new PrivilegedAction () { + public Object run() { + try { + return m.invoke(o, fArguments); + } catch (Throwable t) { + return t; + } + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + String retO; + if (ret == null) { + retO = "null"; + } else { + retO = ret.getClass().toString(); + } + + PluginDebug.debug("Calling " + m + " on " + o + " with " + + collapsedArgs + " and that returned: " + ret + + " of type " + retO); + + if (ret == null) { + write(reference, "CallMethod void"); + } else if (m.getReturnType() == Boolean.TYPE + || m.getReturnType() == Byte.TYPE + || m.getReturnType() == Short.TYPE + || m.getReturnType() == Integer.TYPE + || m.getReturnType() == Long.TYPE + || m.getReturnType() == Float.TYPE + || m.getReturnType() == Double.TYPE) { + write(reference, "CallMethod " + ret); + } else if (m.getReturnType() == Character.TYPE) { + char ch = (Character) ret; + int high = (((int) ch) >> 8) & 0x0ff; + int low = ((int) ch) & 0x0ff; + write(reference, "CallMethod " + low + "_" + high); + } else { + // Track returned object. + store.reference(ret); + write(reference, "CallMethod " + store.getIdentifier(ret)); + } + } else if (message.startsWith("GetSuperclass")) { + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + Class c = null; + Class ret = null; + + c = (Class) store.getObject(classID); + ret = c.getSuperclass(); + store.reference(ret); + + write(reference, "GetSuperclass " + store.getIdentifier(ret)); + } else if (message.startsWith("IsAssignableFrom")) { + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + Integer superclassID = parseCall(args[2], null, Integer.class); + + boolean result = false; + Class clz = (Class) store.getObject(classID); + Class sup = (Class) store.getObject(superclassID); + + result = sup.isAssignableFrom(clz); + + write(reference, "IsAssignableFrom " + (result ? "1" : "0")); + } else if (message.startsWith("IsInstanceOf")) { + String[] args = message.split(" "); + Integer objectID = parseCall(args[1], null, Integer.class); + Integer classID = parseCall(args[2], null, Integer.class); + + boolean result = false; + Object o = (Object) store.getObject(objectID); + Class c = (Class) store.getObject(classID); + + result = c.isInstance(o); + + write(reference, "IsInstanceOf " + (result ? "1" : "0")); + } else if (message.startsWith("GetStringUTFLength")) { + String[] args = message.split(" "); + Integer stringID = parseCall(args[1], null, Integer.class); + + String o = null; + byte[] b = null; + o = (String) store.getObject(stringID); + b = o.getBytes("UTF-8"); + // System.out.println ("STRING UTF-8 LENGTH: " + o + " " + + // b.length); + + write(reference, "GetStringUTFLength " + o.length()); + } else if (message.startsWith("GetStringLength")) { + String[] args = message.split(" "); + Integer stringID = parseCall(args[1], null, Integer.class); + + String o = null; + byte[] b = null; + o = (String) store.getObject(stringID); + b = o.getBytes("UTF-16LE"); + // System.out.println ("STRING UTF-16 LENGTH: " + o + " " + + // b.length); + + // System.out.println ("Java: GetStringLength " + b.length); + write(reference, "GetStringLength " + o.length()); + } else if (message.startsWith("GetStringUTFChars")) { + String[] args = message.split(" "); + Integer stringID = parseCall(args[1], null, Integer.class); + + String o = null; + byte[] b = null; + StringBuffer buf = null; + o = (String) store.getObject(stringID); + b = o.getBytes("UTF-8"); + buf = new StringBuffer(b.length * 2); + buf.append(b.length); + for (int i = 0; i < b.length; i++) + buf + .append(" " + + Integer + .toString(((int) b[i]) & 0x0ff, 16)); + + // System.out.println ("Java: GetStringUTFChars: " + o); + // //System.out.println ("String UTF BYTES: " + buf); + write(reference, "GetStringUTFChars " + buf); + } else if (message.startsWith("GetStringChars")) { + String[] args = message.split(" "); + Integer stringID = parseCall(args[1], null, Integer.class); + + String o = null; + byte[] b = null; + StringBuffer buf = null; + o = (String) store.getObject(stringID); + // FIXME: LiveConnect uses UCS-2. + b = o.getBytes("UTF-16LE"); + buf = new StringBuffer(b.length * 2); + buf.append(b.length); + for (int i = 0; i < b.length; i++) + buf + .append(" " + + Integer + .toString(((int) b[i]) & 0x0ff, 16)); + + PluginDebug.debug("Java: GetStringChars: " + o); + PluginDebug.debug(" String BYTES: " + buf); + write(reference, "GetStringChars " + buf); + } else if (message.startsWith("NewArray")) { + String[] args = message.split(" "); + String type = parseCall(args[1], null, String.class); + Integer length = parseCall(args[2], null, Integer.class); + + // System.out.println ("CALLING: NewArray: " + type + " " + + // length + " " + // + Signature.primitiveNameToType(type)); + + Object newArray = null; + newArray = Array.newInstance(Signature + .primitiveNameToType(type), length); + + store.reference(newArray); + write(reference, "NewArray " + store.getIdentifier(newArray)); + } else if (message.startsWith("NewObjectArray")) { + String[] args = message.split(" "); + Integer length = parseCall(args[1], null, Integer.class); + Integer classID = parseCall(args[2], null, Integer.class); + Integer objectID = parseCall(args[3], null, Integer.class); + + // System.out.println ("CALLING: NewObjectArray: " + + // classID + " " + length + " " + // + objectID); + + Object newArray = null; + newArray = Array.newInstance((Class) store.getObject(classID), + length); + + Object[] array = (Object[]) newArray; + for (int i = 0; i < array.length; i++) + array[i] = store.getObject(objectID); + store.reference(newArray); + write(reference, "NewObjectArray " + + store.getIdentifier(newArray)); + } else if (message.startsWith("NewObject")) { + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + Integer methodID = parseCall(args[2], null, Integer.class); + + final Constructor m = (Constructor) store.getObject(methodID); + Class[] argTypes = m.getParameterTypes(); + + // System.out.println ("NEWOBJ: HERE1"); + Object[] arguments = new Object[argTypes.length]; + // System.out.println ("NEWOBJ: HERE2"); + for (int i = 0; i < argTypes.length; i++) { + arguments[i] = parseArgs(args[3 + i], argTypes[i]); + // System.out.println ("NEWOBJ: GOT ARG: " + arguments[i]); + } + + final Object[] fArguments = arguments; + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + + Class c = (Class) store.getObject(classID); + checkPermission(src, c, acc); + + Object ret = AccessController.doPrivileged(new PrivilegedAction () { + public Object run() { + try { + return m.newInstance(fArguments); + } catch (Throwable t) { + return t; + } + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + store.reference(ret); + + write(reference, "NewObject " + store.getIdentifier(ret)); + + } else if (message.startsWith("NewString")) { + PluginDebug.debug("MESSAGE: " + message); + String[] args = message.split(" "); + Integer strlength = parseCall(args[1], null, Integer.class); + int bytelength = 2 * strlength; + byte[] byteArray = new byte[bytelength]; + String ret = null; + for (int i = 0; i < strlength; i++) { + int c = parseCall(args[2 + i], null, Integer.class); + PluginDebug.debug("char " + i + " " + c); + // Low. + byteArray[2 * i] = (byte) (c & 0x0ff); + // High. + byteArray[2 * i + 1] = (byte) ((c >> 8) & 0x0ff); + } + ret = new String(byteArray, 0, bytelength, "UTF-16LE"); + PluginDebug.debug("NEWSTRING: " + ret); + + // System.out.println ("NEWOBJ: CALLED: " + ret); + // System.out.println ("NEWOBJ: CALLED: " + + // store.getObject(ret)); + store.reference(ret); + write(reference, "NewString " + store.getIdentifier(ret)); + } else if (message.startsWith("NewStringUTF")) { + PluginDebug.debug("MESSAGE: " + message); + String[] args = message.split(" "); + byte[] byteArray = new byte[60]; + String ret = null; + int i = 0; + int c = 0; + while (((byte) c) != 0) { + c = parseCall(args[1 + i], null, Integer.class); + byteArray[i] = (byte) c; + i++; + if (i == byteArray.length) { + byte[] newByteArray = new byte[2 * byteArray.length]; + System.arraycopy(byteArray, 0, newByteArray, 0, + byteArray.length); + byteArray = newByteArray; + } + } + byteArray[i] = (byte) 0; + ret = new String(byteArray, "UTF-8"); + PluginDebug.debug("NEWSTRINGUTF: " + ret); + + store.reference(ret); + write(reference, "NewStringUTF " + store.getIdentifier(ret)); + } else if (message.startsWith("ExceptionOccurred")) { + PluginDebug.debug("EXCEPTION: " + throwable); + if (throwable != null) + store.reference(throwable); + write(reference, "ExceptionOccurred " + + store.getIdentifier(throwable)); + } else if (message.startsWith("ExceptionClear")) { + if (throwable != null && store.contains(throwable)) + store.unreference(store.getIdentifier(throwable)); + throwable = null; + write(reference, "ExceptionClear"); + } else if (message.startsWith("DeleteGlobalRef")) { + String[] args = message.split(" "); + Integer id = parseCall(args[1], null, Integer.class); + store.unreference(id); + write(reference, "DeleteGlobalRef"); + } else if (message.startsWith("DeleteLocalRef")) { + String[] args = message.split(" "); + Integer id = parseCall(args[1], null, Integer.class); + store.unreference(id); + write(reference, "DeleteLocalRef"); + } else if (message.startsWith("NewGlobalRef")) { + String[] args = message.split(" "); + Integer id = parseCall(args[1], null, Integer.class); + store.reference(store.getObject(id)); + write(reference, "NewGlobalRef " + id); + } + } catch (Throwable t) { + t.printStackTrace(); + String msg = t.getCause() != null ? t.getCause().getMessage() : t.getMessage(); + + // add an identifier string to let javaside know of the type of error + // check for cause as well, since the top level exception will be InvocationTargetException in most cases + if (t instanceof AccessControlException || t.getCause() instanceof AccessControlException) { + msg = "LiveConnectPermissionNeeded " + msg; + } + + this.streamhandler.write("instance " + identifier + " reference " + reference + " Error " + msg); + + // ExceptionOccured is only called after Callmethod() by mozilla. So + // for exceptions that are not related to CallMethod, we need a way + // to log them. This is how we do it.. send an error message to the + // c++ side to let it know that something went wrong, and it will do + // the right thing to let mozilla know + + // Store the cause as the actual exception. This is needed because + // the exception we get here will always be an + // "InvocationTargetException" due to the use of reflection above + if (message.startsWith("CallMethod") || message.startsWith("CallStaticMethod")) + throwable = t.getCause(); + } + + } + + /** + * Checks if the calling script is allowed to access the specified class + * + * See http://java.sun.com/j2se/1.3/docs/guide/plugin/security.html#liveconnect for details + * + * @param jsSrc The source of the script + * @param target The target class that the script is trying to access + * @param acc AccessControlContext for this execution + * @throws AccessControlException If the script has insufficient permissions + */ + public void checkPermission(String jsSrc, Class target, AccessControlContext acc) throws AccessControlException { + + // target classloader == null => primordial loader. Allow this. + if (target.getClassLoader() == null) + return; + + String classSrc = this.classLoaders.get(target.getClassLoader()); + + PluginDebug.debug("target = " + target + " jsSrc=" + jsSrc + " classSrc=" + classSrc); + + // if src is not a file and class loader does not map to the same base, UniversalBrowserRead (BrowserReadPermission) must be set + if (!jsSrc.equals("file://") && !jsSrc.equals("[System]") && !classSrc.equals(jsSrc)) { + acc.checkPermission(new BrowserReadPermission()); + } + } + + private void write(int reference, String message) { + PluginDebug.debug("appletviewer writing " + message); + streamhandler.write("context " + identifier + " reference " + reference + + " " + message); + } + + public void prePopulateLCClasses() { + + int classID; + + prepopulateClass("netscape/javascript/JSObject"); + classID = prepopulateClass("netscape/javascript/JSException"); + prepopulateMethod(classID, "", "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;I)V"); + prepopulateMethod(classID, "", "(ILjava/lang/Object;)V"); + prepopulateField(classID, "lineno"); + prepopulateField(classID, "tokenIndex"); + prepopulateField(classID, "source"); + prepopulateField(classID, "filename"); + prepopulateField(classID, "wrappedExceptionType"); + prepopulateField(classID, "wrappedException"); + + classID = prepopulateClass("netscape/javascript/JSUtil"); + prepopulateMethod(classID, "getStackTrace", "(Ljava/lang/Throwable;)Ljava/lang/String;"); + + prepopulateClass("java/lang/Object"); + classID = prepopulateClass("java/lang/Class"); + prepopulateMethod(classID, "getMethods", "()[Ljava/lang/reflect/Method;"); + prepopulateMethod(classID, "getConstructors", "()[Ljava/lang/reflect/Constructor;"); + prepopulateMethod(classID, "getFields", "()[Ljava/lang/reflect/Field;"); + prepopulateMethod(classID, "getName", "()Ljava/lang/String;"); + prepopulateMethod(classID, "isArray", "()Z"); + prepopulateMethod(classID, "getComponentType", "()Ljava/lang/Class;"); + prepopulateMethod(classID, "getModifiers", "()I"); + + + classID = prepopulateClass("java/lang/reflect/Method"); + prepopulateMethod(classID, "getName", "()Ljava/lang/String;"); + prepopulateMethod(classID, "getParameterTypes", "()[Ljava/lang/Class;"); + prepopulateMethod(classID, "getReturnType", "()Ljava/lang/Class;"); + prepopulateMethod(classID, "getModifiers", "()I"); + + classID = prepopulateClass("java/lang/reflect/Constructor"); + prepopulateMethod(classID, "getParameterTypes", "()[Ljava/lang/Class;"); + prepopulateMethod(classID, "getModifiers", "()I"); + + classID = prepopulateClass("java/lang/reflect/Field"); + prepopulateMethod(classID, "getName", "()Ljava/lang/String;"); + prepopulateMethod(classID, "getType", "()Ljava/lang/Class;"); + prepopulateMethod(classID, "getModifiers", "()I"); + + classID = prepopulateClass("java/lang/reflect/Array"); + prepopulateMethod(classID, "newInstance", "(Ljava/lang/Class;I)Ljava/lang/Object;"); + + classID = prepopulateClass("java/lang/Throwable"); + prepopulateMethod(classID, "toString", "()Ljava/lang/String;"); + prepopulateMethod(classID, "getMessage", "()Ljava/lang/String;"); + + classID = prepopulateClass("java/lang/System"); + prepopulateMethod(classID, "identityHashCode", "(Ljava/lang/Object;)I"); + + classID = prepopulateClass("java/lang/Boolean"); + prepopulateMethod(classID, "booleanValue", "()D"); + prepopulateMethod(classID, "", "(Z)V"); + + classID = prepopulateClass("java/lang/Double"); + prepopulateMethod(classID, "doubleValue", "()D"); + prepopulateMethod(classID, "", "(D)V"); + + classID = prepopulateClass("java/lang/Void"); + prepopulateField(classID, "TYPE"); + + prepopulateClass("java/lang/String"); + prepopulateClass("java/applet/Applet"); + } + + private int prepopulateClass(String name) { + name = name.replace('/', '.'); + ClassLoader cl = liveconnectLoader; + Class c = null; + + try { + c = cl.loadClass(name); + store.reference(c); + } catch (ClassNotFoundException cnfe) { + // do nothing ... this should never happen + cnfe.printStackTrace(); + } + + return store.getIdentifier(c); + } + + private int prepopulateMethod(int classID, String methodName, String signatureStr) { + Signature signature = parseCall(signatureStr, ((Class) store.getObject(classID)).getClassLoader(), Signature.class); + Object[] a = signature.getClassArray(); + + Class c = (Class) store.getObject(classID); + Method m = null; + Constructor cs = null; + Object o = null; + + try { + if (methodName.equals("") + || methodName.equals("")) { + o = cs = c.getConstructor(signature.getClassArray()); + store.reference(cs); + } else { + o = m = c.getMethod(methodName, signature.getClassArray()); + store.reference(m); + } + } catch (NoSuchMethodException e) { + // should never happen + e.printStackTrace(); + } + + return store.getIdentifier(m); + } + + private int prepopulateField(int classID, String fieldName) { + + Class c = (Class) store.getObject(classID); + Field f = null; + try { + f = c.getField(fieldName); + } catch (SecurityException e) { + // should never happen + e.printStackTrace(); + } catch (NoSuchFieldException e) { + // should never happen + e.printStackTrace(); + } + + store.reference(f); + return store.getIdentifier(f); + } + + public void dumpStore() { + store.dump(); + } + + public Object getObject(int identifier) { + return store.getObject(identifier); + } + + public int getIdentifier(Object o) { + return store.getIdentifier(o); + } + + public void store(Object o) { + store.reference(o); + } + + /** + * Returns a "closed" AccessControlContext i.e. no permissions to get out of sandbox. + */ + public AccessControlContext getClosedAccessControlContext() { + // Deny everything + Permissions p = new Permissions(); + ProtectionDomain pd = new ProtectionDomain(null, p); + return new AccessControlContext(new ProtectionDomain[] {pd}); + } + + public AccessControlContext getAccessControlContext(String[] nsPrivilegeList, String src) { + +/* + for (int i=0; i < nsPrivilegeList.length; i++) { + String privilege = nsPrivilegeList[i]; + + if (privilege.equals("UniversalAccept")) { + SocketPermission sp = new SocketPermission("*", "accept,resolve"); + grantedPermissions.add(sp); + } else if (privilege.equals("UniversalAwtEventQueueAccess")) { + AWTPermission awtp = new AWTPermission("accessEventQueue"); + grantedPermissions.add(awtp); + } else if (privilege.equals("UniversalConnect")) { + SocketPermission sp = new SocketPermission("*", "connect,resolve"); + grantedPermissions.add(sp); + } else if (privilege.equals("UniversalListen")) { + SocketPermission sp = new SocketPermission("*", "listen,resolve"); + grantedPermissions.add(sp); + } else if (privilege.equals("UniversalExecAccess")) { + FilePermission fp = new FilePermission("<>", "execute"); + RuntimePermission rtp = new RuntimePermission("setIO"); + grantedPermissions.add(fp); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalExitAccess")) { + // Doesn't matter what the permissions are. Do not allow VM to exit.. we + // use a single VM for the entire browser lifecycle once invoked, we + // cannot let it exit + + //RuntimePermission rtp = new RuntimePermission("exitVM.*"); + //grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalFileDelete")) { + FilePermission fp = new FilePermission("<>", "delete"); + grantedPermissions.add(fp); + } else if (privilege.equals("UniversalFileRead")) { + FilePermission fp = new FilePermission("<>", "read"); + grantedPermissions.add(fp); + } else if (privilege.equals("UniversalFileWrite")) { + FilePermission fp = new FilePermission("<>", "write"); + grantedPermissions.add(fp); + } else if (privilege.equals("UniversalFdRead")) { + RuntimePermission rtp = new RuntimePermission("readFileDescriptor"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalFdWrite")) { + RuntimePermission rtp = new RuntimePermission("writeFileDescriptor"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalLinkAccess")) { + RuntimePermission rtp = new RuntimePermission("loadLibrary.*"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalListen")) { + SocketPermission sp = new SocketPermission("*", "listen"); + grantedPermissions.add(sp); + } else if (privilege.equals("UniversalMulticast")) { + SocketPermission sp = new SocketPermission("*", "accept,connect,resolve"); + grantedPermissions.add(sp); + } else if (privilege.equals("UniversalPackageAccess")) { + RuntimePermission rtp = new RuntimePermission("defineClassInPackage.*"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalPackageDefinition")) { + RuntimePermission rtp = new RuntimePermission("accessClassInPackage.*"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalPrintJobAccess")) { + RuntimePermission rtp = new RuntimePermission("queuePrintJob"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalPropertyRead")) { + PropertyPermission pp = new PropertyPermission("*", "read"); + grantedPermissions.add(pp); + } else if (privilege.equals("UniversalPropertyWrite")) { + PropertyPermission pp = new PropertyPermission("*", "write"); + grantedPermissions.add(pp); + } else if (privilege.equals("UniversalSetFactory")) { + RuntimePermission rtp = new RuntimePermission("setFactory"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalSystemClipboardAccess")) { + AWTPermission awtp = new AWTPermission("accessClipboard"); + grantedPermissions.add(awtp); + } else if (privilege.equals("UniversalThreadAccess")) { + RuntimePermission rtp1 = new RuntimePermission("modifyThread"); + RuntimePermission rtp2 = new RuntimePermission("stopThread"); + grantedPermissions.add(rtp1); + grantedPermissions.add(rtp2); + } else if (privilege.equals("UniversalThreadGroupAccess")) { + RuntimePermission rtp1 = new RuntimePermission("modifyThreadGroup"); + RuntimePermission rtp2 = new RuntimePermission("modifyThread"); + RuntimePermission rtp3 = new RuntimePermission("stopThread"); + grantedPermissions.add(rtp1); + grantedPermissions.add(rtp2); + grantedPermissions.add(rtp3); + } else if (privilege.equals("UniversalTopLevelWindow")) { + AWTPermission awtp = new AWTPermission("topLevelWindow"); + grantedPermissions.add(awtp); + } else if (privilege.equals("UniversalBrowserRead")) { + BrowserReadPermission bp = new BrowserReadPermission(); + grantedPermissions.add(bp); + } else if (privilege.equals("UniversalJavaPermissions")) { + AllPermission ap = new AllPermission(); + grantedPermissions.add(ap); + } + } + + // what to do with these is unknown: UniversalConnectWithRedirect, UniversalDialogModality, UniversalSendMail, LimitedInstall, FullInstall, SilentInstall +*/ + + Permissions grantedPermissions = new Permissions(); + + for (int i=0; i < nsPrivilegeList.length; i++) { + String privilege = nsPrivilegeList[i]; + + if (privilege.equals("UniversalBrowserRead")) { + BrowserReadPermission bp = new BrowserReadPermission(); + grantedPermissions.add(bp); + } else if (privilege.equals("UniversalJavaPermission")) { + AllPermission ap = new AllPermission(); + grantedPermissions.add(ap); + } + } + + CodeSource cs = new CodeSource((URL) null, (java.security.cert.Certificate [])null); + + if (src != null) { + try { + cs = new CodeSource(new URL(src + "/"), (java.security.cert.Certificate[]) null); + } catch (MalformedURLException mfue) { + // do nothing + } + } + + ProtectionDomain pd = new ProtectionDomain(cs, grantedPermissions, null, null); + + // Add to hashmap + return new AccessControlContext(new ProtectionDomain[] {pd}); + } + + class BrowserReadPermission extends BasicPermission { + public BrowserReadPermission() { + super("browserRead"); + } + } +} diff -r 8dc304404e01 -r 933d372e9ded plugin/icedteanp/sun/applet/PluginAppletViewer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/icedteanp/sun/applet/PluginAppletViewer.java Wed May 20 21:30:24 2009 +0100 @@ -0,0 +1,1872 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 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. */ + +/* + * Copyright 1995-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.applet; + +import java.applet.Applet; +import java.applet.AppletContext; +import java.applet.AudioClip; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Toolkit; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.SocketPermission; +import java.net.URI; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Vector; + +import javax.swing.SwingUtilities; + +import net.sourceforge.jnlp.NetxPanel; +import net.sourceforge.jnlp.runtime.JNLPClassLoader; +import sun.awt.AppContext; +import sun.awt.SunToolkit; +import sun.awt.X11.XEmbeddedFrame; +import sun.misc.Ref; + +import com.sun.jndi.toolkit.url.UrlUtil; + + /** + * Lets us construct one using unix-style one shot behaviors + */ + + class PluginAppletViewerFactory + { + public PluginAppletViewer createAppletViewer(int identifier, + long handle, int x, int y, + URL doc, Hashtable atts) { + PluginAppletViewer pluginappletviewer = new PluginAppletViewer(identifier, handle, x, y, doc, atts, System.out, this); + return pluginappletviewer; + } + + public boolean isStandalone() + { + return false; + } + } + + class PluginParseRequest + { + long handle; + String tag; + String documentbase; + } + + /* + */ + // FIXME: declare JSProxy implementation + public class PluginAppletViewer extends XEmbeddedFrame + implements AppletContext, Printable { + /** + * Some constants... + */ + private static String defaultSaveFile = "Applet.ser"; + + /** + * The panel in which the applet is being displayed. + */ + AppletViewerPanel panel; + + /** + * The status line. + */ + Label label; + + /** + * output status messages to this stream + */ + + PrintStream statusMsgStream; + + /** + * For cloning + */ + PluginAppletViewerFactory factory; + + int identifier; + + private static HashMap requests = new HashMap(); + + // Instance identifier -> PluginAppletViewer object. + private static HashMap applets = new HashMap(); + + private static PluginStreamHandler streamhandler; + + private static PluginCallRequestFactory requestFactory; + + private static HashMap siteCookies = new HashMap(); + + private double proposedHeightFactor; + private double proposedWidthFactor; + + private enum AppletStatus { INITIALIZING, INITIALIZED, FAILED }; + private AppletStatus status = null; + + /** + * Null constructor to allow instantiation via newInstance() + */ + public PluginAppletViewer() { + } + + /** + * Create the applet viewer + */ + public PluginAppletViewer(final int identifier, long handle, int x, int y, final URL doc, + final Hashtable atts, PrintStream statusMsgStream, + PluginAppletViewerFactory factory) { + super(handle, true); + 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); + + + // we intercept height and width specifications here because + proposedHeightFactor = 1.0; + proposedWidthFactor = 1.0; + + if (atts.get("heightPercentage") != null) { + proposedHeightFactor = (Integer) atts.get("heightPercentage")/100.0; + } + + if (atts.get("widthPercentage") != null) { + proposedWidthFactor = (Integer) atts.get("widthPercentage")/100.0; + } + + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + panel = new NetxPanel(doc, siteCookies.get(identifier), atts, false); + AppletViewerPanel.debug("Using NetX panel"); + PluginDebug.debug(atts.toString()); + } catch (Exception ex) { + AppletViewerPanel.debug("Unable to start NetX applet - defaulting to Sun applet", ex); + panel = new AppletViewerPanel(doc, atts); + } + return null; + } + }); + + add("Center", panel); + panel.init(); + appletPanels.addElement(panel); + + pack(); + setVisible(true); + + WindowListener windowEventListener = new WindowAdapter() { + + public void windowClosing(WindowEvent evt) { + appletClose(); + } + + public void windowIconified(WindowEvent evt) { + appletStop(); + } + + public void windowDeiconified(WindowEvent evt) { + appletStart(); + } + }; + + class AppletEventListener implements AppletListener + { + final Frame frame; + + public AppletEventListener(Frame frame) + { + this.frame = frame; + } + + public void appletStateChanged(AppletEvent evt) + { + AppletPanel src = (AppletPanel)evt.getSource(); + + switch (evt.getID()) { + case AppletPanel.APPLET_RESIZE: { + if(src != null) { + resize(preferredSize()); + validate(); + } + break; + } + case AppletPanel.APPLET_LOADING_COMPLETED: { + Applet a = src.getApplet(); // sun.applet.AppletPanel + + // Fixed #4754451: Applet can have methods running on main + // thread event queue. + // + // The cause of this bug is that the frame of the applet + // is created in main thread group. Thus, when certain + // AWT/Swing events are generated, the events will be + // dispatched through the wrong event dispatch thread. + // + // To fix this, we rearrange the AppContext with the frame, + // so the proper event queue will be looked up. + // + // Swing also maintains a Frame list for the AppContext, + // so we will have to rearrange it as well. + // + if (a != null) + AppletPanel.changeFrameAppContext(frame, SunToolkit.targetToAppContext(a)); + else + AppletPanel.changeFrameAppContext(frame, AppContext.getAppContext()); + + break; + } + } + } + }; + + addWindowListener(windowEventListener); + panel.addAppletListener(new AppletEventListener(this)); + + // Start the applet + showStatus(amh.getMessage("status.start")); + initEventQueue(); + + // Wait for the panel to initialize + // (happens in a separate thread) + Applet a; + while ((a = panel.getApplet()) == null && ((NetxPanel) panel).isAlive()) { + try { + Thread.sleep(2000); + PluginDebug.debug("Waiting for applet to initialize... "); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + + // 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(); + + if (atts.get("codebase") != null) { + try { + URL appletSrcURL = new URL(codeBase + (String) atts.get("codebase")); + codeBase = appletSrcURL.getProtocol() + "://" + appletSrcURL.getHost(); + } catch (MalformedURLException mfue) { + // do nothing + } + } + + AppletSecurityContextManager.getSecurityContext(0).associateSrc(a.getClass().getClassLoader(), codeBase); + + try { + write("initialized"); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + + } + + public static void setStreamhandler(PluginStreamHandler sh) { + streamhandler = sh; + } + + public static void setPluginCallRequestFactory(PluginCallRequestFactory rf) { + requestFactory = rf; + } + + /** + * Handle an incoming message from the plugin. + */ + public static void handleMessage(int identifier, int reference, String message) + { + + PluginDebug.debug("PAV handling: " + message); + + try { + if (message.startsWith("tag")) { + + // tag and handle must both be set before parsing, so we need + // synchronization here, as the setting of these variables + // may happen in independent threads + + synchronized(requests) { + PluginParseRequest request = requests.get(identifier); + if (request == null) { + request = new PluginParseRequest(); + requests.put(identifier, request); + } + int index = message.indexOf(' ', "tag".length() + 1); + request.documentbase = + message.substring("tag".length() + 1, index); + request.tag = message.substring(index + 1); + PluginDebug.debug ("REQUEST TAG: " + request.tag + " " + + Thread.currentThread()); + + if (request.handle != 0) { + PluginDebug.debug ("REQUEST TAG, PARSING " + + Thread.currentThread()); + PluginAppletViewer.parse + (identifier, request.handle, + new StringReader(request.tag), + new URL(request.documentbase)); + requests.remove(identifier); + } else { + PluginDebug.debug ("REQUEST HANDLE NOT SET: " + request.handle + ". BYPASSING"); + } + } + + } else if (message.startsWith("handle")) { + synchronized(requests) { + PluginParseRequest request = requests.get(identifier); + if (request == null) { + request = new PluginParseRequest(); + requests.put(identifier, request); + } + request.handle = Long.parseLong + (message.substring("handle".length() + 1)); + PluginDebug.debug ("REQUEST HANDLE: " + request.handle); + if (request.tag != null) { + PluginDebug.debug ("REQUEST HANDLE, PARSING " + + Thread.currentThread()); + PluginAppletViewer.parse + (identifier, request.handle, + new StringReader(request.tag), + new URL(request.documentbase)); + requests.remove(identifier); + PluginDebug.debug ("REQUEST HANDLE, DONE PARSING " + + Thread.currentThread()); + } else { + PluginDebug.debug ("REQUEST TAG NOT SET: " + request.tag + ". BYPASSING"); + } + } + } else if (message.startsWith("cookie")) { + + int cookieStrIndex = message.indexOf(" "); + String cookieStr = null; + + if (cookieStrIndex > 0) + cookieStr = message.substring(cookieStrIndex); + + // Always set the cookie -- even if it is null + siteCookies.put(identifier, cookieStr); + } else { + PluginDebug.debug ("HANDLING MESSAGE " + message + " instance " + identifier + " " + Thread.currentThread()); + + while (!applets.containsKey(identifier) || applets.get(identifier).status == AppletStatus.INITIALIZING); + + applets.get(identifier).handleMessage(reference, message); + } + } catch (Exception e) { + throw new RuntimeException("Failed to handle message: " + message + " " + + Thread.currentThread(), e); + } + } + + public void handleMessage(int reference, String message) + { + if (message.startsWith("width")) { + + // 0 => width, 1=> width_value, 2 => height, 3=> height_value + String[] dimMsg = message.split(" "); + + final int height = (int) (proposedHeightFactor*Integer.parseInt(dimMsg[3])); + final int width = (int) (proposedWidthFactor*Integer.parseInt(dimMsg[1])); + + if (panel instanceof NetxPanel) + ((NetxPanel) panel).updateSizeInAtts(height, width); + + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + + setSize(width, height); + + // There is a rather odd drawing bug whereby resizing + // the panel makes no difference on initial call + // because the panel thinks that it is already the + // right size. Validation has no effect there either. + // So we work around by setting size to 1, validating, + // and then setting to the right size and validating + // again. This is not very efficient, and there is + // probably a better way -- but resizing happens + // quite infrequently, so for now this is how we do it + + panel.setSize(1,1); + panel.validate(); + + panel.setSize(width, height); + panel.validate(); + } + }); + } catch (InterruptedException e) { + // do nothing + e.printStackTrace(); + } catch (InvocationTargetException e) { + // do nothing + e.printStackTrace(); + } + } else if (message.startsWith("destroy")) { + dispose(); + } else if (message.startsWith("GetJavaObject")) { + // FIXME: how do we determine what security context this + // object should belong to? + Object o; + + // Wait for the panel to initialize + // (happens in a separate thread) + while ((o = panel.getApplet()) == null && ((NetxPanel) panel).isAlive()) { + try { + Thread.sleep(2000); + PluginDebug.debug("Waiting for applet to initialize..."); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + + // Still null? + if (panel.getApplet() == null) { + this.streamhandler.write("instance " + identifier + " reference " + -1 + " fatalError " + "Initialization failed"); + return; + } + + PluginDebug.debug ("Looking for object " + o + " panel is " + panel); + AppletSecurityContextManager.getSecurityContext(0).store(o); + PluginDebug.debug ("WRITING 1: " + "context 0 reference " + reference + " GetJavaObject " + + AppletSecurityContextManager.getSecurityContext(0).getIdentifier(o)); + streamhandler.write("context 0 reference " + reference + " GetJavaObject " + + AppletSecurityContextManager.getSecurityContext(0).getIdentifier(o)); + PluginDebug.debug ("WRITING 1 DONE"); + } + } + + // FIXME: Kind of hackish way to ensure synchronized re-drawing + private synchronized void forceredraw() { + doLayout(); + } + + /** + * Send the initial set of events to the appletviewer event queue. + * On start-up the current behaviour is to load the applet and call + * Applet.init() and Applet.start(). + */ + private void initEventQueue() { + // appletviewer.send.event is an undocumented and unsupported system + // property which is used exclusively for testing purposes. + PrivilegedAction pa = new PrivilegedAction() { + public Object run() { + return System.getProperty("appletviewer.send.event"); + } + }; + String eventList = (String) AccessController.doPrivileged(pa); + + if (eventList == null) { + // Add the standard events onto the event queue. + panel.sendEvent(AppletPanel.APPLET_LOAD); + panel.sendEvent(AppletPanel.APPLET_INIT); + panel.sendEvent(AppletPanel.APPLET_START); + } else { + // We're testing AppletViewer. Force the specified set of events + // onto the event queue, wait for the events to be processed, and + // exit. + + // The list of events that will be executed is provided as a + // ","-separated list. No error-checking will be done on the list. + String [] events = splitSeparator(",", eventList); + + for (int i = 0; i < events.length; i++) { + PluginDebug.debug("Adding event to queue: " + events[i]); + if (events[i].equals("dispose")) + panel.sendEvent(AppletPanel.APPLET_DISPOSE); + else if (events[i].equals("load")) + panel.sendEvent(AppletPanel.APPLET_LOAD); + else if (events[i].equals("init")) + panel.sendEvent(AppletPanel.APPLET_INIT); + else if (events[i].equals("start")) + panel.sendEvent(AppletPanel.APPLET_START); + else if (events[i].equals("stop")) + panel.sendEvent(AppletPanel.APPLET_STOP); + else if (events[i].equals("destroy")) + panel.sendEvent(AppletPanel.APPLET_DESTROY); + else if (events[i].equals("quit")) + panel.sendEvent(AppletPanel.APPLET_QUIT); + else if (events[i].equals("error")) + panel.sendEvent(AppletPanel.APPLET_ERROR); + else + // non-fatal error if we get an unrecognized event + PluginDebug.debug("Unrecognized event name: " + events[i]); + } + + while (!panel.emptyEventQueue()) ; + appletSystemExit(); + } + } + + /** + * Split a string based on the presence of a specified separator. Returns + * an array of arbitrary length. The end of each element in the array is + * indicated by the separator of the end of the string. If there is a + * separator immediately before the end of the string, the final element + * will be empty. None of the strings will contain the separator. Useful + * when separating strings such as "foo/bar/bas" using separator "/". + * + * @param sep The separator. + * @param s The string to split. + * @return An array of strings. Each string in the array is determined + * by the location of the provided sep in the original string, + * s. Whitespace not stripped. + */ + private String [] splitSeparator(String sep, String s) { + Vector v = new Vector(); + int tokenStart = 0; + int tokenEnd = 0; + + while ((tokenEnd = s.indexOf(sep, tokenStart)) != -1) { + v.addElement(s.substring(tokenStart, tokenEnd)); + tokenStart = tokenEnd+1; + } + // Add the final element. + v.addElement(s.substring(tokenStart)); + + String [] retVal = new String[v.size()]; + v.copyInto(retVal); + return retVal; + } + + /* + * Methods for java.applet.AppletContext + */ + + private static Map audioClips = new HashMap(); + + /** + * Get an audio clip. + */ + public AudioClip getAudioClip(URL url) { + checkConnect(url); + synchronized (audioClips) { + AudioClip clip = (AudioClip)audioClips.get(url); + if (clip == null) { + audioClips.put(url, clip = new AppletAudioClip(url)); + } + return clip; + } + } + + private static Map imageRefs = new HashMap(); + + /** + * Get an image. + */ + public Image getImage(URL url) { + return getCachedImage(url); + } + + private Image getCachedImage(URL url) { + // System.getSecurityManager().checkConnection(url.getHost(), url.getPort()); + return (Image)getCachedImageRef(url).get(); + } + + /** + * Get an image ref. + */ + private synchronized Ref getCachedImageRef(URL url) { + PluginDebug.debug("getCachedImageRef() searching for " + url); + + try { + + String originalURL = url.toString(); + String codeBase = panel.getCodeBase().toString(); + + if (originalURL.startsWith(codeBase)) { + + PluginDebug.debug("getCachedImageRef() got URL = " + url); + PluginDebug.debug("getCachedImageRef() plugin codebase = " + codeBase); + + // try to fetch it locally + if (panel instanceof NetxPanel) { + + URL localURL = null; + + String resourceName = originalURL.substring(codeBase.length()); + JNLPClassLoader loader = (JNLPClassLoader) ((NetxPanel) panel).getAppletClassLoader(); + + if (loader.resourceAvailableLocally(resourceName)) + localURL = loader.getResource(resourceName); + + url = localURL != null ? localURL : url; + } + } + + PluginDebug.debug("getCachedImageRef() getting img from URL = " + url); + + synchronized (imageRefs) { + AppletImageRef ref = (AppletImageRef)imageRefs.get(url); + if (ref == null) { + ref = new AppletImageRef(url); + imageRefs.put(url, ref); + } + return ref; + } + } catch (Exception e) { + System.err.println("Error occurred when trying to fetch image:"); + e.printStackTrace(); + return null; + } + } + + /** + * Flush the image cache. + */ + static void flushImageCache() { + imageRefs.clear(); + } + + static Vector appletPanels = new Vector(); + + /** + * Get an applet by name. + */ + public Applet getApplet(String name) { + name = name.toLowerCase(); + SocketPermission panelSp = + new SocketPermission(panel.getCodeBase().getHost(), "connect"); + for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) { + AppletPanel p = (AppletPanel)e.nextElement(); + String param = p.getParameter("name"); + if (param != null) { + param = param.toLowerCase(); + } + if (name.equals(param) && + p.getDocumentBase().equals(panel.getDocumentBase())) { + + SocketPermission sp = + new SocketPermission(p.getCodeBase().getHost(), "connect"); + + if (panelSp.implies(sp)) { + return p.applet; + } + } + } + return null; + } + + /** + * Return an enumeration of all the accessible + * applets on this page. + */ + public Enumeration getApplets() { + AppletSecurity security = (AppletSecurity)System.getSecurityManager(); + Vector v = new Vector(); + SocketPermission panelSp = + new SocketPermission(panel.getCodeBase().getHost(), "connect"); + + for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) { + AppletPanel p = (AppletPanel)e.nextElement(); + if (p.getDocumentBase().equals(panel.getDocumentBase())) { + + SocketPermission sp = + new SocketPermission(p.getCodeBase().getHost(), "connect"); + if (panelSp.implies(sp)) { + v.addElement(p.applet); + } + } + } + return v.elements(); + } + + /** + * Ignore. + */ + public void showDocument(URL url) { + PluginDebug.debug("Showing document..."); + showDocument(url, "_self"); + } + + /** + * Ignore. + */ + public void showDocument(URL url, String target) { + try { + // FIXME: change to postCallRequest + write("url " + UrlUtil.encode(url.toString(), "UTF-8") + " " + target); + } catch (IOException exception) { + // Deliberately ignore IOException. showDocument may be + // called from threads other than the main thread after + // streamhandler.pluginOutputStream has been closed. + } + } + + /** + * Show status. + */ + public void showStatus(String status) { + try { + // FIXME: change to postCallRequest + write("status " + status); + } catch (IOException exception) { + // Deliberately ignore IOException. showStatus may be + // called from threads other than the main thread after + // streamhandler.pluginOutputStream has been closed. + } + } + + public long getWindow() { + PluginDebug.debug ("STARTING getWindow"); + PluginCallRequest request = requestFactory.getPluginCallRequest("window", + "instance " + identifier + " " + "GetWindow", + "JavaScriptGetWindow"); + PluginDebug.debug ("STARTING postCallRequest"); + streamhandler.postCallRequest(request); + PluginDebug.debug ("STARTING postCallRequest done"); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait request 1"); + synchronized(request) { + PluginDebug.debug ("wait request 2"); + while ((Long) request.getObject() == 0) + request.wait(); + PluginDebug.debug ("wait request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + + PluginDebug.debug ("STARTING getWindow DONE"); + return (Long) request.getObject(); + } + + // FIXME: make private, access via reflection. + public static Object getMember(long internal, String name) + { + AppletSecurityContextManager.getSecurityContext(0).store(name); + int nameID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(name); + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("member", + "instance " + 0 + " GetMember " + internal + " " + nameID, + "JavaScriptGetMember"); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait getMEM request 1"); + synchronized(request) { + PluginDebug.debug ("wait getMEM request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait getMEM request 3 GOT: " + request.getObject().getClass()); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" getMember DONE"); + return request.getObject(); + } + + public static void setMember(long internal, String name, Object value) { + AppletSecurityContextManager.getSecurityContext(0).store(name); + int nameID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(name); + AppletSecurityContextManager.getSecurityContext(0).store(value); + int valueID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(value); + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("void", + "instance " + 0 + " SetMember " + internal + " " + nameID + " " + valueID, + "JavaScriptSetMember"); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait setMem request: " + request.getMessage()); + PluginDebug.debug ("wait setMem request 1"); + synchronized(request) { + PluginDebug.debug ("wait setMem request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait setMem request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" setMember DONE"); + } + + // FIXME: handle long index as well. + public static void setSlot(long internal, int index, Object value) { + AppletSecurityContextManager.getSecurityContext(0).store(value); + int valueID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(value); + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("void", + "instance " + 0 + " SetSlot " + internal + " " + index + " " + valueID, + "JavaScriptSetSlot"); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait setSlot request 1"); + synchronized(request) { + PluginDebug.debug ("wait setSlot request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait setSlot request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" setSlot DONE"); + } + + public static Object getSlot(long internal, int index) + { + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("member", + "instance " + 0 + " GetSlot " + internal + " " + index, + "JavaScriptGetSlot"); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait getSlot request 1"); + synchronized(request) { + PluginDebug.debug ("wait getSlot request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait getSlot request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" getSlot DONE"); + return request.getObject(); + } + + public static Object eval(long internal, String s) + { + AppletSecurityContextManager.getSecurityContext(0).store(s); + int stringID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(s); + // Prefix with dummy instance for convenience. + // FIXME: rename GetMemberPluginCallRequest ObjectPluginCallRequest. + PluginCallRequest request = requestFactory.getPluginCallRequest("member", + "instance " + 0 + " Eval " + internal + " " + stringID, + "JavaScriptEval"); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait eval request 1"); + synchronized(request) { + PluginDebug.debug ("wait eval request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait eval request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" getSlot DONE"); + return request.getObject(); + } + + public static void removeMember (long internal, String name) { + AppletSecurityContextManager.getSecurityContext(0).store(name); + int nameID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(name); + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("void", + "instance " + 0 + " RemoveMember " + internal + " " + nameID, + "JavaScriptRemoveMember"); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait removeMember request 1"); + synchronized(request) { + PluginDebug.debug ("wait removeMember request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait removeMember request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" RemoveMember DONE"); + } + + public static Object call(long internal, String name, Object args[]) + { + // FIXME: when is this removed from the object store? + // FIXME: reference should return the ID. + // FIXME: convenience method for this long line. + AppletSecurityContextManager.getSecurityContext(0).store(name); + int nameID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(name); + AppletSecurityContextManager.getSecurityContext(0).store(args); + int argsID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(args); + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("member", + "instance " + 0 + " Call " + internal + " " + nameID + " " + argsID, + "JavaScriptCall"); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait call request 1"); + synchronized(request) { + PluginDebug.debug ("wait call request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait call request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" Call DONE"); + return request.getObject(); + } + + public static Object requestPluginProxyInfo(URI uri) { + + String requestURI = null; + + try { + + // there is no easy way to get SOCKS proxy info. So, we tell mozilla that we want proxy for + // an HTTP uri in case of non http/ftp protocols. If we get back a SOCKS proxy, we can + // use that, if we get back an http proxy, we fallback to DIRECT connect + + String scheme = uri.getScheme(); + String port = uri.getPort() != -1 ? ":" + uri.getPort() : ""; + if (!uri.getScheme().startsWith("http") && !uri.getScheme().equals("ftp")) + scheme = "http"; + + requestURI = UrlUtil.encode(scheme + "://" + uri.getHost() + port + "/" + uri.getPath(), "UTF-8"); + } catch (Exception e) { + PluginDebug.debug("Cannot construct URL from " + uri.toString() + " ... falling back to DIRECT proxy"); + e.printStackTrace(); + return null; + } + + PluginCallRequest request = requestFactory.getPluginCallRequest("proxyinfo", + "plugin PluginProxyInfo " + requestURI, + "plugin"); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait call request 1"); + synchronized(request) { + PluginDebug.debug ("wait call request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait call request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" Call DONE"); + return request.getObject(); + } + + public static void JavaScriptFinalize(long internal) + { + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("void", + "instance " + 0 + " Finalize " + internal, + "JavaScriptFinalize"); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait finalize request 1"); + synchronized(request) { + PluginDebug.debug ("wait finalize request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait finalize request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" finalize DONE"); + } + + public static String javascriptToString(long internal) + { + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("member", + "instance " + 0 + " ToString " + internal, + "JavaScriptToString"); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait ToString request 1"); + synchronized(request) { + PluginDebug.debug ("wait ToString request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait ToString request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" ToString DONE"); + return (String) request.getObject(); + } + + // FIXME: make this private and access it from JSObject using + // reflection. + private void write(String message) throws IOException { + PluginDebug.debug ("WRITING 2: " + "instance " + identifier + " " + message); + streamhandler.write("instance " + identifier + " " + message); + PluginDebug.debug ("WRITING 2 DONE"); + } + + public void setStream(String key, InputStream stream)throws IOException{ + // We do nothing. + } + + public InputStream getStream(String key){ + // We do nothing. + return null; + } + + public Iterator getStreamKeys(){ + // We do nothing. + return null; + } + + /** + * System parameters. + */ + static Hashtable systemParam = new Hashtable(); + + static { + systemParam.put("codebase", "codebase"); + systemParam.put("code", "code"); + systemParam.put("alt", "alt"); + systemParam.put("width", "width"); + systemParam.put("height", "height"); + systemParam.put("align", "align"); + systemParam.put("vspace", "vspace"); + systemParam.put("hspace", "hspace"); + } + + /** + * Print the HTML tag. + */ + public static void printTag(PrintStream out, Hashtable atts) { + out.print(""); + + // A very slow sorting algorithm + int len = atts.size(); + String params[] = new String[len]; + len = 0; + for (Enumeration e = atts.keys() ; e.hasMoreElements() ;) { + String param = (String)e.nextElement(); + int i = 0; + for (; i < len ; i++) { + if (params[i].compareTo(param) >= 0) { + break; + } + } + System.arraycopy(params, i, params, i + 1, len - i); + params[i] = param; + len++; + } + + for (int i = 0 ; i < len ; i++) { + String param = params[i]; + if (systemParam.get(param) == null) { + out.println(""); + } + } + out.println(""); + } + + /** + * Make sure the atrributes are uptodate. + */ + public void updateAtts() { + Dimension d = panel.size(); + Insets in = panel.insets(); + panel.atts.put("width", + new Integer(d.width - (in.left + in.right)).toString()); + panel.atts.put("height", + new Integer(d.height - (in.top + in.bottom)).toString()); + } + + /** + * Restart the applet. + */ + void appletRestart() { + panel.sendEvent(AppletPanel.APPLET_STOP); + panel.sendEvent(AppletPanel.APPLET_DESTROY); + panel.sendEvent(AppletPanel.APPLET_INIT); + panel.sendEvent(AppletPanel.APPLET_START); + } + + /** + * Reload the applet. + */ + void appletReload() { + panel.sendEvent(AppletPanel.APPLET_STOP); + panel.sendEvent(AppletPanel.APPLET_DESTROY); + panel.sendEvent(AppletPanel.APPLET_DISPOSE); + + /** + * Fixed #4501142: Classlaoder sharing policy doesn't + * take "archive" into account. This will be overridden + * by Java Plug-in. [stanleyh] + */ + AppletPanel.flushClassLoader(panel.getClassLoaderCacheKey()); + + /* + * Make sure we don't have two threads running through the event queue + * at the same time. + */ + try { + panel.joinAppletThread(); + panel.release(); + } catch (InterruptedException e) { + return; // abort the reload + } + + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + panel.createAppletThread(); + return null; + } + }); + + panel.sendEvent(AppletPanel.APPLET_LOAD); + panel.sendEvent(AppletPanel.APPLET_INIT); + panel.sendEvent(AppletPanel.APPLET_START); + } + + public int print(Graphics graphics, PageFormat pf, int pageIndex) { + return Printable.NO_SUCH_PAGE; + } + + /** + * Start the applet. + */ + void appletStart() { + panel.sendEvent(AppletPanel.APPLET_START); + } + + /** + * Stop the applet. + */ + void appletStop() { + panel.sendEvent(AppletPanel.APPLET_STOP); + } + + /** + * Shutdown a viewer. + * Stop, Destroy, Dispose and Quit a viewer + */ + private void appletShutdown(AppletPanel p) { + p.sendEvent(AppletPanel.APPLET_STOP); + p.sendEvent(AppletPanel.APPLET_DESTROY); + p.sendEvent(AppletPanel.APPLET_DISPOSE); + p.sendEvent(AppletPanel.APPLET_QUIT); + } + + /** + * Close this viewer. + * Stop, Destroy, Dispose and Quit an AppletView, then + * reclaim resources and exit the program if this is + * 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(); + } + + /** + * Exit the program. + * Exit from the program (if not stand alone) - do no clean-up + */ + private void appletSystemExit() { + if (factory.isStandalone()) + System.exit(0); + } + + /** + * How many applets are running? + */ + + public static int countApplets() { + return appletPanels.size(); + } + + + /** + * The current character. + */ + static int c; + + /** + * Scan spaces. + */ + public static void skipSpace(Reader in) throws IOException { + while ((c >= 0) && + ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'))) { + c = in.read(); + } + } + + /** + * Scan identifier + */ + public static String scanIdentifier(Reader in) throws IOException { + StringBuffer buf = new StringBuffer(); + + if (c == '!') { + // Technically, we should be scanning for '!--' but we are reading + // from a stream, and there is no way to peek ahead. That said, + // a ! at this point can only mean comment here afaik, so we + // should be okay + skipComment(in); + return ""; + } + + while (true) { + if (((c >= 'a') && (c <= 'z')) || + ((c >= 'A') && (c <= 'Z')) || + ((c >= '0') && (c <= '9')) || (c == '_')) { + buf.append((char)c); + c = in.read(); + } else { + return buf.toString(); + } + } + } + + public static void skipComment(Reader in) throws IOException { + StringBuffer buf = new StringBuffer(); + boolean commentHeaderPassed = false; + c = in.read(); + buf.append((char)c); + + while (true) { + if (c == '-' && (c = in.read()) == '-') { + buf.append((char)c); + if (commentHeaderPassed) { + // -- encountered ... is > next? + if ((c = in.read()) == '>') { + buf.append((char)c); + + PluginDebug.debug("Comment skipped: " + buf.toString()); + + // comment skipped. + return; + } + } else { + // first -- is part of