changeset 1806:933d372e9ded

Merge shiny new plugin. 2009-05-15 Deepak Bhole <dbhole@redhat.com> * 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.
author Andrew John Hughes <ahughes@redhat.com>
date Wed, 20 May 2009 21:30:24 +0100
parents 8dc304404e01
children 97367bcc3bc1
files ChangeLog IcedTeaNPPlugin.cc Makefile.am configure.ac plugin/icedteanp/netscape/javascript/JSException.java plugin/icedteanp/netscape/javascript/JSObject.java plugin/icedteanp/netscape/javascript/JSProxy.java plugin/icedteanp/netscape/javascript/JSRunnable.java plugin/icedteanp/netscape/javascript/JSUtil.java plugin/icedteanp/sun/applet/AppletSecurityContextManager.java plugin/icedteanp/sun/applet/GetMemberPluginCallRequest.java plugin/icedteanp/sun/applet/GetWindowPluginCallRequest.java plugin/icedteanp/sun/applet/JavaConsole.java plugin/icedteanp/sun/applet/PasswordAuthenticationDialog.java plugin/icedteanp/sun/applet/PluginAppletSecurityContext.java plugin/icedteanp/sun/applet/PluginAppletViewer.java plugin/icedteanp/sun/applet/PluginCallRequest.java plugin/icedteanp/sun/applet/PluginCallRequestFactory.java plugin/icedteanp/sun/applet/PluginClassLoader.java plugin/icedteanp/sun/applet/PluginDebug.java plugin/icedteanp/sun/applet/PluginException.java plugin/icedteanp/sun/applet/PluginMain.java plugin/icedteanp/sun/applet/PluginMessageConsumer.java plugin/icedteanp/sun/applet/PluginMessageHandlerWorker.java plugin/icedteanp/sun/applet/PluginObjectStore.java plugin/icedteanp/sun/applet/PluginProxyInfoRequest.java plugin/icedteanp/sun/applet/PluginProxySelector.java plugin/icedteanp/sun/applet/PluginStreamHandler.java plugin/icedteanp/sun/applet/RequestQueue.java plugin/icedteanp/sun/applet/TestEnv.java plugin/icedteanp/sun/applet/VoidPluginCallRequest.java
diffstat 31 files changed, 8789 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- 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  <dbhole@redhat.com>
+
+	* 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  <omajid@redhat.com>
 
 	* overlays/openjdk/jdk/src/share/classes/net/sourceforge/jnlp/JNLPSplashScreen.java: New file. This new class
--- /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 <dlfcn.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// Netscape plugin API includes.
+#include <npapi.h>
+#include <npupp.h>
+
+// GLib includes.
+#include <glib.h>
+#include <glib/gstdio.h>
+
+// GTK includes.
+#include <gtk/gtk.h>
+
+// Documentbase retrieval includes.
+#include <nsIPluginInstance.h>
+#include <nsIPluginInstancePeer.h>
+#include <nsIPluginTagInfo2.h>
+
+// API's into Mozilla
+#include <nsCOMPtr.h>
+#include <nsICookieService.h>
+#include <nsIDNSRecord.h>
+#include <nsIDNSService.h>
+#include <nsINetUtil.h>
+#include <nsIProxyInfo.h>
+#include <nsIProtocolProxyService.h>
+#include <nsIScriptSecurityManager.h>
+#include <nsIIOService.h>
+#include <nsIURI.h>
+#include <nsNetCID.h>
+#include <nsStringAPI.h>
+#include <nsServiceManagerUtils.h>
+
+
+// Debugging macros.
+#define PLUGIN_DEBUG(message)                                           \
+  g_print ("GCJ PLUGIN: thread %p: %s\n", g_thread_self (), message)
+
+#define PLUGIN_DEBUG_TWO(first, second)                                 \
+  g_print ("GCJ PLUGIN: thread %p: %s %s\n", g_thread_self (),          \
+           first, second)
+
+// Error reporting macros.
+#define PLUGIN_ERROR(message)                                       \
+  g_printerr ("%s:%d: thread %p: Error: %s\n", __FILE__, __LINE__,  \
+              g_thread_self (), message)
+
+#define PLUGIN_ERROR_TWO(first, second)                                 \
+  g_printerr ("%s:%d: thread %p: Error: %s: %s\n", __FILE__, __LINE__,  \
+              g_thread_self (), first, second)
+
+#define PLUGIN_ERROR_THREE(first, second, third)                        \
+  g_printerr ("%s:%d: thread %p: Error: %s: %s: %s\n", __FILE__,        \
+              __LINE__, g_thread_self (), first, second, third)
+
+// Plugin information passed to about:plugins.
+#define PLUGIN_NAME "IcedTea NPR Web Browser Plugin (using IcedTea)"
+#define PLUGIN_DESC "The " PLUGIN_NAME PLUGIN_VERSION " executes Java applets."
+#define PLUGIN_MIME_DESC                                               \
+  "application/x-java-vm:class,jar:IcedTea;"                           \
+  "application/x-java-applet:class,jar:IcedTea;"                       \
+  "application/x-java-applet;version=1.1:class,jar:IcedTea;"           \
+  "application/x-java-applet;version=1.1.1:class,jar:IcedTea;"         \
+  "application/x-java-applet;version=1.1.2:class,jar:IcedTea;"         \
+  "application/x-java-applet;version=1.1.3:class,jar:IcedTea;"         \
+  "application/x-java-applet;version=1.2:class,jar:IcedTea;"           \
+  "application/x-java-applet;version=1.2.1:class,jar:IcedTea;"         \
+  "application/x-java-applet;version=1.2.2:class,jar:IcedTea;"         \
+  "application/x-java-applet;version=1.3:class,jar:IcedTea;"           \
+  "application/x-java-applet;version=1.3.1:class,jar:IcedTea;"         \
+  "application/x-java-applet;version=1.4:class,jar:IcedTea;"           \
+  "application/x-java-applet;version=1.4.1:class,jar:IcedTea;"         \
+  "application/x-java-applet;version=1.4.2:class,jar:IcedTea;"         \
+  "application/x-java-applet;version=1.5:class,jar:IcedTea;"           \
+  "application/x-java-applet;version=1.6:class,jar:IcedTea;"           \
+  "application/x-java-applet;jpi-version=1.6.0_00:class,jar:IcedTea;"  \
+  "application/x-java-bean:class,jar:IcedTea;"                         \
+  "application/x-java-bean;version=1.1:class,jar:IcedTea;"             \
+  "application/x-java-bean;version=1.1.1:class,jar:IcedTea;"           \
+  "application/x-java-bean;version=1.1.2:class,jar:IcedTea;"           \
+  "application/x-java-bean;version=1.1.3:class,jar:IcedTea;"           \
+  "application/x-java-bean;version=1.2:class,jar:IcedTea;"             \
+  "application/x-java-bean;version=1.2.1:class,jar:IcedTea;"           \
+  "application/x-java-bean;version=1.2.2:class,jar:IcedTea;"           \
+  "application/x-java-bean;version=1.3:class,jar:IcedTea;"             \
+  "application/x-java-bean;version=1.3.1:class,jar:IcedTea;"           \
+  "application/x-java-bean;version=1.4:class,jar:IcedTea;"             \
+  "application/x-java-bean;version=1.4.1:class,jar:IcedTea;"           \
+  "application/x-java-bean;version=1.4.2:class,jar:IcedTea;"           \
+  "application/x-java-bean;version=1.5:class,jar:IcedTea;"             \
+  "application/x-java-bean;version=1.6:class,jar:IcedTea;"             \
+  "application/x-java-bean;jpi-version=1.6.0_00:class,jar:IcedTea;"
+#define PLUGIN_URL NS_INLINE_PLUGIN_CONTRACTID_PREFIX NS_JVM_MIME_TYPE
+#define PLUGIN_MIME_TYPE "application/x-java-vm"
+#define PLUGIN_FILE_EXTS "class,jar,zip"
+#define PLUGIN_MIME_COUNT 1
+
+#define FAILURE_MESSAGE "gcjwebplugin error: Failed to run %s." \
+  "  For more detail rerun \"firefox -g\" in a terminal window."
+
+static int plugin_debug = 1;
+
+#define PLUGIN_DEBUG_0ARG(str) \
+  do                                        \
+  {                                         \
+    if (plugin_debug)                       \
+    {                                       \
+      fprintf(stderr, "GCJ PLUGIN: thread %p: ", g_thread_self ()); \
+      fprintf(stderr, str);                \
+    }                                       \
+  } while (0)
+
+#define PLUGIN_DEBUG_1ARG(str, arg1) \
+  do                                        \
+  {                                         \
+    if (plugin_debug)                       \
+    {                                       \
+      fprintf(stderr, "GCJ PLUGIN: thread %p: ", g_thread_self ()); \
+      fprintf(stderr, str, arg1);          \
+    }                                       \
+  } while (0)
+
+#define PLUGIN_DEBUG_2ARG(str, arg1, arg2)  \
+  do                                        \
+  {                                         \
+    if (plugin_debug)                       \
+    {                                       \
+      fprintf(stderr, "GCJ PLUGIN: thread %p: ", g_thread_self ()); \
+      fprintf(stderr, str, arg1, arg2);    \
+    }                                       \
+  } while (0)
+
+#define PLUGIN_DEBUG_3ARG(str, arg1, arg2, arg3) \
+  do                                           \
+  {                                            \
+    if (plugin_debug)                          \
+    {                                          \
+      fprintf(stderr, "GCJ PLUGIN: thread %p: ", g_thread_self ()); \
+      fprintf(stderr, str, arg1, arg2, arg3); \
+    }                                          \
+  } while (0)
+
+#define PLUGIN_DEBUG_4ARG(str, arg1, arg2, arg3, arg4) \
+  do                                                 \
+  {                                                  \
+    if (plugin_debug)                                \
+    {                                                \
+      fprintf(stderr, "GCJ PLUGIN: thread %p: ", g_thread_self ()); \
+      fprintf(stderr, str, arg1, arg2, arg3, arg4); \
+    }                                                \
+  } while (0)
+
+// Documentbase retrieval required definition.
+static NS_DEFINE_IID (kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID);
+
+// Browser function table.
+static NPNetscapeFuncs browserFunctions;
+
+// Data directory for plugin.
+static gchar* data_directory = NULL;
+
+// Fully-qualified appletviewer executable.
+static gchar* appletviewer_executable = NULL;
+
+// Applet viewer input channel (needs to be static because it is used in plugin_in_pipe_callback)
+static GIOChannel* in_from_appletviewer = NULL;
+
+// Applet viewer input pipe name.
+gchar* in_pipe_name;
+
+// Applet viewer input watch source.
+gint in_watch_source;
+
+// Applet viewer output pipe name.
+gchar* out_pipe_name;
+
+// Applet viewer output watch source.
+gint out_watch_source;
+
+// Applet viewer output channel.
+GIOChannel* out_to_appletviewer;
+
+// Tracks jvm status
+gboolean jvm_up = FALSE;
+
+// Keeps track of initialization. NP_Initialize should only be
+// called once.
+gboolean initialized = false;
+
+GQuark ITNP_PLUGIN_ERROR = g_quark_from_string("IcedTeaNPPlugin");
+
+// GCJPluginData stores all the data associated with a single plugin
+// instance.  A separate plugin instance is created for each <APPLET>
+// tag.  For now, each plugin instance spawns its own applet viewer
+// process but this may need to change if we find pages containing
+// multiple applets that expect to be running in the same VM.
+struct GCJPluginData
+{
+  // A unique identifier for this plugin window.
+  gchar* instance_string;
+  // Mutex to protect appletviewer_alive.
+  GMutex* appletviewer_mutex;
+  // Back-pointer to the plugin instance to which this data belongs.
+  // This should not be freed but instead simply set to NULL.
+  NPP owner;
+  // The address of the plugin window.  This should not be freed but
+  // instead simply set to NULL.
+  gpointer window_handle;
+  // The last plugin window width sent to us by the browser.
+  guint32 window_width;
+  // The last plugin window height sent to us by the browser.
+  guint32 window_height;
+};
+
+// Documentbase retrieval type-punning union.
+typedef union
+{
+  void** void_field;
+  nsIPluginTagInfo2** info_field;
+} info_union;
+
+// Static instance helper functions.
+// Have the browser allocate a new GCJPluginData structure.
+static void plugin_data_new (GCJPluginData** data);
+// Retrieve the current document's documentbase.
+static gchar* plugin_get_documentbase (NPP instance);
+// Notify the user that the appletviewer is not installed correctly.
+static void plugin_display_failure_dialog ();
+// Callback used to monitor input pipe status.
+static gboolean plugin_in_pipe_callback (GIOChannel* source,
+                                         GIOCondition condition,
+                                         gpointer plugin_data);
+// Callback used to monitor output pipe status.
+static gboolean plugin_out_pipe_callback (GIOChannel* source,
+                                          GIOCondition condition,
+                                          gpointer plugin_data);
+static NPError plugin_start_appletviewer (GCJPluginData* data);
+static gchar* plugin_create_applet_tag (int16 argc, char* argn[],
+                                        char* argv[]);
+static void plugin_send_message_to_appletviewer (gchar const* message);
+static void plugin_stop_appletviewer ();
+// Uninitialize GCJPluginData structure
+static void plugin_data_destroy (NPP instance);
+
+NS_IMETHODIMP get_cookie_info(const char* siteAddr, char** cookieString);
+void get_proxy_info(const char* siteAddr, char** proxy_scheme, char** proxy_host, char** proxy_port, GError *error);
+void decode_url(const gchar* url, gchar** decoded_url);
+void consume_message(gchar* message);
+void start_jvm_if_needed();
+static void appletviewer_monitor(GPid pid, gint status, gpointer data); 
+
+// Global instance counter.
+// Mutex to protect plugin_instance_counter.
+static GMutex* plugin_instance_mutex = NULL;
+// A global variable for reporting GLib errors.  This must be free'd
+// and set to NULL after each use.
+static GError* channel_error = NULL;
+
+static GHashTable* instance_to_id_map = g_hash_table_new(NULL, NULL);
+static GHashTable* id_to_instance_map = g_hash_table_new(NULL, NULL);
+static gint instance_counter = 1;
+static GPid appletviewer_pid = -1;
+static guint appletviewer_watch_id = -1;
+
+// Functions prefixed by GCJ_ are instance functions.  They are called
+// by the browser and operate on instances of GCJPluginData.
+// Functions prefixed by plugin_ are static helper functions.
+// Functions prefixed by NP_ are factory functions.  They are called
+// by the browser and provide functionality needed to create plugin
+// instances.
+
+// INSTANCE FUNCTIONS
+
+// Creates a new gcjwebplugin instance.  This function creates a
+// GCJPluginData* and stores it in instance->pdata.  The following
+// GCJPluginData fiels are initialized: instance_string, in_pipe_name,
+// in_from_appletviewer, in_watch_source, out_pipe_name,
+// out_to_appletviewer, out_watch_source, appletviewer_mutex, owner,
+// appletviewer_alive.  In addition two pipe files are created.  All
+// of those fields must be properly destroyed, and the pipes deleted,
+// by GCJ_Destroy.  If an error occurs during initialization then this
+// function will free anything that's been allocated so far, set
+// instance->pdata to NULL and return an error code.
+NPError
+GCJ_New (NPMIMEType pluginType, NPP instance, uint16 mode,
+         int16 argc, char* argn[], char* argv[],
+         NPSavedData* saved)
+{
+  PLUGIN_DEBUG ("GCJ_New");
+
+  NPError np_error = NPERR_NO_ERROR;
+  GCJPluginData* data = NULL;
+
+  gchar* documentbase = NULL;
+  gchar* read_message = NULL;
+  gchar* applet_tag = NULL;
+  gchar* tag_message = NULL;
+  gchar* cookie_info = NULL;
+
+  if (!instance)
+    {
+      PLUGIN_ERROR ("Browser-provided instance pointer is NULL.");
+      np_error = NPERR_INVALID_INSTANCE_ERROR;
+      goto cleanup_done;
+    }
+
+  // data
+  plugin_data_new (&data);
+  if (data == NULL)
+    {
+      PLUGIN_ERROR ("Failed to allocate plugin data.");
+      np_error = NPERR_OUT_OF_MEMORY_ERROR;
+      goto cleanup_done;
+    }
+
+  // start the jvm if needed
+  start_jvm_if_needed();
+
+  // Initialize data->instance_string.
+  //
+  // instance_string should be unique for this process so we use a
+  // combination of getpid and plugin_instance_counter.
+  //
+  // Critical region.  Reference and increment plugin_instance_counter
+  // global.
+  g_mutex_lock (plugin_instance_mutex);
+
+  // data->instance_string
+  data->instance_string = g_strdup_printf ("%d",
+                                           instance_counter);
+
+  g_mutex_unlock (plugin_instance_mutex);
+
+  // data->appletviewer_mutex
+  data->appletviewer_mutex = g_mutex_new ();
+
+  g_mutex_lock (data->appletviewer_mutex);
+
+  // Documentbase retrieval.
+  documentbase = plugin_get_documentbase (instance);
+  if (!documentbase)
+    {
+      PLUGIN_ERROR ("Documentbase retrieval failed."
+                    " Browser not Mozilla-based?");
+      goto cleanup_appletviewer_mutex;
+    }
+
+  // Send applet tag message to appletviewer.
+  applet_tag = plugin_create_applet_tag (argc, argn, argv);
+
+  tag_message = (gchar*) malloc(strlen(applet_tag)*sizeof(gchar) + 1024);
+  g_sprintf(tag_message, "instance %d tag %s %s", instance_counter, documentbase, applet_tag);
+
+  //plugin_send_message_to_appletviewer (data, data->instance_string);
+  plugin_send_message_to_appletviewer (tag_message);
+
+  //send cookie information
+  char* cookie_string;
+  if (get_cookie_info(documentbase, &cookie_string) == NS_OK)
+    {
+      cookie_info = (gchar*) malloc(sizeof(cookie_string) + 1024);
+      g_sprintf(cookie_info, "instance %d cookie %s", instance_counter, cookie_string);
+    }
+  else
+    {
+      cookie_info = (gchar*) malloc(1024);
+      g_sprintf(cookie_info, "instance %d cookie", instance_counter);
+    }
+
+  plugin_send_message_to_appletviewer (cookie_info); 
+
+  g_mutex_unlock (data->appletviewer_mutex);
+
+  // If initialization succeeded entirely then we store the plugin
+  // data in the instance structure and return.  Otherwise we free the
+  // data we've allocated so far and set instance->pdata to NULL.
+
+  // Set back-pointer to owner instance.
+  data->owner = instance;
+  instance->pdata = data;
+  goto cleanup_done;
+
+ cleanup_appletviewer_mutex:
+  g_free (data->appletviewer_mutex);
+  data->appletviewer_mutex = NULL;
+
+  // cleanup_instance_string:
+  g_free (data->instance_string);
+  data->instance_string = NULL;
+
+  // cleanup_data:
+  // Eliminate back-pointer to plugin instance.
+  data->owner = NULL;
+  (*browserFunctions.memfree) (data);
+  data = NULL;
+
+  // Initialization failed so return a NULL pointer for the browser
+  // data.
+  instance->pdata = NULL;
+
+ cleanup_done:
+  g_free (tag_message);
+  tag_message = NULL;
+  g_free (applet_tag);
+  applet_tag = NULL;
+  g_free (read_message);
+  read_message = NULL;
+  g_free (documentbase);
+  documentbase = NULL;
+
+  // store an identifier for this plugin
+  g_hash_table_insert(instance_to_id_map, instance, GINT_TO_POINTER(instance_counter));
+  g_hash_table_insert(id_to_instance_map, GINT_TO_POINTER(instance_counter), instance);
+  instance_counter++;
+
+  PLUGIN_DEBUG ("GCJ_New return");
+
+  return np_error;
+}
+
+// Starts the JVM if it is not already running
+void start_jvm_if_needed()
+{
+
+  // This is asynchronized function. It must 
+  // have exclusivity when running.
+
+  GMutex *vm_start_mutex = g_mutex_new();
+  g_mutex_lock(vm_start_mutex);
+
+  PLUGIN_DEBUG_0ARG("Checking JVM status...\n");
+
+  // If the jvm is already up, do nothing
+  if (jvm_up)
+  {
+      PLUGIN_DEBUG_0ARG("JVM is up. Returning.\n");
+      return;
+  }
+
+  PLUGIN_DEBUG_0ARG("No JVM is running. Attempting to start one...\n");
+
+  NPError np_error = NPERR_NO_ERROR;
+  GCJPluginData* data = NULL;
+  
+  // Create appletviewer-to-plugin pipe which we refer to as the input
+  // pipe.
+
+  // in_pipe_name
+  in_pipe_name = g_strdup_printf ("%s/icedteanp-appletviewer-to-plugin", 
+                                         data_directory);
+  if (!in_pipe_name)
+    {
+      PLUGIN_ERROR ("Failed to create input pipe name.");
+      np_error = NPERR_OUT_OF_MEMORY_ERROR;
+      // If in_pipe_name is NULL then the g_free at
+      // cleanup_in_pipe_name will simply return.
+      goto cleanup_in_pipe_name;
+    }
+
+  // clean up any older pip
+  unlink (in_pipe_name);
+
+  PLUGIN_DEBUG_TWO ("GCJ_New: creating input fifo:", in_pipe_name);
+  if (mkfifo (in_pipe_name, 0700) == -1 && errno != EEXIST)
+    {
+      PLUGIN_ERROR_TWO ("Failed to create input pipe", strerror (errno));
+      np_error = NPERR_GENERIC_ERROR;
+      goto cleanup_in_pipe_name;
+    }
+  PLUGIN_DEBUG_TWO ("GCJ_New: created input fifo:", in_pipe_name);
+
+  // Create plugin-to-appletviewer pipe which we refer to as the
+  // output pipe.
+
+  // out_pipe_name
+  out_pipe_name = g_strdup_printf ("%s/icedteanp-plugin-to-appletviewer", 
+                                         data_directory);
+
+  if (!out_pipe_name)
+    {
+      PLUGIN_ERROR ("Failed to create output pipe name.");
+      np_error = NPERR_OUT_OF_MEMORY_ERROR;
+      goto cleanup_out_pipe_name;
+    }
+
+  // clean up any older pip
+  unlink (out_pipe_name);
+
+  PLUGIN_DEBUG_TWO ("GCJ_New: creating output fifo:", out_pipe_name);
+  if (mkfifo (out_pipe_name, 0700) == -1 && errno != EEXIST)
+    {
+      PLUGIN_ERROR_TWO ("Failed to create output pipe", strerror (errno));
+      np_error = NPERR_GENERIC_ERROR;
+      goto cleanup_out_pipe_name;
+    }
+  PLUGIN_DEBUG_TWO ("GCJ_New: created output fifo:", out_pipe_name);
+
+  // Start a separate appletviewer process for each applet, even if
+  // there are multiple applets in the same page.  We may need to
+  // change this behaviour if we find pages with multiple applets that
+  // rely on being run in the same VM.
+ 
+  np_error = plugin_start_appletviewer (data);
+
+  // Create plugin-to-appletviewer channel.  The default encoding for
+  // the file is UTF-8.
+  // out_to_appletviewer
+  out_to_appletviewer = g_io_channel_new_file (out_pipe_name,
+                                               "w", &channel_error);
+  if (!out_to_appletviewer)
+    {
+      if (channel_error)
+        {
+          PLUGIN_ERROR_TWO ("Failed to create output channel",
+                            channel_error->message);
+          g_error_free (channel_error);
+          channel_error = NULL;
+        }
+      else
+        PLUGIN_ERROR ("Failed to create output channel");
+        
+      np_error = NPERR_GENERIC_ERROR;
+      goto cleanup_out_to_appletviewer;
+    }
+
+  // Watch for hangup and error signals on the output pipe.
+  out_watch_source =
+    g_io_add_watch (out_to_appletviewer,
+                    (GIOCondition) (G_IO_ERR | G_IO_HUP),
+                    plugin_out_pipe_callback, (gpointer) out_to_appletviewer);
+
+  // Create appletviewer-to-plugin channel.  The default encoding for
+  // the file is UTF-8.
+  // in_from_appletviewer
+  in_from_appletviewer = g_io_channel_new_file (in_pipe_name,
+                                                      "r", &channel_error);
+  if (!in_from_appletviewer)
+    {
+      if (channel_error)
+        {
+          PLUGIN_ERROR_TWO ("Failed to create input channel",
+                            channel_error->message);
+          g_error_free (channel_error);
+          channel_error = NULL;
+        }
+      else
+        PLUGIN_ERROR ("Failed to create input channel");
+        
+      np_error = NPERR_GENERIC_ERROR;
+      goto cleanup_in_from_appletviewer;
+    }
+
+  // Watch for hangup and error signals on the input pipe.
+  in_watch_source =
+    g_io_add_watch (in_from_appletviewer,
+                    (GIOCondition) (G_IO_IN | G_IO_ERR | G_IO_HUP),
+                    plugin_in_pipe_callback, (gpointer) in_from_appletviewer);
+
+  jvm_up = TRUE;
+
+  goto done;
+
+  // Free allocated data
+
+ cleanup_in_watch_source:
+  // Removing a source is harmless if it fails since it just means the
+  // source has already been removed.
+  g_source_remove (in_watch_source);
+  in_watch_source = 0;
+
+ cleanup_in_from_appletviewer:
+  if (in_from_appletviewer)
+    g_io_channel_unref (in_from_appletviewer);
+  in_from_appletviewer = NULL;
+
+  // cleanup_out_watch_source:
+  g_source_remove (out_watch_source);
+  out_watch_source = 0;
+
+ cleanup_out_to_appletviewer:
+  if (out_to_appletviewer)
+    g_io_channel_unref (out_to_appletviewer);
+  out_to_appletviewer = NULL;
+
+  // cleanup_out_pipe:
+  // Delete output pipe.
+  PLUGIN_DEBUG_TWO ("GCJ_New: deleting input fifo:", in_pipe_name);
+  unlink (out_pipe_name);
+  PLUGIN_DEBUG_TWO ("GCJ_New: deleted input fifo:", in_pipe_name);
+
+ cleanup_out_pipe_name:
+  g_free (out_pipe_name);
+  out_pipe_name = NULL;
+
+  // cleanup_in_pipe:
+  // Delete input pipe.
+  PLUGIN_DEBUG_TWO ("GCJ_New: deleting output fifo:", out_pipe_name);
+  unlink (in_pipe_name);
+  PLUGIN_DEBUG_TWO ("GCJ_New: deleted output fifo:", out_pipe_name);
+
+ cleanup_in_pipe_name:
+  g_free (in_pipe_name);
+  in_pipe_name = NULL;
+
+ done:
+
+  // Now other threads may re-enter.. unlock the mutex
+  g_mutex_unlock(vm_start_mutex);
+
+}
+
+NPError
+GCJ_GetValue (NPP instance, NPPVariable variable, void* value)
+{
+  PLUGIN_DEBUG ("GCJ_GetValue");
+
+  NPError np_error = NPERR_NO_ERROR;
+
+  switch (variable)
+    {
+    // This plugin needs XEmbed support.
+    case NPPVpluginNeedsXEmbed:
+      {
+        PLUGIN_DEBUG ("GCJ_GetValue: returning TRUE for NeedsXEmbed.");
+        PRBool* bool_value = (PRBool*) value;
+        *bool_value = PR_TRUE;
+      }
+      break;
+
+    default:
+      PLUGIN_ERROR ("Unknown plugin value requested.");
+      np_error = NPERR_GENERIC_ERROR;
+      break;
+    }
+
+  PLUGIN_DEBUG ("GCJ_GetValue return");
+
+  return np_error;
+}
+
+NPError
+GCJ_Destroy (NPP instance, NPSavedData** save)
+{
+  PLUGIN_DEBUG ("GCJ_Destroy");
+
+  GCJPluginData* data = (GCJPluginData*) instance->pdata;
+
+  if (data)
+    {
+      // Free plugin data.
+      plugin_data_destroy (instance);
+    }
+
+  PLUGIN_DEBUG ("GCJ_Destroy return");
+
+  return NPERR_NO_ERROR;
+}
+
+NPError
+GCJ_SetWindow (NPP instance, NPWindow* window)
+{
+  PLUGIN_DEBUG ("GCJ_SetWindow");
+
+  if (instance == NULL)
+    {
+      PLUGIN_ERROR ("Invalid instance.");
+
+      return NPERR_INVALID_INSTANCE_ERROR;
+    }
+
+  gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance);
+
+  gint id = 0;
+  if (id_ptr)
+    {
+      id = GPOINTER_TO_INT(id_ptr);
+    }
+  
+  GCJPluginData* data = (GCJPluginData*) instance->pdata;
+
+  // Simply return if we receive a NULL window.
+  if ((window == NULL) || (window->window == NULL))
+    {
+      PLUGIN_DEBUG ("GCJ_SetWindow: got NULL window.");
+
+      return NPERR_NO_ERROR;
+    }
+
+  if (data->window_handle)
+    {
+      // The window already exists.
+      if (data->window_handle == window->window)
+    {
+          // The parent window is the same as in previous calls.
+          PLUGIN_DEBUG ("GCJ_SetWindow: window already exists.");
+
+          // Critical region.  Read data->appletviewer_mutex and send
+          // a message to the appletviewer.
+          g_mutex_lock (data->appletviewer_mutex);
+
+      if (jvm_up)
+        {
+
+          gboolean dim_changed = FALSE;
+
+          // The window is the same as it was for the last
+          // SetWindow call.
+          if (window->width != data->window_width)
+        {
+                  PLUGIN_DEBUG ("GCJ_SetWindow: window width changed.");
+          // The width of the plugin window has changed.
+
+                  // Store the new width.
+                  data->window_width = window->width;
+                  dim_changed = TRUE;
+        }
+
+          if (window->height != data->window_height)
+        {
+                  PLUGIN_DEBUG ("GCJ_SetWindow: window height changed.");
+          // The height of the plugin window has changed.
+
+                  // Store the new height.
+                  data->window_height = window->height;
+
+                  dim_changed = TRUE;
+        }
+
+        if (dim_changed) {
+            gchar* message = g_strdup_printf ("instance 1 width %d height %d",
+                                                          window->width, window->height);
+            plugin_send_message_to_appletviewer (message);
+            g_free (message);
+            message = NULL;
+        }
+
+
+        }
+      else
+        {
+              // The appletviewer is not running.
+          PLUGIN_DEBUG ("GCJ_SetWindow: appletviewer is not running.");
+        }
+
+          g_mutex_unlock (data->appletviewer_mutex);
+    }
+      else
+    {
+      // The parent window has changed.  This branch does run but
+      // doing nothing in response seems to be sufficient.
+      PLUGIN_DEBUG ("GCJ_SetWindow: parent window changed.");
+    }
+    }
+  else
+    {
+      PLUGIN_DEBUG ("GCJ_SetWindow: setting window.");
+
+      // Critical region.  Send messages to appletviewer.
+      g_mutex_lock (data->appletviewer_mutex);
+
+      gchar *window_message = g_strdup_printf ("instance %d handle %ld",
+                                               id, (gulong) window->window);
+      plugin_send_message_to_appletviewer (window_message);
+      g_free (window_message);
+
+      window_message = g_strdup_printf ("instance %d width %d height %d",
+                        id,
+                        window->width,
+                        window->height);
+      plugin_send_message_to_appletviewer (window_message);
+      g_free (window_message);
+      window_message = NULL;
+
+      g_mutex_unlock (data->appletviewer_mutex);
+
+      // Store the window handle.
+      data->window_handle = window->window;
+    }
+
+  PLUGIN_DEBUG ("GCJ_SetWindow return");
+
+  return NPERR_NO_ERROR;
+}
+
+NPError
+GCJ_NewStream (NPP instance, NPMIMEType type, NPStream* stream,
+               NPBool seekable, uint16* stype)
+{
+  PLUGIN_DEBUG ("GCJ_NewStream");
+
+  PLUGIN_DEBUG ("GCJ_NewStream return");
+
+  return NPERR_NO_ERROR;
+}
+
+void
+GCJ_StreamAsFile (NPP instance, NPStream* stream, const char* filename)
+{
+  PLUGIN_DEBUG ("GCJ_StreamAsFile");
+
+  PLUGIN_DEBUG ("GCJ_StreamAsFile return");
+}
+
+NPError
+GCJ_DestroyStream (NPP instance, NPStream* stream, NPReason reason)
+{
+  PLUGIN_DEBUG ("GCJ_DestroyStream");
+
+  PLUGIN_DEBUG ("GCJ_DestroyStream return");
+
+  return NPERR_NO_ERROR;
+}
+
+int32
+GCJ_WriteReady (NPP instance, NPStream* stream)
+{
+  PLUGIN_DEBUG ("GCJ_WriteReady");
+
+  PLUGIN_DEBUG ("GCJ_WriteReady return");
+
+  return 0;
+}
+
+int32
+GCJ_Write (NPP instance, NPStream* stream, int32 offset, int32 len,
+           void* buffer)
+{
+  PLUGIN_DEBUG ("GCJ_Write");
+
+  PLUGIN_DEBUG ("GCJ_Write return");
+
+  return 0;
+}
+
+void
+GCJ_Print (NPP instance, NPPrint* platformPrint)
+{
+  PLUGIN_DEBUG ("GCJ_Print");
+
+  PLUGIN_DEBUG ("GCJ_Print return");
+}
+
+int16
+GCJ_HandleEvent (NPP instance, void* event)
+{
+  PLUGIN_DEBUG ("GCJ_HandleEvent");
+
+  PLUGIN_DEBUG ("GCJ_HandleEvent return");
+
+  return 0;
+}
+
+void
+GCJ_URLNotify (NPP instance, const char* url, NPReason reason,
+               void* notifyData)
+{
+  PLUGIN_DEBUG ("GCJ_URLNotify");
+
+  PLUGIN_DEBUG ("GCJ_URLNotify return");
+}
+
+jref
+GCJ_GetJavaClass (void)
+{
+  PLUGIN_DEBUG ("GCJ_GetJavaClass");
+
+  PLUGIN_DEBUG ("GCJ_GetJavaClass return");
+
+  return 0;
+}
+
+NS_IMETHODIMP
+get_cookie_info(const char* siteAddr, char** cookieString)
+{
+
+  nsresult rv;
+  nsCOMPtr<nsIScriptSecurityManager> sec_man =
+    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+
+  if (!sec_man) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv);
+
+  if (NS_FAILED(rv) || !io_svc) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri));
+
+  nsCOMPtr<nsICookieService> cookie_svc = do_GetService("@mozilla.org/cookieService;1", &rv);
+
+  if (NS_FAILED(rv) || !cookie_svc) {
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = cookie_svc->GetCookieString(uri, NULL, cookieString);
+
+  if (NS_FAILED(rv) || !*cookieString) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+
+// HELPER FUNCTIONS
+
+static void
+plugin_data_new (GCJPluginData** data)
+{
+  PLUGIN_DEBUG ("plugin_data_new");
+
+  *data = (GCJPluginData*)
+    (*browserFunctions.memalloc) (sizeof (struct GCJPluginData));
+
+  // appletviewer_alive is false until the applet viewer is spawned.
+  if (*data)
+    memset (*data, 0, sizeof (struct GCJPluginData));
+
+  PLUGIN_DEBUG ("plugin_data_new return");
+}
+
+// Documentbase retrieval.  This function gets the current document's
+// documentbase.  This function relies on browser-private data so it
+// will only work when the plugin is loaded in a Mozilla-based
+// browser.  We could not find a way to retrieve the documentbase
+// using the original Netscape plugin API so we use the XPCOM API
+// instead.
+static gchar*
+plugin_get_documentbase (NPP instance)
+{
+  PLUGIN_DEBUG ("plugin_get_documentbase");
+
+  nsIPluginInstance* xpcom_instance = NULL;
+  nsIPluginInstancePeer* peer = NULL;
+  nsresult result = 0;
+  nsIPluginTagInfo2* pluginTagInfo2 = NULL;
+  info_union u = { NULL };
+  char const* documentbase = NULL;
+  gchar* documentbase_copy = NULL;
+
+  xpcom_instance = (nsIPluginInstance*) (instance->ndata);
+  if (!xpcom_instance)
+    {
+      PLUGIN_ERROR ("xpcom_instance is NULL.");
+      goto cleanup_done;
+    }
+
+  xpcom_instance->GetPeer (&peer);
+  if (!peer)
+    {
+      PLUGIN_ERROR ("peer is NULL.");
+      goto cleanup_done;
+    }
+
+  u.info_field = &pluginTagInfo2;
+
+  result = peer->QueryInterface (kIPluginTagInfo2IID,
+                                 u.void_field);
+  if (result || !pluginTagInfo2)
+    {
+      PLUGIN_ERROR ("pluginTagInfo2 retrieval failed.");
+      goto cleanup_peer;
+    }
+
+  pluginTagInfo2->GetDocumentBase (&documentbase);
+
+  if (!documentbase)
+    {
+      PLUGIN_ERROR ("documentbase is NULL.");
+      goto cleanup_plugintaginfo2;
+    }
+
+  documentbase_copy = g_strdup (documentbase);
+
+  // Release references.
+ cleanup_plugintaginfo2:
+  NS_RELEASE (pluginTagInfo2);
+
+ cleanup_peer:
+  NS_RELEASE (peer);
+
+ cleanup_done:
+  PLUGIN_DEBUG ("plugin_get_documentbase return");
+
+  return documentbase_copy;
+}
+
+// This function displays an error message if the appletviewer has not
+// been installed correctly.
+static void
+plugin_display_failure_dialog ()
+{
+  GtkWidget* dialog = NULL;
+
+  PLUGIN_DEBUG ("plugin_display_failure_dialog");
+
+  dialog = gtk_message_dialog_new (NULL,
+                                   GTK_DIALOG_DESTROY_WITH_PARENT,
+                                   GTK_MESSAGE_ERROR,
+                                   GTK_BUTTONS_CLOSE,
+                                   FAILURE_MESSAGE,
+                                   appletviewer_executable);
+  gtk_widget_show_all (dialog);
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+
+  PLUGIN_DEBUG ("plugin_display_failure_dialog return");
+}
+
+
+
+// plugin_in_pipe_callback is called when data is available on the
+// input pipe, or when the appletviewer crashes or is killed.  It may
+// be called after data has been destroyed in which case it simply
+// returns FALSE to remove itself from the glib main loop.
+static gboolean
+plugin_in_pipe_callback (GIOChannel* source,
+                         GIOCondition condition,
+                         gpointer plugin_data)
+{
+  PLUGIN_DEBUG ("plugin_in_pipe_callback");
+
+  gboolean keep_installed = TRUE;
+
+  if (condition & G_IO_IN)
+    {
+      gchar* message = NULL;
+
+      if (g_io_channel_read_line (in_from_appletviewer,
+                                  &message, NULL, NULL,
+                                  &channel_error)
+          != G_IO_STATUS_NORMAL)
+        {
+          if (channel_error)
+            {
+              PLUGIN_ERROR_TWO ("Failed to read line from input channel",
+                                channel_error->message);
+              g_error_free (channel_error);
+              channel_error = NULL;
+            }
+          else
+            PLUGIN_ERROR ("Failed to read line from input channel");
+        } else 
+            {
+                consume_message(message);
+            }
+
+      g_free (message);
+      message = NULL;
+
+      keep_installed = TRUE;
+    }
+
+  if (condition & (G_IO_ERR | G_IO_HUP))
+    {
+      PLUGIN_DEBUG ("appletviewer has stopped.");
+      keep_installed = FALSE;
+    }
+
+  PLUGIN_DEBUG ("plugin_in_pipe_callback return");
+
+  return keep_installed;
+}
+
+void consume_message(gchar* message) {
+
+  g_print ("  PIPE: plugin read: %s\n", message);
+
+  if (g_str_has_prefix (message, "instance"))
+    {
+
+      gchar** parts = g_strsplit (message, " ", -1);
+      guint parts_sz = g_strv_length (parts);
+
+      int instance_id = atoi(parts[1]);
+      NPP instance = (NPP) g_hash_table_lookup(id_to_instance_map,
+                                         GINT_TO_POINTER(instance_id));
+
+      if (!instance)
+        {
+          PLUGIN_DEBUG_2ARG("Instance %d is not active. Refusing to consume message \"%s\"\n", instance_id, message);
+          return;
+        }
+
+      GCJPluginData* data = (GCJPluginData*) instance->pdata;
+
+      if (g_str_has_prefix (parts[2], "url"))
+        {
+          // Open the URL in a new browser window.
+          gchar* decoded_url = (gchar*) malloc(strlen(parts[3])*sizeof(gchar) + sizeof(gchar));
+          decode_url(parts[3], &decoded_url);
+
+          PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback: opening URL", decoded_url);
+          PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback: URL target", parts[4]);
+
+          NPError np_error =
+            (*browserFunctions.geturl) (data->owner, decoded_url, parts[4]);
+
+
+          if (np_error != NPERR_NO_ERROR)
+            PLUGIN_ERROR ("Failed to load URL.");
+
+          g_free(decoded_url);
+          decoded_url = NULL;
+        }
+      else if (g_str_has_prefix (parts[2], "status"))
+        {
+
+          // clear the "instance X status" parts
+          sprintf(parts[0], "");
+          sprintf(parts[1], "");
+          sprintf(parts[2], "");
+
+          // join the rest
+          gchar* status_message = g_strjoinv(" ", parts);
+          PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback: setting status", status_message);
+          (*browserFunctions.status) (data->owner, status_message);
+
+          g_free(status_message);
+          status_message = NULL;
+        } 
+
+        g_strfreev (parts);
+        parts = NULL;
+    }
+  else if (g_str_has_prefix (message, "plugin "))
+    {
+      // internal plugin related message
+      gchar** parts = g_strsplit (message, " ", 3);
+      if (g_str_has_prefix(parts[1], "PluginProxyInfo")) 
+      {
+        gchar* proxy_scheme = (gchar*) malloc(sizeof(gchar)*32);
+        gchar* proxy_host = (gchar*) malloc(sizeof(gchar)*64);
+        gchar* proxy_port = (gchar*) malloc(sizeof(gchar)*8);
+
+        GError *error = g_error_new(ITNP_PLUGIN_ERROR, 0, "");
+
+        gchar* decoded_url = (gchar*) malloc(strlen(parts[2])*sizeof(gchar) + sizeof(gchar));
+        decode_url(parts[2], &decoded_url);
+        get_proxy_info(decoded_url, &proxy_scheme, &proxy_host, &proxy_port, error);
+
+        gchar* proxy_info;
+        proxy_info = g_strconcat ("plugin PluginProxyInfo ", NULL);
+        if (error->code == 0)
+          {
+            proxy_info = g_strconcat (proxy_info, proxy_scheme, " ", proxy_host, " ", proxy_port, NULL);
+          } 
+
+        PLUGIN_DEBUG_1ARG("Proxy info: %s\n", proxy_info);
+        plugin_send_message_to_appletviewer(proxy_info);
+
+        g_free(decoded_url);
+        decoded_url = NULL;
+        g_free(proxy_info);
+        proxy_info = NULL;
+        g_free(proxy_scheme);
+        proxy_scheme = NULL;
+        g_free(proxy_host);
+        proxy_host = NULL;
+        g_free(proxy_port);
+        proxy_port = NULL;
+      }
+    }
+  else
+    {
+        g_print ("  Unable to handle message: %s\n", message);
+    }
+}
+
+void decode_url(const gchar* url, gchar** decoded_url)
+{
+    // There is no GLib function to decode urls, so we fallback to Mozilla's
+    // methods
+
+    nsresult rv;
+    nsCOMPtr<nsINetUtil> net_util = do_GetService(NS_NETUTIL_CONTRACTID, &rv);
+
+    if (!net_util)
+        printf("Error instantiating NetUtil service.\n");
+    
+    nsDependentCSubstring unescaped_url;
+    net_util->UnescapeString(nsCString(url), 0, unescaped_url);
+
+    // no need for strn. decoded_url is malloced to sizeof unescaped_url, which
+    // will always be <= decoded size
+    strcpy(*decoded_url,  nsCString(unescaped_url).get());
+}
+
+void get_proxy_info(const char* siteAddr, char** proxy_scheme, char** proxy_host, char** proxy_port, GError *error)
+{
+  nsresult rv;
+
+  // Initialize service variables
+  nsCOMPtr<nsIProtocolProxyService> proxy_svc = do_GetService("@mozilla.org/network/protocol-proxy-service;1", &rv);
+
+  if (!proxy_svc) {
+      printf("Cannot initialize proxy service\n");
+      error->code = 1;
+      return;
+  }
+
+  nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv);
+
+  if (NS_FAILED(rv) || !io_svc) {
+    printf("Cannot initialize io service\n");
+    error->code = 1;
+    return;
+  }
+
+  // uri which needs to be accessed
+  nsCOMPtr<nsIURI> uri;
+  io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri));
+
+  // find the proxy address if any
+  nsCOMPtr<nsIProxyInfo> info;
+  proxy_svc->Resolve(uri, 0, getter_AddRefs(info));
+
+  // if there is no proxy found, return immediately
+  if (!info) {
+     PLUGIN_DEBUG_1ARG("%s does not need a proxy\n", siteAddr);
+     error->code = 1;
+     return;
+  }
+
+  // if proxy info is available, extract it
+  nsCString phost;
+  PRInt32 pport;
+  nsCString ptype;
+
+  info->GetHost(phost);
+  info->GetPort(&pport);
+  info->GetType(ptype);
+
+  // resolve the proxy address to an IP
+  nsCOMPtr<nsIDNSService> dns_svc = do_GetService("@mozilla.org/network/dns-service;1", &rv);
+
+  if (!dns_svc) {
+      printf("Cannot initialize DNS service\n");
+      error->code = 1;
+      return;
+  }
+
+  nsCOMPtr<nsIDNSRecord> record;
+  dns_svc->Resolve(phost, 0U, getter_AddRefs(record));
+
+  // TODO: Add support for multiple ips
+  nsDependentCString ipAddr;
+  record->GetNextAddrAsString(ipAddr);
+
+  // pack information in return variables
+  snprintf(*proxy_scheme, sizeof(char)*32, "%s", ptype.get());
+  snprintf(*proxy_host, sizeof(char)*64, "%s", ipAddr.get());
+  snprintf(*proxy_port, sizeof(char)*8, "%d", pport);
+
+  PLUGIN_DEBUG_4ARG("Proxy info for %s: %s %s %s\n", siteAddr, *proxy_scheme, *proxy_host, *proxy_port);
+}
+
+// plugin_out_pipe_callback is called when the appletviewer crashes or
+// is killed.  It may be called after data has been destroyed in which
+// case it simply returns FALSE to remove itself from the glib main
+// loop.
+static gboolean
+plugin_out_pipe_callback (GIOChannel* source,
+                          GIOCondition condition,
+                          gpointer plugin_data)
+{
+  PLUGIN_DEBUG ("plugin_out_pipe_callback");
+
+  GCJPluginData* data = (GCJPluginData*) plugin_data;
+
+  PLUGIN_DEBUG ("plugin_out_pipe_callback: appletviewer has stopped.");
+
+  PLUGIN_DEBUG ("plugin_out_pipe_callback return");
+
+  return FALSE;
+}
+
+static NPError
+plugin_test_appletviewer ()
+{
+  PLUGIN_DEBUG ("plugin_test_appletviewer");
+  NPError error = NPERR_NO_ERROR;
+
+  gchar* command_line[3] = { NULL, NULL, NULL };
+
+  command_line[0] = g_strdup (appletviewer_executable);
+  command_line[1] = g_strdup("-version");
+  command_line[2] = NULL;
+
+
+  if (!g_spawn_async (NULL, command_line, NULL, (GSpawnFlags) 0,
+                      NULL, NULL, NULL, &channel_error))
+    {
+      if (channel_error)
+        {
+          PLUGIN_ERROR_TWO ("Failed to spawn applet viewer",
+                            channel_error->message);
+          g_error_free (channel_error);
+          channel_error = NULL;
+        }
+      else
+        PLUGIN_ERROR ("Failed to spawn applet viewer");
+      error = NPERR_GENERIC_ERROR;
+    }
+
+  g_free (command_line[0]);
+  command_line[0] = NULL;
+  g_free (command_line[1]);
+  command_line[1] = NULL;
+  g_free (command_line[2]);
+  command_line[2] = NULL;
+
+  PLUGIN_DEBUG ("plugin_test_appletviewer return");
+  return error;
+}
+
+static NPError
+plugin_start_appletviewer (GCJPluginData* data)
+{
+  PLUGIN_DEBUG ("plugin_start_appletviewer");
+  NPError error = NPERR_NO_ERROR;
+
+  gchar* command_line[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
+
+  command_line[0] = g_strdup (appletviewer_executable);
+  // Output from plugin's perspective is appletviewer's input.
+  // Input from plugin's perspective is appletviewer's output.
+  command_line[1] = g_strdup("-Xdebug");
+  command_line[2] = g_strdup("-Xnoagent");
+  command_line[3] = g_strdup("-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n");
+  command_line[4] = g_strdup("sun.applet.PluginMain");
+  command_line[5] = NULL;
+
+  if (!g_spawn_async (NULL, command_line, NULL, (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD,
+                      NULL, NULL, &appletviewer_pid, &channel_error))
+    {
+      if (channel_error)
+        {
+          PLUGIN_ERROR_TWO ("Failed to spawn applet viewer",
+                            channel_error->message);
+          g_error_free (channel_error);
+          channel_error = NULL;
+        }
+      else
+        PLUGIN_ERROR ("Failed to spawn applet viewer");
+      error = NPERR_GENERIC_ERROR;
+    }
+
+  g_free (command_line[0]);
+  command_line[0] = NULL;
+  g_free (command_line[1]);
+  command_line[1] = NULL;
+  g_free (command_line[2]);
+  command_line[2] = NULL;
+  g_free (command_line[3]);
+  command_line[3] = NULL;
+  g_free (command_line[4]);
+  command_line[4] = NULL;
+  g_free (command_line[5]);
+  command_line[5] = NULL;
+
+  if (appletviewer_pid)
+    {
+      PLUGIN_DEBUG_1ARG("Initialized VM with pid=%d\n", appletviewer_pid);
+      appletviewer_watch_id = g_child_watch_add(appletviewer_pid, (GChildWatchFunc) appletviewer_monitor, (gpointer) appletviewer_pid);
+    }
+
+
+  PLUGIN_DEBUG ("plugin_start_appletviewer return");
+  return error;
+}
+
+// Build up the applet tag string that we'll send to the applet
+// viewer.
+static gchar*
+plugin_create_applet_tag (int16 argc, char* argn[], char* argv[])
+{
+  PLUGIN_DEBUG ("plugin_create_applet_tag");
+
+  gchar* applet_tag = g_strdup ("<EMBED ");
+  gchar* parameters = g_strdup ("");
+
+  for (int16 i = 0; i < argc; i++)
+    {
+      if (!g_ascii_strcasecmp (argn[i], "code"))
+        {
+          gchar* code = g_strdup_printf ("CODE=\"%s\" ", argv[i]);
+          applet_tag = g_strconcat (applet_tag, code, NULL);
+          g_free (code);
+          code = NULL;
+    }
+      else if (!g_ascii_strcasecmp (argn[i], "codebase"))
+    {
+          gchar* codebase = g_strdup_printf ("CODEBASE=\"%s\" ", argv[i]);
+          applet_tag = g_strconcat (applet_tag, codebase, NULL);
+          g_free (codebase);
+          codebase = NULL;
+    }
+      else if (!g_ascii_strcasecmp (argn[i], "classid"))
+    {
+          gchar* classid = g_strdup_printf ("CLASSID=\"%s\" ", argv[i]);
+          applet_tag = g_strconcat (applet_tag, classid, NULL);
+          g_free (classid);
+          classid = NULL;
+    }
+      else if (!g_ascii_strcasecmp (argn[i], "archive"))
+    {
+          gchar* archive = g_strdup_printf ("ARCHIVE=\"%s\" ", argv[i]);
+          applet_tag = g_strconcat (applet_tag, archive, NULL);
+          g_free (archive);
+          archive = NULL;
+    }
+      else if (!g_ascii_strcasecmp (argn[i], "width"))
+    {
+          gchar* width = g_strdup_printf ("width=\"%s\" ", argv[i]);
+          applet_tag = g_strconcat (applet_tag, width, NULL);
+          g_free (width);
+          width = NULL;
+    }
+      else if (!g_ascii_strcasecmp (argn[i], "height"))
+    {
+          gchar* height = g_strdup_printf ("height=\"%s\" ", argv[i]);
+          applet_tag = g_strconcat (applet_tag, height, NULL);
+          g_free (height);
+          height = NULL;
+    }
+      else
+        {
+          // Escape the parameter value so that line termination
+          // characters will pass through the pipe.
+          if (argv[i] != '\0')
+            {
+              gchar* escaped = NULL;
+
+              escaped = g_strescape (argv[i], NULL);
+              parameters = g_strconcat (parameters, "<PARAM NAME=\"", argn[i],
+                                        "\" VALUE=\"", escaped, "\">", NULL);
+
+              g_free (escaped);
+              escaped = NULL;
+            }
+        }
+    }
+
+  applet_tag = g_strconcat (applet_tag, ">", parameters, "</EMBED>", NULL);
+
+  g_free (parameters);
+  parameters = NULL;
+
+  PLUGIN_DEBUG ("plugin_create_applet_tag return");
+
+  return applet_tag;
+}
+
+// plugin_send_message_to_appletviewer must be called while holding
+// data->appletviewer_mutex.
+static void
+plugin_send_message_to_appletviewer (gchar const* message)
+{
+  PLUGIN_DEBUG ("plugin_send_message_to_appletviewer");
+
+  if (jvm_up)
+    {
+      gchar* newline_message = NULL;
+      gsize bytes_written = 0;
+
+      // Send message to appletviewer.
+      newline_message = g_strdup_printf ("%s\n", message);
+
+      // g_io_channel_write_chars will return something other than
+      // G_IO_STATUS_NORMAL if not all the data is written.  In that
+      // case we fail rather than retrying.
+      if (g_io_channel_write_chars (out_to_appletviewer,
+                                    newline_message, -1, &bytes_written,
+                                    &channel_error)
+            != G_IO_STATUS_NORMAL)
+        {
+          if (channel_error)
+            {
+              PLUGIN_ERROR_TWO ("Failed to write bytes to output channel",
+                                channel_error->message);
+              g_error_free (channel_error);
+              channel_error = NULL;
+            }
+          else
+            PLUGIN_ERROR ("Failed to write bytes to output channel");
+        }            
+
+      if (g_io_channel_flush (out_to_appletviewer, &channel_error)
+          != G_IO_STATUS_NORMAL)
+        {
+          if (channel_error)
+            {
+              PLUGIN_ERROR_TWO ("Failed to flush bytes to output channel",
+                                channel_error->message);
+              g_error_free (channel_error);
+              channel_error = NULL;
+            }
+          else
+            PLUGIN_ERROR ("Failed to flush bytes to output channel");
+        }
+      g_free (newline_message);
+      newline_message = NULL;
+
+      g_print ("  PIPE: plugin wrote: %s\n", message);
+    }
+
+  PLUGIN_DEBUG ("plugin_send_message_to_appletviewer return");
+}
+
+// Stop the appletviewer process.  When this is called the
+// appletviewer can be in any of three states: running, crashed or
+// hung.  If the appletviewer is running then sending it "shutdown"
+// will cause it to exit.  This will cause
+// plugin_out_pipe_callback/plugin_in_pipe_callback to be called and
+// the input and output channels to be shut down.  If the appletviewer
+// has crashed then plugin_out_pipe_callback/plugin_in_pipe_callback
+// would already have been called and data->appletviewer_alive cleared
+// in which case this function simply returns.  If the appletviewer is
+// hung then this function will be successful and the input and output
+// watches will be removed by plugin_data_destroy.
+// plugin_stop_appletviewer must be called with
+// data->appletviewer_mutex held.
+static void
+plugin_stop_appletviewer ()
+{
+  PLUGIN_DEBUG ("plugin_stop_appletviewer");
+
+  if (jvm_up)
+    {
+      // Shut down the appletviewer.
+      gsize bytes_written = 0;
+
+      if (out_to_appletviewer)
+        {
+          if (g_io_channel_write_chars (out_to_appletviewer, "shutdown",
+                                        -1, &bytes_written, &channel_error)
+              != G_IO_STATUS_NORMAL)
+            {
+              if (channel_error)
+                {
+                  PLUGIN_ERROR_TWO ("Failed to write shutdown message to"
+                                    " appletviewer", channel_error->message);
+                  g_error_free (channel_error);
+                  channel_error = NULL;
+                }
+              else
+                PLUGIN_ERROR ("Failed to write shutdown message to");
+            }
+
+          if (g_io_channel_flush (out_to_appletviewer, &channel_error)
+              != G_IO_STATUS_NORMAL)
+            {
+              if (channel_error)
+                {
+                  PLUGIN_ERROR_TWO ("Failed to write shutdown message to"
+                                    " appletviewer", channel_error->message);
+                  g_error_free (channel_error);
+                  channel_error = NULL;
+                }
+              else
+                PLUGIN_ERROR ("Failed to write shutdown message to");
+            }
+
+          if (g_io_channel_shutdown (out_to_appletviewer,
+                                     TRUE, &channel_error)
+              != G_IO_STATUS_NORMAL)
+            {
+              if (channel_error)
+                {
+                  PLUGIN_ERROR_TWO ("Failed to shut down appletviewer"
+                                    " output channel", channel_error->message);
+                  g_error_free (channel_error);
+                  channel_error = NULL;
+                }
+              else
+                PLUGIN_ERROR ("Failed to shut down appletviewer");
+            }
+        }
+
+      if (in_from_appletviewer)
+        {
+          if (g_io_channel_shutdown (in_from_appletviewer,
+                                     TRUE, &channel_error)
+              != G_IO_STATUS_NORMAL)
+            {
+              if (channel_error)
+                {
+                  PLUGIN_ERROR_TWO ("Failed to shut down appletviewer"
+                                    " input channel", channel_error->message);
+                  g_error_free (channel_error);
+                  channel_error = NULL;
+                }
+              else
+                PLUGIN_ERROR ("Failed to shut down appletviewer");
+            }
+        }
+    }
+
+  jvm_up = FALSE;
+  sleep(2); /* Needed to prevent crashes during debug (when JDWP port is not freed by the kernel right away) */
+
+  PLUGIN_DEBUG ("plugin_stop_appletviewer return");
+}
+
+static void appletviewer_monitor(GPid pid, gint status, gpointer data) 
+{
+    PLUGIN_DEBUG ("appletviewer_monitor");
+    jvm_up = FALSE;
+    pid = -1;
+    PLUGIN_DEBUG ("appletviewer_monitor return");
+}
+
+static void
+plugin_data_destroy (NPP instance)
+{
+  PLUGIN_DEBUG ("plugin_data_destroy");
+
+  GCJPluginData* tofree = (GCJPluginData*) instance->pdata;
+
+  // Remove instance from map
+  gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance);
+
+  if (id_ptr)
+    {
+      gint id = GPOINTER_TO_INT(id_ptr);
+      g_hash_table_remove(instance_to_id_map, instance);
+      g_hash_table_remove(id_to_instance_map, id_ptr);
+    }
+
+  tofree->window_handle = NULL;
+  tofree->window_height = 0;
+  tofree->window_width = 0;
+
+  // cleanup_appletviewer_mutex:
+  g_free (tofree->appletviewer_mutex);
+  tofree->appletviewer_mutex = NULL;
+
+  // cleanup_instance_string:
+  g_free (tofree->instance_string);
+  tofree->instance_string = NULL;
+
+  // cleanup_data:
+  // Eliminate back-pointer to plugin instance.
+  tofree->owner = NULL;
+  (*browserFunctions.memfree) (tofree);
+  tofree = NULL;
+
+  PLUGIN_DEBUG ("plugin_data_destroy return");
+}
+
+// FACTORY FUNCTIONS
+
+// Provides the browser with pointers to the plugin functions that we
+// implement and initializes a local table with browser functions that
+// we may wish to call.  Called once, after browser startup and before
+// the first plugin instance is created.
+// The field 'initialized' is set to true once this function has
+// finished. If 'initialized' is already true at the beginning of
+// this function, then it is evident that NP_Initialize has already
+// been called. There is no need to call this function more than once and
+// this workaround avoids any duplicate calls.
+NPError
+NP_Initialize (NPNetscapeFuncs* browserTable, NPPluginFuncs* pluginTable)
+{
+  PLUGIN_DEBUG ("NP_Initialize");
+  
+  if (initialized)
+    return NPERR_NO_ERROR;
+  else if ((browserTable == NULL) || (pluginTable == NULL))
+    {
+      PLUGIN_ERROR ("Browser or plugin function table is NULL.");
+
+      return NPERR_INVALID_FUNCTABLE_ERROR;
+    }
+  
+  // Ensure that the major version of the plugin API that the browser
+  // expects is not more recent than the major version of the API that
+  // we've implemented.
+  if ((browserTable->version >> 8) > NP_VERSION_MAJOR)
+    {
+      PLUGIN_ERROR ("Incompatible version.");
+
+      return NPERR_INCOMPATIBLE_VERSION_ERROR;
+    }
+
+  // Ensure that the plugin function table we've received is large
+  // enough to store the number of functions that we may provide.
+  if (pluginTable->size < sizeof (NPPluginFuncs))      
+    {
+      PLUGIN_ERROR ("Invalid plugin function table.");
+
+      return NPERR_INVALID_FUNCTABLE_ERROR;
+    }
+
+  // Ensure that the browser function table is large enough to store
+  // the number of browser functions that we may use.
+  if (browserTable->size < sizeof (NPNetscapeFuncs))
+    {
+      PLUGIN_ERROR ("Invalid browser function table.");
+
+      return NPERR_INVALID_FUNCTABLE_ERROR;
+    }
+
+  // Store in a local table the browser functions that we may use.
+  browserFunctions.version = browserTable->version;
+  browserFunctions.size = browserTable->size;
+  browserFunctions.posturl = browserTable->posturl;
+  browserFunctions.geturl = browserTable->geturl;
+  browserFunctions.geturlnotify = browserTable->geturlnotify;
+  browserFunctions.requestread = browserTable->requestread;
+  browserFunctions.newstream = browserTable->newstream;
+  browserFunctions.write = browserTable->write;
+  browserFunctions.destroystream = browserTable->destroystream;
+  browserFunctions.status = browserTable->status;
+  browserFunctions.uagent = browserTable->uagent;
+  browserFunctions.memalloc = browserTable->memalloc;
+  browserFunctions.memfree = browserTable->memfree;
+  browserFunctions.memflush = browserTable->memflush;
+  browserFunctions.reloadplugins = browserTable->reloadplugins;
+  browserFunctions.getvalue = browserTable->getvalue;
+
+  // Return to the browser the plugin functions that we implement.
+  pluginTable->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
+  pluginTable->size = sizeof (NPPluginFuncs);
+  pluginTable->newp = NewNPP_NewProc (GCJ_New);
+  pluginTable->destroy = NewNPP_DestroyProc (GCJ_Destroy);
+  pluginTable->setwindow = NewNPP_SetWindowProc (GCJ_SetWindow);
+  pluginTable->newstream = NewNPP_NewStreamProc (GCJ_NewStream);
+  pluginTable->destroystream = NewNPP_DestroyStreamProc (GCJ_DestroyStream);
+  pluginTable->asfile = NewNPP_StreamAsFileProc (GCJ_StreamAsFile);
+  pluginTable->writeready = NewNPP_WriteReadyProc (GCJ_WriteReady);
+  pluginTable->write = NewNPP_WriteProc (GCJ_Write);
+  pluginTable->print = NewNPP_PrintProc (GCJ_Print);
+  pluginTable->urlnotify = NewNPP_URLNotifyProc (GCJ_URLNotify);
+  pluginTable->getvalue = NewNPP_GetValueProc (GCJ_GetValue);
+
+  // Make sure the plugin data directory exists, creating it if
+  // necessary.
+  data_directory = g_strconcat (getenv ("HOME"), "/.icedteaplugin", NULL);
+  if (!data_directory)
+    {
+      PLUGIN_ERROR ("Failed to create data directory name.");
+      return NPERR_OUT_OF_MEMORY_ERROR;
+    }
+  NPError np_error = NPERR_NO_ERROR;
+  gchar* filename = NULL;
+  if (!g_file_test (data_directory,
+                    (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
+    {
+      int file_error = 0;
+
+      file_error = g_mkdir (data_directory, 0700);
+      if (file_error != 0)
+        {
+          PLUGIN_ERROR_THREE ("Failed to create data directory",
+                              data_directory,
+                              strerror (errno));
+          np_error = NPERR_GENERIC_ERROR;
+          goto cleanup_data_directory;
+        }
+    }
+
+  // Set appletviewer_executable.
+  Dl_info info;
+  if (dladdr ((const void*) GCJ_New, &info) == 0)
+    {
+      PLUGIN_ERROR_TWO ("Failed to determine plugin shared object filename",
+                        dlerror ());
+      np_error = NPERR_GENERIC_ERROR;
+      goto cleanup_data_directory;
+    }
+  filename = g_strdup (info.dli_fname);
+  if (!filename)
+    {
+      PLUGIN_ERROR ("Failed to create plugin shared object filename.");
+      np_error = NPERR_OUT_OF_MEMORY_ERROR;
+      goto cleanup_data_directory;
+    }
+  appletviewer_executable = g_strdup_printf ("%s/../../bin/java",
+                                             dirname (filename));
+  if (!appletviewer_executable)
+    {
+      PLUGIN_ERROR ("Failed to create appletviewer executable name.");
+      np_error = NPERR_OUT_OF_MEMORY_ERROR;
+      goto cleanup_filename;
+    }
+
+  np_error = plugin_test_appletviewer ();
+  if (np_error != NPERR_NO_ERROR)
+    {
+      plugin_display_failure_dialog ();
+      goto cleanup_appletviewer_executable;
+    }
+  g_free (filename);
+
+  initialized = true;
+
+  // Initialize threads (needed for mutexes).
+  if (!g_thread_supported ())
+    g_thread_init (NULL);
+
+  plugin_instance_mutex = g_mutex_new ();
+
+  PLUGIN_DEBUG_TWO ("NP_Initialize: using", appletviewer_executable);
+
+  PLUGIN_DEBUG ("NP_Initialize return");
+
+  return NPERR_NO_ERROR;
+
+ cleanup_appletviewer_executable:
+  if (appletviewer_executable)
+    {
+      g_free (appletviewer_executable);
+      appletviewer_executable = NULL;
+    }
+
+ cleanup_filename:
+  if (filename)
+    {
+      g_free (filename);
+      filename = NULL;
+    }
+
+ cleanup_data_directory:
+  if (data_directory)
+    {
+      g_free (data_directory);
+      data_directory = NULL;
+    }
+
+  return np_error;
+}
+
+// Returns a string describing the MIME type that this plugin
+// handles.
+char*
+NP_GetMIMEDescription (void)
+{
+  PLUGIN_DEBUG ("NP_GetMIMEDescription");
+
+  PLUGIN_DEBUG ("NP_GetMIMEDescription return");
+
+  return (char*) PLUGIN_MIME_DESC;
+}
+
+// Returns a value relevant to the plugin as a whole.  The browser
+// calls this function to obtain information about the plugin.
+NPError
+NP_GetValue (void* future, NPPVariable variable, void* value)
+{
+  PLUGIN_DEBUG ("NP_GetValue");
+
+  NPError result = NPERR_NO_ERROR;
+  gchar** char_value = (gchar**) value;
+
+  switch (variable)
+    {
+    case NPPVpluginNameString:
+      PLUGIN_DEBUG ("NP_GetValue: returning plugin name.");
+      *char_value = g_strdup (PLUGIN_NAME " " PACKAGE_VERSION);
+      break;
+
+    case NPPVpluginDescriptionString:
+      PLUGIN_DEBUG ("NP_GetValue: returning plugin description.");
+      *char_value = g_strdup (PLUGIN_DESC);
+      break;
+
+    default:
+      PLUGIN_ERROR ("Unknown plugin value requested.");
+      result = NPERR_GENERIC_ERROR;
+      break;
+    }
+
+  PLUGIN_DEBUG ("NP_GetValue return");
+
+  return result;
+}
+
+// Shuts down the plugin.  Called after the last plugin instance is
+// destroyed.
+NPError
+NP_Shutdown (void)
+{
+  PLUGIN_DEBUG ("NP_Shutdown");
+
+  // Free mutex.
+  if (plugin_instance_mutex)
+    {
+      g_mutex_free (plugin_instance_mutex);
+      plugin_instance_mutex = NULL;
+    }
+
+  if (data_directory)
+    {
+      g_free (data_directory);
+      data_directory = NULL;
+    }
+
+  if (appletviewer_executable)
+    {
+      g_free (appletviewer_executable);
+      appletviewer_executable = NULL;
+    }
+
+  // stop the appletviewer
+  plugin_stop_appletviewer();
+
+  // remove monitor
+  if (appletviewer_watch_id != -1)
+    g_source_remove(appletviewer_watch_id);
+
+  // Removing a source is harmless if it fails since it just means the
+  // source has already been removed.
+  g_source_remove (in_watch_source);
+  in_watch_source = 0;
+
+  // cleanup_in_from_appletviewer:
+  if (in_from_appletviewer)
+    g_io_channel_unref (in_from_appletviewer);
+  in_from_appletviewer = NULL;
+
+  // cleanup_out_watch_source:
+  g_source_remove (out_watch_source);
+  out_watch_source = 0;
+
+  // cleanup_out_to_appletviewer:
+  if (out_to_appletviewer)
+    g_io_channel_unref (out_to_appletviewer);
+  out_to_appletviewer = NULL;
+
+  // cleanup_out_pipe:
+  // Delete output pipe.
+  PLUGIN_DEBUG_TWO ("NP_Shutdown: deleting output fifo:", out_pipe_name);
+  unlink (out_pipe_name);
+  PLUGIN_DEBUG_TWO ("NP_Shutdown: deleted output fifo:", out_pipe_name);
+
+  // cleanup_out_pipe_name:
+  g_free (out_pipe_name);
+  out_pipe_name = NULL;
+
+  // cleanup_in_pipe:
+  // Delete input pipe.
+  PLUGIN_DEBUG_TWO ("NP_Shutdown: deleting input fifo:", in_pipe_name);
+  unlink (in_pipe_name);
+  PLUGIN_DEBUG_TWO ("NP_Shutdown: deleted input fifo:", in_pipe_name);
+
+  // cleanup_in_pipe_name:
+  g_free (in_pipe_name);
+  in_pipe_name = NULL;
+
+  initialized = false;
+
+  PLUGIN_DEBUG ("NP_Shutdown return");
+
+  return NPERR_NO_ERROR;
+}
--- a/Makefile.am	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
--- 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,
--- /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;
+    }
+
+}
+
--- /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:<ul>
+ * <li>JSObject is converted to the original JavaScript object
+ * <li>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.
+ * <li>Java arrays are wrapped with a JavaScript object that understands
+ *   array.length and array[index]
+ * <li>A Java boolean is converted to a JavaScript boolean
+ * <li>Java byte, char, short, int, long, float, and double are converted
+ *   to JavaScript numbers
+ * </ul>
+ * Values passed from JavaScript to Java are converted as follows:<ul>
+ * <li>objects which are wrappers around java objects are unwrapped
+ * <li>other objects are wrapped with a JSObject
+ * <li>strings, numbers and booleans are converted to String, Double,
+ *   and Boolean objects respectively
+ * </ul>
+ * 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. <code>(String) window.getMember("name");</code> or
+ * <code>(JSObject) window.getMember("document");</code>.
+ */
+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.<i>name</i>" 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[<i>index</i>]" 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.<i>name</i> = <i>value</i>" 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[<i>index</i>] = <i>value</i>" 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.<i>methodName</i>(<i>args</i>[0], <i>args</i>[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;
+    }
+
+}
--- /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);
+}
--- /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);
+		}
+	}
+}
--- /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();
+    }
+}
--- /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<Integer, PluginAppletSecurityContext> 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);
+	}
+}
--- /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;
+    }
+}
+
--- /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;
+    }
+}
--- /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<Object> 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<ClassLoader> 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<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
+        Set<Thread> 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();
+            }
+
+        }
+
+    }
+
+}
--- /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("<html>The " + type + " server at " + host + " is requesting authentication. It says \"" + prompt + "\"</html>");
+
+        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);
+    }
+}
--- /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<Class> 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<Class>(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<ClassLoader, String> classLoaders = new Hashtable<ClassLoader, String>();
+
+	// 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> V parseCall(String s, ClassLoader cl, Class<V> 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("<init>")
+						|| methodName.equals("<clinit>")) {
+					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<Object> () {
+					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<Object> () {
+					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<Object> () {
+					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<Object> () {
+					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<Object> () {
+					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<Object> () {
+					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<Object> () {
+					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, "<init>", "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;I)V");
+		prepopulateMethod(classID, "<init>", "(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, "<init>", "(Z)V");
+
+		classID = prepopulateClass("java/lang/Double");
+		prepopulateMethod(classID, "doubleValue", "()D");
+		prepopulateMethod(classID, "<init>", "(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("<init>")
+					|| methodName.equals("<clinit>")) {
+				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("<<ALL FILES>>", "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("<<ALL FILES>>", "delete");
+				grantedPermissions.add(fp);
+			} else if (privilege.equals("UniversalFileRead")) {
+				FilePermission fp = new FilePermission("<<ALL FILES>>", "read");
+				grantedPermissions.add(fp);
+			} else if (privilege.equals("UniversalFileWrite")) {
+				FilePermission fp = new FilePermission("<<ALL FILES>>", "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");
+		}
+	}
+}
--- /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<Integer, PluginParseRequest> requests = new HashMap();
+ 
+     // Instance identifier -> PluginAppletViewer object.
+     private static HashMap<Integer, PluginAppletViewer> applets = new HashMap();
+     
+     private static PluginStreamHandler streamhandler;
+     
+     private static PluginCallRequestFactory requestFactory;
+     
+     private static HashMap<Integer, String> siteCookies = new HashMap<Integer,String>();
+     
+     private 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("<applet");
+ 
+ 	String v = (String)atts.get("codebase");
+ 	if (v != null) {
+ 	    out.print(" codebase=\"" + v + "\"");
+ 	}
+ 
+ 	v = (String)atts.get("code");
+ 	if (v == null) {
+ 	    v = "applet.class";
+ 	}
+ 	out.print(" code=\"" + v + "\"");
+ 	v = (String)atts.get("width");
+ 	if (v == null) {
+ 	    v = "150";
+ 	}
+ 	out.print(" width=" + v);
+ 
+ 	v = (String)atts.get("height");
+ 	if (v == null) {
+ 	    v = "100";
+ 	}
+ 	out.print(" height=" + v);
+ 
+ 	v = (String)atts.get("name");
+ 	if (v != null) {
+ 	    out.print(" name=\"" + v + "\"");
+ 	}
+ 	out.println(">");
+ 
+ 	// 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("<param name=" + param +
+ 			    " value=\"" + atts.get(param) + "\">");
+ 	    }
+ 	}
+ 	out.println("</applet>");
+     }
+ 
+     /**
+      * 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 <!-- ... , just mark that we have passed it
+                     commentHeaderPassed = true;
+                 }
+
+             } else if (commentHeaderPassed == false) {
+                 buf.append((char)c);
+                 PluginDebug.debug("Warning: Attempted to skip comment, but this tag does not appear to be a comment: " + buf.toString());
+                 return;
+             }
+
+             c = in.read();
+             buf.append((char)c);
+         }
+     }
+ 
+     /**
+      * Scan tag
+      */
+     public static Hashtable scanTag(Reader in) throws IOException {
+ 	Hashtable atts = new Hashtable();
+ 	skipSpace(in);
+         while (c >= 0 && c != '>') {
+ 	    String att = scanIdentifier(in);
+ 	    String val = "";
+ 	    skipSpace(in);
+ 	    if (c == '=') {
+ 		int quote = -1;
+ 		c = in.read();
+ 		skipSpace(in);
+ 		if ((c == '\'') || (c == '\"')) {
+ 		    quote = c;
+ 		    c = in.read();
+ 		}
+ 		StringBuffer buf = new StringBuffer();
+                 while ((c > 0) &&
+ 		       (((quote < 0) && (c != ' ') && (c != '\t') &&
+                          (c != '\n') && (c != '\r') && (c != '>'))
+ 			|| ((quote >= 0) && (c != quote)))) {
+ 		    buf.append((char)c);
+ 		    c = in.read();
+ 		}
+ 		if (c == quote) {
+ 		    c = in.read();
+ 		}
+ 		skipSpace(in);
+ 		val = buf.toString();
+ 	    }
+
+        att = att.replace("&gt;", ">");
+        att = att.replace("&lt;", "<");
+        att = att.replace("&amp;", "&");
+        att = att.replace("&#10;", "\n");
+        att = att.replace("&#13;", "\r");
+ 	    
+        val = val.replace("&gt;", ">");
+        val = val.replace("&lt;", "<");
+        val = val.replace("&amp;", "&");
+        val = val.replace("&#10;", "\n");
+        val = val.replace("&#13;", "\r");
+
+        PluginDebug.debug("PUT " + att + " = '" + val + "'");
+        atts.put(att.toLowerCase(java.util.Locale.ENGLISH), val);
+
+             while (true) {
+                 if ((c == '>') || (c < 0) ||
+                     ((c >= 'a') && (c <= 'z')) ||
+                     ((c >= 'A') && (c <= 'Z')) ||
+                     ((c >= '0') && (c <= '9')) || (c == '_'))
+                     break;
+                 c = in.read();
+             }
+             //skipSpace(in);
+ 	}
+ 	return atts;
+     }
+     
+     // private static final == inline
+     private static final boolean isInt(Object o) {
+         boolean isInt = false;
+
+         try {
+             Integer.parseInt((String) o);
+             isInt = true;
+         } catch (Exception e) {
+             // don't care
+         }
+
+         return isInt;
+     }
+ 
+     /* values used for placement of AppletViewer's frames */
+     private static int x = 0;
+     private static int y = 0;
+     private static final int XDELTA = 30;
+     private static final int YDELTA = XDELTA;
+ 
+     static String encoding = null;
+ 
+     static private Reader makeReader(InputStream is) {
+ 	if (encoding != null) {
+ 	    try {
+ 		return new BufferedReader(new InputStreamReader(is, encoding));
+ 	    } catch (IOException x) { }
+ 	}
+ 	InputStreamReader r = new InputStreamReader(is);
+ 	encoding = r.getEncoding();
+ 	return new BufferedReader(r);
+     }
+ 
+     /**
+      * Scan an html file for <applet> tags
+      */
+     public static void parse(int identifier, long handle, Reader in, URL url, String enc)
+         throws IOException {
+         encoding = enc;
+         parse(identifier, handle, in, url, System.out, new PluginAppletViewerFactory());
+     }
+ 
+     public static void parse(int identifier, long handle, Reader in, URL url)
+         throws IOException {
+         
+         // wait until cookie is set (even if cookie is null, it needs to be 
+         // "set" to that first
+         while (!siteCookies.containsKey(identifier));
+
+    	 final int fIdentifier = identifier;
+    	 final long fHandle = handle;
+    	 final Reader fIn = in;
+    	 final URL fUrl = url;
+    	 PrivilegedAction pa = new PrivilegedAction() {
+    		 public Object run() {
+    			 try {
+    				 parse(fIdentifier, fHandle, fIn, fUrl, System.out, new PluginAppletViewerFactory());
+    			 } catch (IOException ioe) {
+    				 return ioe;
+    			 }
+    	         
+    			 return null;
+    		 }
+    	 };
+
+    	 Object ret = AccessController.doPrivileged(pa);
+    	 if (ret instanceof IOException) {
+    		 throw (IOException) ret;
+    	 }
+     }
+ 
+     public static void parse(int identifier, long handle, Reader in, URL url,
+                              PrintStream statusMsgStream,
+                              PluginAppletViewerFactory factory)
+         throws IOException
+     {
+    	 // <OBJECT> <EMBED> tag flags
+    	 boolean isAppletTag = false;
+    	 boolean isObjectTag = false;
+    	 boolean isEmbedTag = false;
+
+    	 // warning messages
+    	 String requiresNameWarning = amh.getMessage("parse.warning.requiresname");
+    	 String paramOutsideWarning = amh.getMessage("parse.warning.paramoutside");
+    	 String appletRequiresCodeWarning = amh.getMessage("parse.warning.applet.requirescode");
+    	 String appletRequiresHeightWarning = amh.getMessage("parse.warning.applet.requiresheight");
+    	 String appletRequiresWidthWarning = amh.getMessage("parse.warning.applet.requireswidth");
+    	 String objectRequiresCodeWarning = amh.getMessage("parse.warning.object.requirescode");
+    	 String objectRequiresHeightWarning = amh.getMessage("parse.warning.object.requiresheight");
+    	 String objectRequiresWidthWarning = amh.getMessage("parse.warning.object.requireswidth");
+    	 String embedRequiresCodeWarning = amh.getMessage("parse.warning.embed.requirescode");
+    	 String embedRequiresHeightWarning = amh.getMessage("parse.warning.embed.requiresheight");
+    	 String embedRequiresWidthWarning = amh.getMessage("parse.warning.embed.requireswidth");
+    	 String appNotLongerSupportedWarning = amh.getMessage("parse.warning.appnotLongersupported");
+
+    	 java.net.URLConnection conn = url.openConnection();
+    	 /* The original URL may have been redirected - this
+    	  * sets it to whatever URL/codebase we ended up getting
+    	  */
+    	 url = conn.getURL();
+
+    	 int ydisp = 1;
+    	 Hashtable atts = null;
+
+    	 while(true) {
+    		 c = in.read();
+    		 if (c == -1)
+    			 break;
+
+    		 if (c == '<') {
+    			 c = in.read();
+    			 if (c == '/') {
+    				 c = in.read();
+    				 String nm = scanIdentifier(in);
+    				 if (nm.equalsIgnoreCase("applet") ||
+    						 nm.equalsIgnoreCase("object") ||
+    						 nm.equalsIgnoreCase("embed")) {
+
+    					 // We can't test for a code tag until </OBJECT>
+    					 // because it is a parameter, not an attribute.
+    					 if(isObjectTag) {
+    						 if (atts.get("code") == null && atts.get("object") == null) {
+    							 statusMsgStream.println(objectRequiresCodeWarning);
+    							 atts = null;
+    						 }
+    					 }
+
+    					 if (atts != null) {
+    						 // XXX 5/18 In general this code just simply
+    						 // shouldn't be part of parsing.  It's presence
+    						 // causes things to be a little too much of a
+    						 // hack.
+    						 factory.createAppletViewer(identifier, handle, x, y, url, atts);
+    						 x += XDELTA;
+    						 y += YDELTA;
+    						 // make sure we don't go too far!
+    						 Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
+    						 if ((x > d.width - 300) || (y > d.height - 300)) {
+    							 x = 0;
+    							 y = 2 * ydisp * YDELTA;
+    							 ydisp++;
+    						 }
+    					 }
+    					 atts = null;
+    					 isAppletTag = false;
+    					 isObjectTag = false;
+    					 isEmbedTag = false;
+    				 }
+    			 }
+    			 else {
+    				 String nm = scanIdentifier(in);
+    				 if (nm.equalsIgnoreCase("param")) {
+    					 Hashtable t = scanTag(in);
+    					 String att = (String)t.get("name");
+    					 if (att == null) {
+    						 statusMsgStream.println(requiresNameWarning);
+    					 } else {
+    						 String val = (String)t.get("value");
+    						 if (val == null) {
+    							 statusMsgStream.println(requiresNameWarning);
+    						 } else if (atts != null) {
+    							 att = att.replace("&gt;", ">");
+    							 att = att.replace("&lt;", "<");
+    							 att = att.replace("&amp;", "&");
+    							 att = att.replace("&#10;", "\n");
+    							 att = att.replace("&#13;", "\r");
+    							 att = att.replace("&quot;", "\"");
+
+    							 val = val.replace("&gt;", ">");
+    							 val = val.replace("&lt;", "<");
+    							 val = val.replace("&amp;", "&");
+    							 val = val.replace("&#10;", "\n");
+    							 val = val.replace("&#13;", "\r");
+    							 val = val.replace("&quot;", "\"");
+    							 PluginDebug.debug("PUT " + att + " = " + val);
+   							     atts.put(att.toLowerCase(), val);
+    						 } else {
+    							 statusMsgStream.println(paramOutsideWarning);
+    						 }
+    					 }
+    				 }
+    				 else if (nm.equalsIgnoreCase("applet")) {
+    					 isAppletTag = true;
+    					 atts = scanTag(in);
+
+                         // If there is a classid and no code tag present, transform it to code tag
+                         if (atts.get("code") == null && atts.get("classid") != null) {
+                             atts.put("code", atts.get("classid"));
+                         }
+                         
+                         // remove java: from code tag
+                         if (atts.get("code") != null && ((String) atts.get("code")).startsWith("java:")) {
+                             atts.put("code", ((String) atts.get("code")).substring(5));
+                         }
+
+    					 if (atts.get("code") == null && atts.get("object") == null) {
+    						 statusMsgStream.println(appletRequiresCodeWarning);
+    						 atts = null;
+    					 }
+
+    					 if (atts.get("width") == null || !isInt(atts.get("width"))) {
+    						 atts.put("width", "1000");
+    						 atts.put("widthPercentage", 100);
+    					 } else if (((String) atts.get("width")).endsWith("%")) {
+    						 String w = (String) atts.get("width");
+    						 atts.put("width", "1000");
+    						 atts.put("widthPercentage", Integer.parseInt((w.substring(0,  w.length() -1))));
+    					  }
+
+    					 if (atts.get("height") == null || !isInt(atts.get("height"))) {
+    						 atts.put("height", "1000");
+    						 atts.put("heightPercentage", 100);
+    					 } else if (((String) atts.get("height")).endsWith("%")) {
+    						 String h = (String) atts.get("height");
+    						 atts.put("height", "1000");
+    						 atts.put("heightPercentage", Integer.parseInt(h.substring(0,  h.length() -1)));
+    					 }
+    				 }
+    				 else if (nm.equalsIgnoreCase("object")) {
+    					 isObjectTag = true;
+    					 atts = scanTag(in);
+
+    					 // If there is a classid and no code tag present, transform it to code tag
+                         if (atts.get("code") == null && atts.get("classid") != null) {
+                             atts.put("code", atts.get("classid"));
+                         }
+                         
+                         // remove java: from code tag
+                         if (atts.get("code") != null && ((String) atts.get("code")).startsWith("java:")) {
+                             atts.put("code", ((String) atts.get("code")).substring(5));
+                         }
+
+                         // java_* aliases override older names:
+                         // http://java.sun.com/j2se/1.4.2/docs/guide/plugin/developer_guide/using_tags.html#in-ie
+                         if (atts.get("java_code") != null) {
+                             atts.put("code", ((String) atts.get("java_code")));
+                         }
+
+                         if (atts.get("java_codebase") != null) {
+                             atts.put("codebase", ((String) atts.get("java_codebase")));
+                         }
+
+                         if (atts.get("java_archive") != null) {
+                             atts.put("archive", ((String) atts.get("java_archive")));
+                         }
+
+                         if (atts.get("java_object") != null) {
+                             atts.put("object", ((String) atts.get("java_object")));
+                         }
+
+                         if (atts.get("java_type") != null) {
+                             atts.put("type", ((String) atts.get("java_type")));
+                         }
+
+    					 if (atts.get("width") == null || !isInt(atts.get("width"))) {
+    						 atts.put("width", "1000");
+    						 atts.put("widthPercentage", 100);
+    					 } else if (((String) atts.get("width")).endsWith("%")) {
+    						 String w = (String) atts.get("width");
+    						 atts.put("width", "1000");
+    						 atts.put("widthPercentage", Integer.parseInt(w.substring(0,  w.length() -1)));
+    					 }
+
+    					 if (atts.get("height") == null || !isInt(atts.get("height"))) {
+    						 atts.put("height", "1000");
+    						 atts.put("heightPercentage", 100);
+    					 } else if (((String) atts.get("height")).endsWith("%")) {
+    						 String h = (String) atts.get("height");
+    						 atts.put("height", "1000");
+    						 atts.put("heightPercentage", Integer.parseInt(h.substring(0,  h.length() -1)));
+    					 }
+    				 }
+    				 else if (nm.equalsIgnoreCase("embed")) {
+    					 isEmbedTag = true;
+    					 atts = scanTag(in);
+
+                         // If there is a classid and no code tag present, transform it to code tag
+                         if (atts.get("code") == null && atts.get("classid") != null) {
+                             atts.put("code", atts.get("classid"));
+                         }
+                         
+                         // remove java: from code tag
+                         if (atts.get("code") != null && ((String) atts.get("code")).startsWith("java:")) {
+                             atts.put("code", ((String) atts.get("code")).substring(5));
+                         }
+    					 
+    					 // java_* aliases override older names:
+    					 // http://java.sun.com/j2se/1.4.2/docs/guide/plugin/developer_guide/using_tags.html#in-nav
+    					 if (atts.get("java_code") != null) {
+    					     atts.put("code", ((String) atts.get("java_code")));
+    					 }
+    					 
+                         if (atts.get("java_codebase") != null) {
+                             atts.put("codebase", ((String) atts.get("java_codebase")));
+                         }
+                         
+                         if (atts.get("java_archive") != null) {
+                             atts.put("archive", ((String) atts.get("java_archive")));
+                         }
+                         
+                         if (atts.get("java_object") != null) {
+                             atts.put("object", ((String) atts.get("java_object")));
+                         }
+    					 
+                         if (atts.get("java_type") != null) {
+                             atts.put("type", ((String) atts.get("java_type")));
+                         }
+
+    					 if (atts.get("code") == null && atts.get("object") == null) {
+    						 statusMsgStream.println(embedRequiresCodeWarning);
+    						 atts = null;
+    					 }
+    					 
+    					 if (atts.get("width") == null || !isInt(atts.get("width"))) {
+    						 atts.put("width", "1000");
+    						 atts.put("widthPercentage", 100);
+    					 } else if (((String) atts.get("width")).endsWith("%")) {
+    						 String w = (String) atts.get("width");
+    						 atts.put("width", "100");
+    						 atts.put("widthPercentage", Integer.parseInt(w.substring(0,  w.length() -1)));
+    					 }
+
+    					 if (atts.get("height") == null || !isInt(atts.get("height"))) {
+    						 atts.put("height", "1000");
+    						 atts.put("heightPercentage", 100);
+    					 } else if (((String) atts.get("height")).endsWith("%")) {
+    						 String h = (String) atts.get("height");
+    						 atts.put("height", "100");
+    						 atts.put("heightPercentage", Integer.parseInt(h.substring(0,  h.length() -1)));
+    					 }
+    				 }
+    				 else if (nm.equalsIgnoreCase("app")) {
+    					 statusMsgStream.println(appNotLongerSupportedWarning);
+    					 Hashtable atts2 = scanTag(in);
+    					 nm = (String)atts2.get("class");
+    					 if (nm != null) {
+    						 atts2.remove("class");
+    						 atts2.put("code", nm + ".class");
+    					 }
+    					 nm = (String)atts2.get("src");
+    					 if (nm != null) {
+    						 atts2.remove("src");
+    						 atts2.put("codebase", nm);
+    					 }
+    					 if (atts2.get("width") == null || !isInt(atts2.get("width"))) {
+    						 atts2.put("width", "1000");
+    						 atts2.put("widthPercentage", 100);
+    					 } else if (((String) atts.get("width")).endsWith("%")) {
+    						 String w = (String) atts.get("width");
+    						 atts2.put("width", "100");
+    						 atts2.put("widthPercentage", Integer.parseInt(w.substring(0,  w.length() -1)));
+    					 }
+
+    					 if (atts2.get("height") == null || !isInt(atts2.get("height"))) {
+    						 atts2.put("height", "1000");
+    						 atts2.put("heightPercentage", 100);
+    					 } else if (((String) atts.get("height")).endsWith("%")) {
+    						 String h = (String) atts.get("height");
+    						 atts2.put("height", "100");
+    						 atts2.put("heightPercentage", Integer.parseInt(h.substring(0,  h.length() -1)));
+    					 }
+
+    					 printTag(statusMsgStream, atts2);
+    					 statusMsgStream.println();
+    				 }
+    			 }
+    		 }
+    	 }
+    	 in.close();
+     }
+ 
+
+     private static AppletMessageHandler amh = new AppletMessageHandler("appletviewer");
+ 
+     private static void checkConnect(URL url)
+     {
+ 	SecurityManager security = System.getSecurityManager();
+ 	if (security != null) {
+ 	    try {
+ 		java.security.Permission perm =
+ 		    url.openConnection().getPermission();
+ 		if (perm != null)
+ 		    security.checkPermission(perm);
+ 		else
+ 		    security.checkConnect(url.getHost(), url.getPort());
+ 	    } catch (java.io.IOException ioe) {
+ 		    security.checkConnect(url.getHost(), url.getPort());
+ 	    }
+ 	}
+     }
+ }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginCallRequest.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,85 @@
+/* PluginCallRequest -- 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;
+
+// FIXME: for each type of request extend a new (anonymous?)
+// PluginCallRequest.
+public abstract class PluginCallRequest {
+    String message;
+    String returnString;
+    PluginCallRequest next;
+    boolean done = false;
+
+    public PluginCallRequest(String message, String returnString) {
+        this.message = message;
+        this.returnString = returnString;
+    }
+
+    public String getMessage() {
+    	return this.message;
+    }
+    
+    public String getReturnString() {
+    	return this.returnString;
+    }
+    
+    public boolean isDone() {
+    	return this.done;
+    }
+    
+    public boolean setDone(boolean done) {
+    	return this.done = done;
+    }
+    
+    public void setNext(PluginCallRequest next) {
+    	this.next = next;
+    }
+    
+    public PluginCallRequest getNext() {
+    	return this.next;
+    }
+
+    public abstract void parseReturn(String message);
+    
+    public abstract boolean serviceable(String message);
+    
+    public abstract Object getObject();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginCallRequestFactory.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,60 @@
+/* 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;
+
+
+public class PluginCallRequestFactory {
+
+	public PluginCallRequest getPluginCallRequest(String id, String message, String returnString) {
+
+		if (id == "member") {
+			return new GetMemberPluginCallRequest(message, returnString);
+		} else if (id == "void") {
+			return new VoidPluginCallRequest(message, returnString);
+		} else if (id == "window") {
+			return new GetWindowPluginCallRequest(message, returnString);
+		} else if (id == "proxyinfo") {
+            return new PluginProxyInfoRequest(message, returnString);
+        } else {
+			throw new RuntimeException ("Unknown plugin call request type requested from factory");
+		}
+		
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginClassLoader.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,51 @@
+/* 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;
+
+public class PluginClassLoader extends ClassLoader {
+
+	public PluginClassLoader() {
+		super();
+	}
+
+	public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+		return super.loadClass(name, resolve);
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginDebug.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,51 @@
+/* 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.io.*;
+
+public class PluginDebug {
+
+	static final boolean DEBUG = System.getenv().containsKey("ICEDTEAPLUGIN_DEBUG"); 
+
+    public static void debug(String message) {
+    	if (DEBUG)
+    		System.err.println(message);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginException.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,53 @@
+/* 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;
+
+
+public class PluginException extends Exception {
+
+	public PluginException (PluginStreamHandler sh, int instance, int reference, Throwable t) {
+		t.printStackTrace();
+		this.setStackTrace(t.getStackTrace());
+		
+		AppletSecurityContextManager.dumpStore(0);
+
+		String message = "instance " + instance + " reference " + reference + " Error " + t.getMessage();
+		sh.write(message);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginMain.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,315 @@
+/* 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 1999-2006 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.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.Authenticator;
+import java.net.PasswordAuthentication;
+import java.net.ProxySelector;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Properties;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+import net.sourceforge.jnlp.security.VariableX509TrustManager;
+
+/**
+ * The main entry point into PluginAppletViewer.
+ */
+public class PluginMain
+{
+
+    // the files where stdout/stderr are sent to
+    public static final String PLUGIN_STDERR_FILE = System.getProperty("user.home") + "/.icedteaplugin/java.stderr";
+    public static final String PLUGIN_STDOUT_FILE = System.getProperty("user.home") + "/.icedteaplugin/java.stdout";
+
+	final boolean redirectStreams = System.getenv().containsKey("ICEDTEAPLUGIN_DEBUG");
+	static PluginStreamHandler streamHandler;
+	
+    // This is used in init().	Getting rid of this is desirable but depends
+    // on whether the property that uses it is necessary/standard.
+    public static final String theVersion = System.getProperty("java.version");
+    
+    private PluginAppletSecurityContext securityContext;
+
+    /**
+     * The main entry point into AppletViewer.
+     */
+    public static void main(String args[])
+	throws IOException
+    {
+
+    	try {
+    		PluginMain pm = new PluginMain(System.getProperty("user.home") + "/.icedteaplugin/icedteanp-plugin-to-appletviewer", System.getProperty("user.home") + "/.icedteaplugin/icedteanp-appletviewer-to-plugin");
+    	} catch (Exception e) {
+    		e.printStackTrace();
+    		System.err.println("Something very bad happened. I don't know what to do, so I am going to exit :(");
+    		System.exit(1);
+    	}
+    }
+
+    public PluginMain(String inPipe, String outPipe) {
+    	
+    	try {
+    		File errFile = new File(PLUGIN_STDERR_FILE);
+    		File outFile = new File(PLUGIN_STDOUT_FILE);
+
+    		System.setErr(new TeeOutputStream(new FileOutputStream(errFile), System.err));
+    		System.setOut(new TeeOutputStream(new FileOutputStream(outFile), System.out));
+    	} catch (Exception e) {
+    		PluginDebug.debug("Unable to redirect streams");
+    		e.printStackTrace();
+    	}
+
+    	connect(inPipe, outPipe);
+
+    	securityContext = new PluginAppletSecurityContext(0);
+    	securityContext.prePopulateLCClasses();
+    	securityContext.setStreamhandler(streamHandler);
+    	AppletSecurityContextManager.addContext(0, securityContext);
+
+		PluginAppletViewer.setStreamhandler(streamHandler);
+		PluginAppletViewer.setPluginCallRequestFactory(new PluginCallRequestFactory());
+
+    	init();
+
+		// Streams set. Start processing.
+		streamHandler.startProcessing();
+    }
+
+	public void connect(String inPipe, String outPipe) {
+		try {
+			streamHandler = new PluginStreamHandler(new FileInputStream(inPipe), new FileOutputStream(outPipe));
+	    	PluginDebug.debug("Streams initialized");
+		} catch (IOException ioe) {
+			ioe.printStackTrace();
+		}
+	}
+
+	private static void init() {
+		Properties avProps = new Properties();
+
+		// ADD OTHER RANDOM PROPERTIES
+		// XXX 5/18 need to revisit why these are here, is there some
+		// standard for what is available?
+
+		// Standard browser properties
+		avProps.put("browser", "sun.applet.AppletViewer");
+		avProps.put("browser.version", "1.06");
+		avProps.put("browser.vendor", "Sun Microsystems Inc.");
+		avProps.put("http.agent", "Java(tm) 2 SDK, Standard Edition v" + theVersion);
+
+		// Define which packages can be extended by applets
+		// XXX 5/19 probably not needed, not checked in AppletSecurity
+		avProps.put("package.restrict.definition.java", "true");
+		avProps.put("package.restrict.definition.sun", "true");
+
+		// Define which properties can be read by applets.
+		// A property named by "key" can be read only when its twin
+		// property "key.applet" is true.  The following ten properties
+		// are open by default.	 Any other property can be explicitly
+		// opened up by the browser user by calling appletviewer with
+		// -J-Dkey.applet=true
+		avProps.put("java.version.applet", "true");
+		avProps.put("java.vendor.applet", "true");
+		avProps.put("java.vendor.url.applet", "true");
+		avProps.put("java.class.version.applet", "true");
+		avProps.put("os.name.applet", "true");
+		avProps.put("os.version.applet", "true");
+		avProps.put("os.arch.applet", "true");
+		avProps.put("file.separator.applet", "true");
+		avProps.put("path.separator.applet", "true");
+		avProps.put("line.separator.applet", "true");
+
+		// Read in the System properties.  If something is going to be
+		// over-written, warn about it.
+		Properties sysProps = System.getProperties();
+		for (Enumeration e = sysProps.propertyNames(); e.hasMoreElements(); ) {
+			String key = (String) e.nextElement();
+			String val = (String) sysProps.getProperty(key);
+			avProps.setProperty(key, val);
+		}
+
+		// INSTALL THE PROPERTY LIST
+		System.setProperties(avProps);
+
+
+		try {
+		    SSLSocketFactory sslSocketFactory;
+		    SSLContext context = SSLContext.getInstance("SSL");
+		    TrustManager[] trust = new TrustManager[] { VariableX509TrustManager.getInstance() };
+		    context.init(null, trust, null);
+		    sslSocketFactory = context.getSocketFactory();
+		    
+		    HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
+		} catch (Exception e) {
+		    System.err.println("Unable to set SSLSocketfactory (may _prevent_ access to sites that should be trusted)! Continuing anyway...");
+		    e.printStackTrace();
+		}
+        
+		// plug in a custom authenticator and proxy selector
+        Authenticator.setDefault(new CustomAuthenticator());
+        ProxySelector.setDefault(new PluginProxySelector());
+	}
+
+    static boolean messageAvailable() {
+    	return streamHandler.messageAvailable();
+    }
+
+    static String getMessage() {
+    	return streamHandler.getMessage();
+    }
+    
+    static class CustomAuthenticator extends Authenticator {
+        
+        public PasswordAuthentication getPasswordAuthentication() {
+
+            // No security check is required here, because the only way to 
+            // set parameters for which auth info is needed 
+            // (Authenticator:requestPasswordAuthentication()), has a security 
+            // check
+
+            String type = this.getRequestorType() == RequestorType.PROXY ? "proxy" : "web"; 
+
+            // request auth info from user
+            PasswordAuthenticationDialog pwDialog = new PasswordAuthenticationDialog();
+            PasswordAuthentication auth = pwDialog.askUser(this.getRequestingHost(), this.getRequestingPort(), this.getRequestingPrompt(), type);
+            
+            // send it along
+            return auth;
+        }
+    }
+
+    /**
+     * Behaves like the 'tee' command, sends output to both actual std stream and a
+     * file
+     */
+    class TeeOutputStream extends PrintStream {
+
+        // Everthing written to TeeOutputStream is written to this file
+        PrintStream logFile;
+
+        public TeeOutputStream(FileOutputStream fileOutputStream,
+                PrintStream stdStream) {
+            super(stdStream);
+            logFile = new PrintStream(fileOutputStream);
+        }
+
+        @Override
+        public boolean checkError() {
+            boolean thisError = super.checkError();
+            boolean fileError = logFile.checkError();
+
+            return thisError || fileError;
+        }
+
+        @Override
+        public void close() {
+            logFile.close();
+            super.close();
+        }
+
+        @Override
+        public void flush() {
+            logFile.flush();
+            super.flush();
+        }
+
+        /*
+         * The big ones: these do the actual writing
+         */
+
+        @Override
+        public void write(byte[] buf, int off, int len) {
+            logFile.write(buf, off, len);
+
+            if (!redirectStreams)
+                super.write(buf, off, len);
+        }
+
+        @Override
+        public void write(int b) {
+            logFile.write(b);
+            
+            if (!redirectStreams)
+                super.write(b);
+        }
+
+        @Override
+        public void write(byte[] b) throws IOException {
+            logFile.write(b);
+            
+            if (!redirectStreams)
+                super.write(b);
+        }
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginMessageConsumer.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,119 @@
+/* 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.util.ArrayList;
+import java.util.LinkedList;
+
+import sun.applet.AppletSecurity;
+
+class PluginMessageConsumer {
+
+	int MAX_WORKERS = 30;
+	LinkedList<String> readQueue = new LinkedList<String>();
+	ArrayList<PluginMessageHandlerWorker> workers = new ArrayList<PluginMessageHandlerWorker>();
+	PluginStreamHandler streamHandler = null;
+	AppletSecurity as;
+
+	public PluginMessageConsumer(PluginStreamHandler streamHandler) {
+		
+		as = new AppletSecurity();
+		this.streamHandler = streamHandler;
+
+		// create some workers at the start...
+		for (int i=0; i < 3; i++) {
+			PluginDebug.debug("Creating worker " + i);
+			PluginMessageHandlerWorker worker = new PluginMessageHandlerWorker(streamHandler, i, as);
+			worker.start();
+			workers.add(worker);
+		}
+	}
+
+	public void consume(String message) {
+		
+		PluginDebug.debug("Consumer received message " + message);
+		
+		synchronized(readQueue) {
+			readQueue.add(message);
+		}
+
+		PluginDebug.debug("Message " + message + " added to queue. Looking for free worker...");
+		final PluginMessageHandlerWorker worker = getFreeWorker();
+
+		synchronized(readQueue) {
+			if (readQueue.size() > 0) {
+				worker.setmessage(readQueue.poll());
+			}
+		}
+
+		worker.interrupt();
+	}
+
+	private PluginMessageHandlerWorker getFreeWorker() {
+		
+		// FIXME: Can be made more efficient by having an idle worker pool
+		
+		while (true) {
+			for (PluginMessageHandlerWorker worker: workers) {
+				if (worker.isFree()) {
+					PluginDebug.debug("Found free worker with id " + worker.getWorkerId());
+					// mark it busy before returning
+					worker.busy();
+					return worker;
+				}
+			}
+			
+			// If we have less than MAX_WORKERS, create a new worker
+			if (workers.size() <= MAX_WORKERS) {
+			    PluginDebug.debug("Cannot find free worker, creating worker " + workers.size());
+			    PluginMessageHandlerWorker worker = new PluginMessageHandlerWorker(streamHandler, workers.size(), as);
+			    worker.start();
+			    workers.add(worker);
+			    worker.busy();
+			    return worker;
+			} else {
+			    // else wait
+			}
+
+			Thread.yield();
+		}
+
+		//throw new RuntimeException("Out of message handler workers");
+	}
+	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginMessageHandlerWorker.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,121 @@
+/* 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;
+
+
+class PluginMessageHandlerWorker extends Thread {
+
+	private boolean free = true;
+	private int id;
+	private String message = null;
+	private SecurityManager sm;
+	PluginStreamHandler streamHandler = null;
+
+	public PluginMessageHandlerWorker(PluginStreamHandler streamHandler, int id, SecurityManager sm) {
+		this.id = id;
+		this.streamHandler = streamHandler;
+		this.sm = sm;
+	}
+
+	public void setmessage(String message) {
+		this.message = message;
+	}
+
+	public void run() {
+		while (true) {
+
+			if (message != null) {
+				
+			    PluginDebug.debug("Consumer thread " + id + " consuming " + message);
+			    
+				// ideally, whoever returns things object should mark it 
+				// busy first, but just in case..
+				busy();
+
+				try {
+					streamHandler.handleMessage(message);
+				} catch (PluginException pe) {
+					/*
+					   catch the exception and DO NOTHING. The plugin should take over after 
+					   this error and let the user know. We don't quit because otherwise the 
+					   exception will spread to the rest of the applets which is a no-no
+					 */ 
+				}
+
+				this.message = null;
+				
+				PluginDebug.debug("Consumption completed by consumer thread " + id);
+
+	            // mark ourselves free again
+				free();
+				
+			} else {
+				
+				// Sleep when there is nothing to do
+			    try {
+			        Thread.sleep(Integer.MAX_VALUE);
+			        PluginDebug.debug("Consumer thread " + id + " sleeping...");
+			    } catch (InterruptedException ie) {
+			        PluginDebug.debug("Consumer thread " + id + " woken...");
+			        // nothing.. someone woke us up, see if there 
+			        // is work to do
+			    }
+			}
+		}
+	}
+	
+	
+	
+	public int getWorkerId() {
+		return id;
+	}
+
+	public void busy() {
+		this.free = false;
+	}
+
+	
+	public void free() {
+		this.free = true;
+	}
+	
+	public boolean isFree() {
+		return free;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginObjectStore.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,132 @@
+/* PluginObjectStore -- manage identifier-to-object mapping
+   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.util.*;
+import java.lang.reflect.*;
+import java.io.*;
+
+public class PluginObjectStore
+{
+    private static HashMap<Integer, Object> objects = new HashMap();
+    private static HashMap<Integer, Integer> counts = new HashMap();
+    private static HashMap<Object, Integer> identifiers = new HashMap();
+    // FIXME:
+    //
+    // IF uniqueID == MAX_LONG, uniqueID =
+    // 0 && wrapped = true
+    //
+    // if (wrapped), check if
+    // objects.get(uniqueID) returns null
+    //
+    // if yes, use uniqueID, if no,
+    // uniqueID++ and keep checking
+    // or:
+    // stack of available ids:
+    // derefed id -> derefed id -> nextUniqueIdentifier
+    private static int nextUniqueIdentifier = 1;
+
+    public Object getObject(Integer identifier) {
+        return objects.get(identifier);
+    }
+
+    public Integer getIdentifier(Object object) {
+        if (object == null)
+            return 0;
+        return identifiers.get(object);
+    }
+    
+    public boolean contains(Object object) {
+    	if (object == null)
+    		return identifiers.containsKey(object);
+
+    	return false;
+    }
+    
+    public boolean contains(int identifier) {
+   		return objects.containsKey(identifier);
+    }
+
+    public void reference(Object object) {
+        Integer identifier = identifiers.get(object);
+        if (identifier == null) {
+            objects.put(nextUniqueIdentifier, object);
+            counts.put(nextUniqueIdentifier, 1);
+            identifiers.put(object, nextUniqueIdentifier);
+            //System.out.println("JAVA ADDED: " + nextUniqueIdentifier);
+            //System.out.println("JAVA REFERENCED: " + nextUniqueIdentifier
+            //                   + " to: 1");
+            nextUniqueIdentifier++;
+        } else {
+            counts.put(identifier, counts.get(identifier) + 1);
+            //System.out.println("JAVA REFERENCED: " + identifier +
+            //                   " to: " + counts.get(identifier));
+        }
+    }
+
+    public void unreference(int identifier) {
+        Integer currentCount = counts.get(identifier);
+        if (currentCount == null) {
+            //System.out.println("ERROR UNREFERENCING: " + identifier);
+            return;
+        }
+        if (currentCount == 1) {
+            //System.out.println("JAVA DEREFERENCED: " + identifier
+            //                   + " to: 0");
+            Object object = objects.get(identifier);
+            objects.remove(identifier);
+            counts.remove(identifier);
+            identifiers.remove(object);
+            //System.out.println("JAVA REMOVED: " + identifier);
+        } else {
+            counts.put(identifier, currentCount - 1);
+            //System.out.println("JAVA DEREFERENCED: " +
+            //                   identifier + " to: " +
+            //                   counts.get(identifier));
+        }
+    }
+
+    public void dump() {
+   		Iterator i = objects.keySet().iterator();
+   		while (i.hasNext()) {
+   			Object key = i.next();
+   			PluginDebug.debug(key + "::" +  objects.get(key));
+   		}
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginProxyInfoRequest.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,85 @@
+/* PluginProxyInfoRequest -- Object representing a request for proxy information from the browser
+   Copyright (C) 2009  Red Hat
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+IcedTea is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+package sun.applet;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+
+/**
+ * This class represents a request object for proxy information for a given URI
+ */
+
+public class PluginProxyInfoRequest extends PluginCallRequest {
+    
+    URI internal = null;
+
+    public PluginProxyInfoRequest(String message, String returnString) {
+        super(message, returnString);
+    }
+    
+    public void parseReturn(String proxyInfo) {
+
+        // try to parse the proxy information. If things go wrong, do nothing .. 
+        // this will keep internal = null which forces a direct connection
+
+    	PluginDebug.debug ("PluginProxyInfoRequest GOT: " + proxyInfo);
+    	String[] messageComponents = proxyInfo.split(" ");
+
+    	try {
+    	    internal = new URI(messageComponents[2], null, messageComponents[3], Integer.parseInt(messageComponents[4]), null, null, null);
+    	} catch (Exception e) {
+    	    // do nothing
+    	}
+
+        setDone(true);
+    }
+
+    /**
+     * Returns whether the given message is serviceable by this object
+     * 
+     * @param message The message to service
+     * @return boolean indicating if message is serviceable
+     */
+    public boolean serviceable(String message) {
+    	return message.startsWith("plugin PluginProxyInfo");
+    }
+
+    public URI getObject() {
+    	return this.internal;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginProxySelector.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,195 @@
+/* PluginProxySelector -- proxy selector for all connections from applets and 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.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.Date;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Proxy selector implementation for plugin network functions.
+ * 
+ * This class fetches proxy information from the web browser and 
+ * uses that information in the context of all network connection 
+ * (plugin specific and applet connections) as applicable
+ * 
+ */
+
+public class PluginProxySelector extends ProxySelector {
+
+    private TimedHashMap<String, Proxy> proxyCache = new TimedHashMap<String, Proxy>(); 
+
+
+    @Override
+    public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+        // If the connection fails, there is little we can do here. Just print the exception
+        ioe.printStackTrace();
+    }
+
+    /**
+     * Selects the appropriate proxy (or DIRECT connection method) for the given URI
+     * 
+     * @param uri The URI being accessed
+     * @return A list of Proxy objects that are usable for this URI
+     */
+    @Override
+    public List<Proxy> select(URI uri) {
+
+        List<Proxy> proxyList = new ArrayList<Proxy>();
+
+        // check cache first
+        Proxy cachedProxy = checkCache(uri);
+        if (cachedProxy != null) {
+            proxyList.add(cachedProxy);
+            return proxyList;
+        }
+
+        // Nothing usable in cache. Fetch info from browser
+        Proxy proxy = Proxy.NO_PROXY;
+        Object o = PluginAppletViewer.requestPluginProxyInfo(uri);
+
+        // If the browser returned anything, try to parse it. If anything in the try block fails, the fallback is direct connection
+        try {
+            if (o != null) {
+                PluginDebug.debug("Proxy URI = " + o);
+                URI proxyURI = (URI) o;
+                
+                // If origin uri is http/ftp, we're good. If origin uri is not that, the proxy _must_ be socks, else we fallback to direct
+                if (uri.getScheme().startsWith("http") || uri.getScheme().equals("ftp") || proxyURI.getScheme().startsWith("socks")) {
+
+                    Proxy.Type type = proxyURI.getScheme().equals("http") ? Proxy.Type.HTTP : Proxy.Type.SOCKS; 
+                    InetSocketAddress socketAddr = new InetSocketAddress(proxyURI.getHost(), proxyURI.getPort());
+
+                    proxy = new Proxy(type, socketAddr);
+
+                    String uriKey = uri.getScheme() + "://" + uri.getHost();
+                    proxyCache.put(uriKey, proxy);
+                } else {
+                    PluginDebug.debug("Proxy " + proxyURI + " cannot be used for " + uri + ". Falling back to DIRECT");
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        proxyList.add(proxy);
+
+        PluginDebug.debug("Proxy for " + uri.toString() + " is " + proxy);
+
+        return proxyList;
+    }
+
+    /** 
+     * Checks to see if proxy information is already cached. 
+     * 
+     * @param uri The URI to check
+     * @return The cached Proxy. null if there is no suitable cached proxy. 
+     */
+    private Proxy checkCache(URI uri) {
+        
+        String uriKey = uri.getScheme() + "://" + uri.getHost();
+        if (proxyCache.get(uriKey) != null) {
+            return proxyCache.get(uriKey);
+        }
+
+        return null;
+    }
+    
+    /**
+     * Simple utility class that extends HashMap by adding an expiry to the entries.
+     * 
+     * This map stores entries, and returns them only if the entries were last accessed within time t=10 seconds
+     *
+     * @param <K> The key type
+     * @param <V> The Object type
+     */
+
+    private class TimedHashMap<K,V> extends HashMap<K,V> {
+
+        HashMap<K, Long> timeStamps = new HashMap<K, Long>();
+        Long expiry = 10000L;
+        
+        /**
+         * Store the item in the map and associate a timestamp with it
+         * 
+         * @param key The key
+         * @param value The value to store
+         */
+        public V put(K key, V value) {
+            timeStamps.put(key, new Date().getTime());
+            return super.put(key, value);
+        }
+
+        /**
+         * Return cached item if it has not already expired.
+         * 
+         * Before returning, this method also resets the "last accessed" 
+         * time for this entry, so it is good for another 10 seconds
+         * 
+         * @param key The key
+         */
+        public V get(Object key) {
+
+            Long now = new Date().getTime();
+
+            if (super.containsKey(key)) {
+                Long age = now - timeStamps.get(key);
+
+                // Item exists. If it has not expired, renew its access time and return it 
+                if (age <= expiry) {
+                    PluginDebug.debug("Returning proxy " + super.get(key) + " from cache for " + key);
+                    timeStamps.put((K) key, (new Date()).getTime());
+                    return super.get(key);
+                } else {
+                    PluginDebug.debug("Proxy cache for " + key + " has expired (age=" + age/1000.0 + " seconds)");
+                }
+            } 
+
+            return null;
+        }
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/PluginStreamHandler.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,479 @@
+/* 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.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StreamTokenizer;
+import java.net.MalformedURLException;
+import java.nio.charset.Charset;
+import java.util.Date;
+import java.util.LinkedList;
+
+import javax.swing.SwingUtilities;
+
+
+public class PluginStreamHandler {
+
+    private BufferedReader pluginInputReader;
+    private StreamTokenizer pluginInputTokenizer;
+    private BufferedWriter pluginOutputWriter;
+    
+    private RequestQueue queue = new RequestQueue();
+    
+    private JavaConsole console = new JavaConsole();
+
+	LinkedList<String> writeQueue = new LinkedList<String>();
+
+	PluginMessageConsumer consumer;
+	Boolean shuttingDown = false;
+	
+	PluginAppletViewer pav;
+	
+	static Date d = new Date();
+	static long startTime = d.getTime();
+	static long totalWait = 0;
+	
+    public PluginStreamHandler(InputStream inputstream, OutputStream outputstream)
+    throws MalformedURLException, IOException
+    {
+
+    	PluginDebug.debug("Current context CL=" + Thread.currentThread().getContextClassLoader());
+    	try {
+			pav = (PluginAppletViewer) ClassLoader.getSystemClassLoader().loadClass("sun.applet.PluginAppletViewer").newInstance();
+			PluginDebug.debug("Loaded: " + pav + " CL=" + pav.getClass().getClassLoader());
+		} catch (InstantiationException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (IllegalAccessException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (ClassNotFoundException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+
+    	PluginDebug.debug("Creating consumer...");
+    	consumer = new PluginMessageConsumer(this);
+
+    	// Set up input and output pipes.  Use UTF-8 encoding.
+    	pluginInputReader =
+    		new BufferedReader(new InputStreamReader(inputstream,
+    				Charset.forName("UTF-8")));
+    	/*pluginInputTokenizer = new StreamTokenizer(pluginInputReader);
+    	pluginInputTokenizer.resetSyntax();
+    	pluginInputTokenizer.whitespaceChars('\u0000', '\u0000');
+    	pluginInputTokenizer.wordChars('\u0001', '\u00FF');*/
+    	pluginOutputWriter =
+    		new BufferedWriter(new OutputStreamWriter
+    				(outputstream, Charset.forName("UTF-8")));
+
+    	/*
+	while(true) {
+            String message = read();
+            PluginDebug.debug(message);
+            handleMessage(message);
+            // TODO:
+            // write(queue.peek());
+	}
+    	 */
+    }
+
+    public void startProcessing() {
+
+    	Thread listenerThread = new Thread() {
+
+    		public void run() {
+    			
+    			while (true) {
+
+    				PluginDebug.debug("Waiting for data...");
+    				
+    	    		long b4 = new Date().getTime();
+
+    				String s = read();
+                    
+    	    		long after = new Date().getTime();
+
+    	    		totalWait += (after - b4);
+    				//System.err.println("Total wait time: " + totalWait);
+
+    				if (s != null) {
+    					consumer.consume(s);
+    				} else {
+    					try {
+    						// Close input/output channels to plugin.
+    						pluginInputReader.close();
+    						pluginOutputWriter.close();
+    					} catch (IOException exception) {
+    						// Deliberately ignore IOException caused by broken
+    						// pipe since plugin may have already detached.
+    					}
+    					AppletSecurityContextManager.dumpStore(0);
+    					PluginDebug.debug("APPLETVIEWER: exiting appletviewer");
+    					System.exit(0);
+    				}
+    				
+/*    				
+    				int readChar = -1;
+    				// blocking read, discard first character
+    				try {
+    					readChar = pluginInputReader.read();
+    				} catch (IOException ioe) {
+    					// plugin may have detached
+    				}
+
+    				// if not disconnected
+    				if (readChar != -1) {
+    					String s = read();
+    					PluginDebug.debug("Got data, consuming " + s);
+    					consumer.consume(s);
+    				} else {
+    					try {
+    						// Close input/output channels to plugin.
+    						pluginInputReader.close();
+    						pluginOutputWriter.close();
+    					} catch (IOException exception) {
+    						// Deliberately ignore IOException caused by broken
+    						// pipe since plugin may have already detached.
+    					}
+    					AppletSecurityContextManager.dumpStore(0);
+    					PluginDebug.debug("APPLETVIEWER: exiting appletviewer");
+    					System.exit(0);
+    				}
+*/
+    			}
+    		}
+    	};
+    	
+    	listenerThread.start();
+    }
+    
+    public void postMessage(String s) {
+
+    	if (s == null || s.equals("shutdown")) {
+    	    try {
+    		// Close input/output channels to plugin.
+    		pluginInputReader.close();
+    		pluginOutputWriter.close();
+    	    } catch (IOException exception) {
+    		// Deliberately ignore IOException caused by broken
+    		// pipe since plugin may have already detached.
+    	    }
+    	    AppletSecurityContextManager.dumpStore(0);
+    	    PluginDebug.debug("APPLETVIEWER: exiting appletviewer");
+    	    System.exit(0);
+    	}
+
+   		//PluginAppletSecurityContext.contexts.get(0).store.dump();
+   		PluginDebug.debug("Plugin posted: " + s);
+
+		PluginDebug.debug("Consuming " + s);
+		consumer.consume(s);
+
+   		PluginDebug.debug("Added to queue");
+    }
+    
+    public void handleMessage(String message) throws PluginException {
+
+    	int nextIndex = 0;
+    	int reference = -1;
+    	String src = null;
+    	String[] privileges = null;
+    	String rest = "";
+
+    	String[] msgComponents = message.split(" ");
+    
+		if (msgComponents.length < 2)
+			return;
+
+        if (msgComponents[0].startsWith("plugin")) {
+            handlePluginMessage(message);
+            return;
+        }
+
+    	// type and identifier are guaranteed to be there
+    	String type = msgComponents[0];
+    	final int identifier = Integer.parseInt(msgComponents[1]);
+    	nextIndex = 2;
+    	
+    	// reference, src and privileges are optional components, 
+    	// and are guaranteed to be in that order, if they occur
+
+    	// is there a reference ?
+    	if (msgComponents[nextIndex].equals("reference")) {
+    		reference = Integer.parseInt(msgComponents[nextIndex+1]);
+    		nextIndex += 2;
+    	}
+
+    	// is there a src?
+    	if (msgComponents[nextIndex].equals("src")) {
+    		src = msgComponents[nextIndex+1];
+    		nextIndex += 2;
+    	}
+
+    	// is there a privileges?
+    	if (msgComponents[nextIndex].equals("privileges")) {
+    		String privs = msgComponents[nextIndex+1];
+    		privileges = privs.split(",");
+    		nextIndex += 2;
+    	}
+    	
+    	// rest
+    	for (int i=nextIndex; i < msgComponents.length; i++) {
+    		rest += msgComponents[i];
+    		rest += " ";
+    	}
+
+    	rest = rest.trim();
+
+    	try {
+
+    		PluginDebug.debug("Breakdown -- type: " + type + " identifier: " + identifier + " reference: " + reference + " src: " + src + " privileges: " + privileges + " rest: \"" + rest + "\"");
+
+    		if (rest.contains("JavaScriptGetWindow")
+    				|| rest.contains("JavaScriptGetMember")
+    				|| rest.contains("JavaScriptSetMember")
+    				|| rest.contains("JavaScriptGetSlot")
+    				|| rest.contains("JavaScriptSetSlot")
+    				|| rest.contains("JavaScriptEval")
+    				|| rest.contains("JavaScriptRemoveMember")
+    				|| rest.contains("JavaScriptCall")
+    				|| rest.contains("JavaScriptFinalize")
+    				|| rest.contains("JavaScriptToString")) {
+    			
+				finishCallRequest(rest);
+    			return;
+    		}
+
+    		final int freference = reference;
+    		final String frest = rest;
+
+    		if (type.equals("instance")) {
+    			PluginAppletViewer.handleMessage(identifier, freference,frest);
+    		} else if (type.equals("context")) {
+    			PluginDebug.debug("Sending to PASC: " + identifier + "/" + reference + " and " + rest);
+    			AppletSecurityContextManager.handleMessage(identifier, reference, src, privileges, rest);
+    		}
+    	} catch (Exception e) {
+    		throw new PluginException(this, identifier, reference, e);
+    	}
+    }
+
+    private void handlePluginMessage(String message) {
+        if (message.equals("plugin showconsole")) {
+            showConsole();
+        } else if (message.equals("plugin hideconsole")) {
+            hideConsole();            
+        } else {
+            // else this is something that was specifically requested
+            finishCallRequest(message);
+        }
+    }
+
+    public void postCallRequest(PluginCallRequest request) {
+        synchronized(queue) {
+   			queue.post(request);
+        }
+    }
+
+    private void finishCallRequest(String message) {
+    	PluginDebug.debug ("DISPATCHCALLREQUESTS 1");
+    	synchronized(queue) {
+    		PluginDebug.debug ("DISPATCHCALLREQUESTS 2");
+    		PluginCallRequest request = queue.pop();
+
+    		// make sure we give the message to the right request 
+    		// in the queue.. for the love of God, MAKE SURE!
+
+    		// first let's be efficient.. if there was only one 
+    		// request in queue, we're already set
+    		if (queue.size() != 0) {
+
+    			int size = queue.size();
+    			int count = 0;
+
+    			while (!request.serviceable(message)) {
+
+     				PluginDebug.debug(request + " cannot service " + message);
+    			    
+    				// something is very wrong.. we have a message to 
+    				// process, but no one to service it
+    				if (count >= size) {
+    					throw new RuntimeException("Unable to find processor for message " + message);
+    				}
+
+    				// post request at the end of the queue
+    				queue.post(request);
+
+    				// Look at the next request
+    				request = queue.pop();
+
+    				count++;
+    			}
+
+    		}
+
+    		PluginDebug.debug ("DISPATCHCALLREQUESTS 3");
+    		if (request != null) {
+    			PluginDebug.debug ("DISPATCHCALLREQUESTS 5");
+    			synchronized(request) {
+    				request.parseReturn(message);
+    				request.notifyAll();
+    			}
+    			PluginDebug.debug ("DISPATCHCALLREQUESTS 6");
+    			PluginDebug.debug ("DISPATCHCALLREQUESTS 7");
+    		}
+    	}
+    	PluginDebug.debug ("DISPATCHCALLREQUESTS 8");
+    }
+
+    /**
+     * Read string from plugin.
+     *
+     * @return the read string
+     *
+     * @exception IOException if an error occurs
+     */
+    private String read()
+    {
+    	String message = null;
+
+    	try {
+    		message = pluginInputReader.readLine();
+    		PluginDebug.debug("  PIPE: appletviewer read: " + message);
+
+            if (message == null || message.equals("shutdown")) {
+                synchronized(shuttingDown) {
+                    shuttingDown = true;
+                }
+                try {
+                    // Close input/output channels to plugin.
+                    pluginInputReader.close();
+                    pluginOutputWriter.close();
+                } catch (IOException exception) {
+                    // Deliberately ignore IOException caused by broken
+                    // pipe since plugin may have already detached.
+                }
+                AppletSecurityContextManager.dumpStore(0);
+                PluginDebug.debug("APPLETVIEWER: exiting appletviewer");
+                System.exit(0);
+            }
+    	} catch (IOException e) {
+    	       e.printStackTrace();
+    	}
+
+    	return message;
+    }
+    
+    /**
+     * Write string to plugin.
+     * 
+     * @param message the message to write
+     *
+     * @exception IOException if an error occurs
+     */
+    public void write(String message)
+    {
+
+    	PluginDebug.debug("  PIPE: appletviewer wrote: " + message);
+        synchronized(pluginOutputWriter) {
+        	try {
+        		pluginOutputWriter.write(message + "\n", 0, message.length());
+        		pluginOutputWriter.write(0);
+        		pluginOutputWriter.flush();
+        	} catch (IOException e) {
+        		// if we are shutting down, ignore write failures as 
+        		// pipe may have closed
+        		synchronized(shuttingDown) {
+        			if (!shuttingDown) {
+        				e.printStackTrace();
+        			}
+        		}
+
+        		// either ways, if the pipe is broken, there is nothing 
+        		// we can do anymore. Don't hang around.
+        		PluginDebug.debug("Unable to write to PIPE. APPLETVIEWER exiting");        		
+        		System.exit(1);
+        	}
+		}
+
+		return;
+    /*	
+    	synchronized(writeQueue) {
+            writeQueue.add(message);
+            PluginDebug.debug("  PIPE: appletviewer wrote: " + message);
+    	}
+	*/
+
+    }
+
+    public boolean messageAvailable() {
+    	return writeQueue.size() != 0;
+    }
+
+    public String getMessage() {
+    	synchronized(writeQueue) {
+			String ret = writeQueue.size() > 0 ? writeQueue.poll() : "";
+    		return ret;
+    	}
+    }
+    
+    private void showConsole() {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                console.showConsole();
+            }
+        });
+    }
+    
+    private void hideConsole() {
+        SwingUtilities.invokeLater(new Runnable() {
+            public void run() {
+                console.hideConsole();
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/RequestQueue.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,77 @@
+/* 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;
+
+
+public class RequestQueue {
+    PluginCallRequest head = null;
+    PluginCallRequest tail = null;
+    private int size = 0;
+
+    public void post(PluginCallRequest request) {
+    	PluginDebug.debug("Securitymanager=" + System.getSecurityManager());
+        if (head == null) {
+            head = tail = request;
+            tail.setNext(null);
+        } else {
+            tail.setNext(request);
+            tail = request;
+            tail.setNext(null);
+        }
+        
+        size++;
+    }
+
+    public PluginCallRequest pop() {
+        if (head == null)
+            return null;
+
+        PluginCallRequest ret = head;
+        head = head.getNext();
+        ret.setNext(null);
+
+        size--;
+        
+        return ret;
+    }
+    
+    public int size() {
+    	return size;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/TestEnv.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,172 @@
+/* TestEnv -- test JavaScript-to-Java calls
+   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 TestEnv
+{
+    public static int intField = 103;
+    public int intInstanceField = 7822;
+    public String stringField = "hello";
+    // z <musical G clef> <chinese water>
+    public String complexStringField = "z\uD834\uDD1E\u6C34";
+
+    public static void TestIt() {
+        PluginDebug.debug("TestIt");
+    }
+
+    public static void TestItBool(boolean arg) {
+        PluginDebug.debug("TestItBool: " + arg);
+    }
+
+    public static void TestItByte(byte arg) {
+        PluginDebug.debug("TestItByte: " + arg);
+    }
+
+    public static void TestItChar(char arg) {
+        PluginDebug.debug("TestItChar: " + arg);
+    }
+
+    public static void TestItShort(short arg) {
+        PluginDebug.debug("TestItShort: " + arg);
+    }
+
+    public static void TestItInt(int arg) {
+        PluginDebug.debug("TestItInt: " + arg);
+    }
+
+    public static void TestItLong(long arg) {
+        PluginDebug.debug("TestItLong: " + arg);
+    }
+
+    public static void TestItFloat(float arg) {
+        PluginDebug.debug("TestItFloat: " + arg);
+    }
+
+    public static void TestItDouble(double arg) {
+        PluginDebug.debug("TestItDouble: " + arg);
+    }
+
+    public static void TestItObject(TestEnv arg) {
+        PluginDebug.debug("TestItObject: " + arg);
+    }
+
+    public static void TestItObjectString(String arg) {
+        PluginDebug.debug("TestItObjectString: " + arg);
+    }
+
+    public static void TestItIntArray(int[] arg) {
+        PluginDebug.debug("TestItIntArray: " + arg);
+        for (int i = 0; i < arg.length; i++)
+            PluginDebug.debug ("ELEMENT: " + i + " " + arg[i]);
+    }
+
+    public static void TestItObjectArray(String[] arg) {
+        PluginDebug.debug("TestItObjectArray: " + arg);
+        for (int i = 0; i < arg.length; i++)
+            PluginDebug.debug ("ELEMENT: " + i + " " + arg[i]);
+    }
+
+    public static void TestItObjectArrayMulti(String[][] arg) {
+        PluginDebug.debug("TestItObjectArrayMulti: " + arg);
+        for (int i = 0; i < arg.length; i++)
+            for (int j = 0; j < arg[i].length; j++)
+                PluginDebug.debug ("ELEMENT: " + i + " " + j + " " + arg[i][j]);
+    }
+
+    public static boolean TestItBoolReturnTrue() {
+        return true;
+    }
+
+    public static boolean TestItBoolReturnFalse() {
+        return false;
+    }
+
+    public static byte TestItByteReturn() {
+        return (byte) 0xfe;
+    }
+
+    public static char TestItCharReturn() {
+        return 'K';
+    }
+
+    public static char TestItCharUnicodeReturn() {
+        return '\u6C34';
+    }
+
+    public static short TestItShortReturn() {
+        return 23;
+    }
+
+    public static int TestItIntReturn() {
+        return 3445;
+    }
+
+    public static long TestItLongReturn() {
+        return 3242883;
+    }
+
+    public static float TestItFloatReturn() {
+        return 9.21E4f;
+    }
+
+    public static double TestItDoubleReturn() {
+        return 8.33E88;
+    }
+
+    public static Object TestItObjectReturn() {
+        return new String("Thomas");
+    }
+
+    public static int[] TestItIntArrayReturn() {
+        return new int[] { 6, 7, 8 };
+    }
+
+    public static String[] TestItObjectArrayReturn() {
+        return new String[] { "Thomas", "Brigitte" };
+    }
+
+    public static String[][] TestItObjectArrayMultiReturn() {
+        return new String[][] { {"Thomas", "Brigitte"},
+                                {"Lindsay", "Michael"} };
+    }
+
+    public int TestItIntInstance(int arg) {
+        PluginDebug.debug("TestItIntInstance: " + this + " " + arg);
+        return 899;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/icedteanp/sun/applet/VoidPluginCallRequest.java	Wed May 20 21:30:24 2009 +0100
@@ -0,0 +1,67 @@
+/* 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;
+
+
+public class VoidPluginCallRequest extends PluginCallRequest {
+    public VoidPluginCallRequest(String message, String returnString) {
+        super(message, returnString);
+        PluginDebug.debug ("VoidPLUGINCAlL " + message + " " + returnString);
+    }
+
+    public void parseReturn(String message) {
+    	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("JavaScriptFinalize") ||
+    			message.contains("JavaScriptRemoveMember") ||
+    			message.contains("JavaScriptSetMember") ||
+    			message.contains("JavaScriptSetSlot");
+    }
+    
+    public Object getObject() {
+    	return null;
+    }
+}