1 /* gcjwebplugin.cc -- web browser plugin to execute Java applets
2 Copyright (C) 2003, 2004, 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
43 #include <sys/types.h>
46 // Netscape plugin API includes.
52 #include <glib/gstdio.h>
57 // gcjwebplugin includes.
60 // Documentbase retrieval includes.
61 #include <nsIPluginInstance.h>
62 #include <nsIPluginInstancePeer.h>
63 #include <nsIPluginTagInfo2.h>
66 #define PLUGIN_DEBUG(message) \
67 g_print ("GCJ PLUGIN: thread %p: %s\n", g_thread_self (), message)
69 #define PLUGIN_DEBUG_TWO(first, second) \
70 g_print ("GCJ PLUGIN: thread %p: %s %s\n", g_thread_self (), \
73 // Error reporting macros.
74 #define PLUGIN_ERROR(message) \
75 g_printerr ("%s:%d: thread %p: Error: %s\n", __FILE__, __LINE__, \
76 g_thread_self (), message)
78 #define PLUGIN_ERROR_TWO(first, second) \
79 g_printerr ("%s:%d: thread %p: Error: %s: %s\n", __FILE__, __LINE__, \
80 g_thread_self (), first, second)
82 #define PLUGIN_ERROR_THREE(first, second, third) \
83 g_printerr ("%s:%d: thread %p: Error: %s: %s: %s\n", __FILE__, \
84 __LINE__, g_thread_self (), first, second, third)
86 // Plugin information passed to about:plugins.
87 #define PLUGIN_NAME "GCJ Web Browser Plugin"
88 #define PLUGIN_DESC "The " PLUGIN_NAME " executes Java applets."
89 #define PLUGIN_MIME_DESC \
90 "application/x-java-vm:class,jar:GCJ;" \
91 "application/x-java-applet:class,jar:GCJ;" \
92 "application/x-java-applet;version=1.1:class,jar:GCJ;" \
93 "application/x-java-applet;version=1.1.1:class,jar:GCJ;" \
94 "application/x-java-applet;version=1.1.2:class,jar:GCJ;" \
95 "application/x-java-applet;version=1.1.3:class,jar:GCJ;" \
96 "application/x-java-applet;version=1.2:class,jar:GCJ;" \
97 "application/x-java-applet;version=1.2.1:class,jar:GCJ;" \
98 "application/x-java-applet;version=1.2.2:class,jar:GCJ;" \
99 "application/x-java-applet;version=1.3:class,jar:GCJ;" \
100 "application/x-java-applet;version=1.3.1:class,jar:GCJ;" \
101 "application/x-java-applet;version=1.4:class,jar:GCJ;" \
102 "application/x-java-applet;version=1.4.1:class,jar:GCJ;" \
103 "application/x-java-applet;version=1.4.2:class,jar:GCJ;" \
104 "application/x-java-applet;jpi-version=1.4.2_01:class,jar:GCJ;" \
105 "application/x-java-bean:class,jar:GCJ;" \
106 "application/x-java-bean;version=1.1:class,jar:GCJ;" \
107 "application/x-java-bean;version=1.1.1:class,jar:GCJ;" \
108 "application/x-java-bean;version=1.1.2:class,jar:GCJ;" \
109 "application/x-java-bean;version=1.1.3:class,jar:GCJ;" \
110 "application/x-java-bean;version=1.2:class,jar:GCJ;" \
111 "application/x-java-bean;version=1.2.1:class,jar:GCJ;" \
112 "application/x-java-bean;version=1.2.2:class,jar:GCJ;" \
113 "application/x-java-bean;version=1.3:class,jar:GCJ;" \
114 "application/x-java-bean;version=1.3.1:class,jar:GCJ;" \
115 "application/x-java-bean;version=1.4:class,jar:GCJ;" \
116 "application/x-java-bean;version=1.4.1:class,jar:GCJ;" \
117 "application/x-java-bean;version=1.4.2:class,jar:GCJ;" \
118 "application/x-java-bean;jpi-version=1.4.2_01:class,jar:GCJ;"
119 #define PLUGIN_URL NS_INLINE_PLUGIN_CONTRACTID_PREFIX NS_JVM_MIME_TYPE
120 #define PLUGIN_MIME_TYPE "application/x-java-vm"
121 #define PLUGIN_FILE_EXTS "class,jar,zip"
122 #define PLUGIN_MIME_COUNT 1
124 // Security dialog messages.
125 #define RESPONSE_TRUST_APPLET "Trust Applet"
126 #define RESPONSE_TRUST_APPLET_ADD_TO_LIST "Trust Applet and Add to Whitelist"
127 #define SECURITY_WARNING \
128 "%s wants to load an applet.\n" \
129 "GNU Classpath's security implementation is not complete.\n" \
130 "HOSTILE APPLETS WILL STEAL AND/OR DESTROY YOUR DATA!\n"
131 #define SECURITY_DESCRIPTION \
132 "Click \"Cancel\" if you do not trust the source of this applet.\n" \
133 "Click \"Trust Applet\" to load and run this applet now.\n" \
134 "Click \"Trust Applet and Add To Whitelist\" to always load" \
135 " and run this applet from now on, without asking.\n" \
136 "The whitelist is a list of the URLs from which you trust" \
138 "Your whitelist file is \" %s \"."
139 #define FAILURE_MESSAGE \
140 "This page wants to load an applet.\n" \
141 "The appletviewer is missing or not installed properly in \"" \
142 APPLETVIEWER_EXECUTABLE "\"."
144 // Documentbase retrieval required definition.
145 static NS_DEFINE_IID (kIPluginTagInfo2IID
, NS_IPLUGINTAGINFO2_IID
);
147 // Browser function table.
148 static NPNetscapeFuncs browserFunctions
;
150 // Data directory for plugin.
151 static gchar
* data_directory
;
153 // Whitelist filename
154 static gchar
* whitelist_filename
;
156 // Keeps track of initialization. NP_Initialize should only be
158 gboolean initialized
= false;
160 // GCJPluginData stores all the data associated with a single plugin
161 // instance. A separate plugin instance is created for each <APPLET>
162 // tag. For now, each plugin instance spawns its own applet viewer
163 // process but this may need to change if we find pages containing
164 // multiple applets that expect to be running in the same VM.
167 // A unique identifier for this plugin window.
168 gchar
* instance_string
;
169 // Applet viewer input pipe name.
171 // Applet viewer input channel.
172 GIOChannel
* in_from_appletviewer
;
173 // Applet viewer input watch source.
174 gint in_watch_source
;
175 // Applet viewer output pipe name.
176 gchar
* out_pipe_name
;
177 // Applet viewer output channel.
178 GIOChannel
* out_to_appletviewer
;
179 // Applet viewer output watch source.
180 gint out_watch_source
;
181 // Mutex to protect appletviewer_alive.
182 GMutex
* appletviewer_mutex
;
183 // Back-pointer to the plugin instance to which this data belongs.
184 // This should not be freed but instead simply set to NULL.
186 // FALSE if the applet viewer process has died. All code
187 // communicating with the applet viewer should check this flag
188 // before attempting to read from/write to the applet viewer pipes.
189 gboolean appletviewer_alive
;
190 // The address of the plugin window. This should not be freed but
191 // instead simply set to NULL.
192 gpointer window_handle
;
193 // The last plugin window width sent to us by the browser.
194 guint32 window_width
;
195 // The last plugin window height sent to us by the browser.
196 guint32 window_height
;
199 // Documentbase retrieval type-punning union.
203 nsIPluginTagInfo2
** info_field
;
206 // Static instance helper functions.
207 // Have the browser allocate a new GCJPluginData structure.
208 static void plugin_data_new (GCJPluginData
** data
);
209 // Documentbase retrieval.
210 static gchar
* plugin_get_documentbase (NPP instance
);
211 // plugin failure handling.
212 static bool plugin_failed ();
213 // Whitelist handling.
214 static bool plugin_user_trusts_documentbase (char* documentbase
);
215 static bool plugin_ask_user_about_documentbase (char* documentbase
);
216 static void plugin_add_documentbase_to_whitelist (char* documentbase
);
217 // Callback used to monitor input pipe status.
218 static gboolean
plugin_in_pipe_callback (GIOChannel
* source
,
219 GIOCondition condition
,
220 gpointer plugin_data
);
221 // Callback used to monitor output pipe status.
222 static gboolean
plugin_out_pipe_callback (GIOChannel
* source
,
223 GIOCondition condition
,
224 gpointer plugin_data
);
225 static NPError
plugin_start_appletviewer (GCJPluginData
* data
);
226 static gchar
* plugin_create_applet_tag (int16 argc
, char* argn
[],
228 static void plugin_send_message_to_appletviewer (GCJPluginData
* data
,
229 gchar
const* message
);
230 static void plugin_stop_appletviewer (GCJPluginData
* data
);
231 // Uninitialize GCJPluginData structure and delete pipes.
232 static void plugin_data_destroy (GCJPluginData
** data
);
234 // Global instance counter.
235 // Mutex to protect plugin_instance_counter.
236 static GMutex
* plugin_instance_mutex
= NULL
;
237 // A counter used to create uniquely named pipes.
238 static gulong plugin_instance_counter
= 0;
239 // The user's documentbase whitelist.
240 static GIOChannel
* whitelist_file
= NULL
;
241 // A global variable for reporting GLib errors. This must be free'd
242 // and set to NULL after each use.
243 static GError
* channel_error
= NULL
;
245 // Functions prefixed by GCJ_ are instance functions. They are called
246 // by the browser and operate on instances of GCJPluginData.
247 // Functions prefixed by plugin_ are static helper functions.
248 // Functions prefixed by NP_ are factory functions. They are called
249 // by the browser and provide functionality needed to create plugin
252 // INSTANCE FUNCTIONS
254 // Creates a new gcjwebplugin instance. This function creates a
255 // GCJPluginData* and stores it in instance->pdata. The following
256 // GCJPluginData fiels are initialized: instance_string, in_pipe_name,
257 // in_from_appletviewer, in_watch_source, out_pipe_name,
258 // out_to_appletviewer, out_watch_source, appletviewer_mutex, owner,
259 // appletviewer_alive. In addition two pipe files are created. All
260 // of those fields must be properly destroyed, and the pipes deleted,
261 // by GCJ_Destroy. If an error occurs during initialization then this
262 // function will free anything that's been allocated so far, set
263 // instance->pdata to NULL and return an error code.
265 GCJ_New (NPMIMEType pluginType
, NPP instance
, uint16 mode
,
266 int16 argc
, char* argn
[], char* argv
[],
269 PLUGIN_DEBUG ("GCJ_New");
271 NPError np_error
= NPERR_NO_ERROR
;
272 GCJPluginData
* data
= NULL
;
274 gchar
* documentbase
= NULL
;
275 gchar
* read_message
= NULL
;
276 gchar
* applet_tag
= NULL
;
277 gchar
* tag_message
= NULL
;
281 PLUGIN_ERROR ("Browser-provided instance pointer is NULL.");
282 np_error
= NPERR_INVALID_INSTANCE_ERROR
;
287 plugin_data_new (&data
);
290 PLUGIN_ERROR ("Failed to allocate plugin data.");
291 np_error
= NPERR_OUT_OF_MEMORY_ERROR
;
295 // Initialize data->instance_string.
297 // instance_string should be unique for this process so we use a
298 // combination of getpid and plugin_instance_counter.
300 // Critical region. Reference and increment plugin_instance_counter
302 g_mutex_lock (plugin_instance_mutex
);
304 // data->instance_string
305 data
->instance_string
= g_strdup_printf ("instance-%d-%ld",
307 plugin_instance_counter
++);
309 g_mutex_unlock (plugin_instance_mutex
);
311 // data->appletviewer_mutex
312 data
->appletviewer_mutex
= g_mutex_new ();
314 // Documentbase retrieval.
315 documentbase
= plugin_get_documentbase (instance
);
318 PLUGIN_ERROR ("Documentbase retrieval failed."
319 " Browser not Mozilla-based?");
320 goto cleanup_appletviewer_mutex
;
323 // Open the user's documentbase whitelist.
324 whitelist_file
= g_io_channel_new_file (whitelist_filename
,
325 "a+", &channel_error
);
330 PLUGIN_ERROR_THREE ("Failed to open whitelist file",
332 channel_error
->message
);
333 g_error_free (channel_error
);
334 channel_error
= NULL
;
337 PLUGIN_ERROR_TWO ("Failed to open whitelist file",
340 return NPERR_GENERIC_ERROR
;
343 if (!plugin_user_trusts_documentbase (documentbase
))
345 PLUGIN_ERROR ("User does not trust applet.");
346 np_error
= NPERR_GENERIC_ERROR
;
347 goto cleanup_appletviewer_mutex
;
350 // Create appletviewer-to-plugin pipe which we refer to as the input
353 // data->in_pipe_name
354 data
->in_pipe_name
= g_strdup_printf ("%s/gcj-%s-appletviewer-to-plugin",
355 data_directory
, data
->instance_string
);
356 if (!data
->in_pipe_name
)
358 PLUGIN_ERROR ("Failed to create input pipe name.");
359 np_error
= NPERR_OUT_OF_MEMORY_ERROR
;
360 // If data->in_pipe_name is NULL then the g_free at
361 // cleanup_in_pipe_name will simply return.
362 goto cleanup_in_pipe_name
;
365 if (mkfifo (data
->in_pipe_name
, 0700) == -1 && errno
!= EEXIST
)
367 PLUGIN_ERROR_TWO ("Failed to create input pipe", strerror (errno
));
368 np_error
= NPERR_GENERIC_ERROR
;
369 goto cleanup_in_pipe_name
;
372 // Create plugin-to-appletviewer pipe which we refer to as the
375 // data->out_pipe_name
376 data
->out_pipe_name
= g_strdup_printf ("%s/gcj-%s-plugin-to-appletviewer",
377 data_directory
, data
->instance_string
);
379 if (!data
->out_pipe_name
)
381 PLUGIN_ERROR ("Failed to create output pipe name.");
382 np_error
= NPERR_OUT_OF_MEMORY_ERROR
;
383 goto cleanup_out_pipe_name
;
386 if (mkfifo (data
->out_pipe_name
, 0700) == -1 && errno
!= EEXIST
)
388 PLUGIN_ERROR_TWO ("Failed to create output pipe", strerror (errno
));
389 np_error
= NPERR_GENERIC_ERROR
;
390 goto cleanup_out_pipe_name
;
393 // Start a separate appletviewer process for each applet, even if
394 // there are multiple applets in the same page. We may need to
395 // change this behaviour if we find pages with multiple applets that
396 // rely on being run in the same VM.
398 // Critical region. Hold appletviewer_mutex while we start the
399 // appletviewer, create the IO channels and install the channel
401 g_mutex_lock (data
->appletviewer_mutex
);
403 np_error
= plugin_start_appletviewer (data
);
405 // If the appletviewer is not installed, then a dialog box will
406 // show up and the plugin will be killed.
407 if (np_error
!= NPERR_NO_ERROR
)
409 if (plugin_failed ())
410 goto cleanup_applet_failure
;
413 // Create plugin-to-appletviewer channel. The default encoding for
414 // the file is UTF-8.
415 // data->out_to_appletviewer
416 data
->out_to_appletviewer
= g_io_channel_new_file (data
->out_pipe_name
,
417 "w", &channel_error
);
418 if (!data
->out_to_appletviewer
)
422 PLUGIN_ERROR_TWO ("Failed to create output channel",
423 channel_error
->message
);
424 g_error_free (channel_error
);
425 channel_error
= NULL
;
428 PLUGIN_ERROR ("Failed to create output channel");
430 np_error
= NPERR_GENERIC_ERROR
;
431 goto cleanup_out_to_appletviewer
;
434 // Watch for hangup and error signals on the output pipe.
435 data
->out_watch_source
=
436 g_io_add_watch (data
->out_to_appletviewer
,
437 (GIOCondition
) (G_IO_ERR
| G_IO_HUP
),
438 plugin_out_pipe_callback
, (gpointer
) data
);
440 // Create appletviewer-to-plugin channel. The default encoding for
441 // the file is UTF-8.
442 // data->in_from_appletviewer
443 data
->in_from_appletviewer
= g_io_channel_new_file (data
->in_pipe_name
,
444 "r", &channel_error
);
445 if (!data
->in_from_appletviewer
)
449 PLUGIN_ERROR_TWO ("Failed to create input channel",
450 channel_error
->message
);
451 g_error_free (channel_error
);
452 channel_error
= NULL
;
455 PLUGIN_ERROR ("Failed to create input channel");
457 np_error
= NPERR_GENERIC_ERROR
;
458 goto cleanup_in_from_appletviewer
;
461 // Watch for hangup and error signals on the input pipe.
462 data
->in_watch_source
=
463 g_io_add_watch (data
->in_from_appletviewer
,
464 (GIOCondition
) (G_IO_IN
| G_IO_ERR
| G_IO_HUP
),
465 plugin_in_pipe_callback
, (gpointer
) data
);
467 // Wait until we receive confirmation that the appletviewer has
469 if (g_io_channel_read_line (data
->in_from_appletviewer
,
470 &read_message
, NULL
, NULL
,
472 != G_IO_STATUS_NORMAL
)
476 PLUGIN_ERROR_TWO ("Receiving confirmation from appletviewer failed",
477 channel_error
->message
);
478 g_error_free (channel_error
);
479 channel_error
= NULL
;
482 PLUGIN_ERROR ("Receiving confirmation from appletviewer failed");
484 np_error
= NPERR_GENERIC_ERROR
;
485 goto cleanup_in_watch_source
;
488 PLUGIN_DEBUG ("GCJ_New: got confirmation that appletviewer is running.");
489 data
->appletviewer_alive
= TRUE
;
491 // Send applet tag message to appletviewer.
492 applet_tag
= plugin_create_applet_tag (argc
, argn
, argv
);
493 tag_message
= g_strconcat ("tag ", documentbase
, " ", applet_tag
, NULL
);
495 plugin_send_message_to_appletviewer (data
, data
->instance_string
);
496 plugin_send_message_to_appletviewer (data
, tag_message
);
498 g_mutex_unlock (data
->appletviewer_mutex
);
500 // If initialization succeeded entirely then we store the plugin
501 // data in the instance structure and return. Otherwise we free the
502 // data we've allocated so far and set instance->pdata to NULL.
504 // Set back-pointer to owner instance.
505 data
->owner
= instance
;
506 instance
->pdata
= data
;
509 // An error occurred while initializing the plugin data or spawning
510 // the appletviewer so we free the data we've already allocated.
512 cleanup_in_watch_source
:
513 // Removing a source is harmless if it fails since it just means the
514 // source has already been removed.
515 g_source_remove (data
->in_watch_source
);
516 data
->in_watch_source
= 0;
518 cleanup_in_from_appletviewer
:
519 if (data
->in_from_appletviewer
)
520 g_io_channel_unref (data
->in_from_appletviewer
);
521 data
->in_from_appletviewer
= NULL
;
523 // cleanup_out_watch_source:
524 g_source_remove (data
->out_watch_source
);
525 data
->out_watch_source
= 0;
527 cleanup_out_to_appletviewer
:
528 if (data
->out_to_appletviewer
)
529 g_io_channel_unref (data
->out_to_appletviewer
);
530 data
->out_to_appletviewer
= NULL
;
533 // Delete output pipe.
534 unlink (data
->out_pipe_name
);
536 cleanup_applet_failure
:
537 cleanup_out_pipe_name
:
538 g_free (data
->out_pipe_name
);
539 data
->out_pipe_name
= NULL
;
542 // Delete input pipe.
543 unlink (data
->in_pipe_name
);
545 cleanup_in_pipe_name
:
546 g_free (data
->in_pipe_name
);
547 data
->in_pipe_name
= NULL
;
549 cleanup_appletviewer_mutex
:
550 g_free (data
->appletviewer_mutex
);
551 data
->appletviewer_mutex
= NULL
;
553 // cleanup_instance_string:
554 g_free (data
->instance_string
);
555 data
->instance_string
= NULL
;
558 // Eliminate back-pointer to plugin instance.
560 (*browserFunctions
.memfree
) (data
);
563 // Initialization failed so return a NULL pointer for the browser
565 instance
->pdata
= NULL
;
568 g_free (tag_message
);
572 g_free (read_message
);
574 g_free (documentbase
);
577 PLUGIN_DEBUG ("GCJ_New return");
583 GCJ_GetValue (NPP instance
, NPPVariable variable
, void* value
)
585 PLUGIN_DEBUG ("GCJ_GetValue");
587 NPError np_error
= NPERR_NO_ERROR
;
591 // This plugin needs XEmbed support.
592 case NPPVpluginNeedsXEmbed
:
594 PLUGIN_DEBUG ("GCJ_GetValue: returning TRUE for NeedsXEmbed.");
595 PRBool
* bool_value
= (PRBool
*) value
;
596 *bool_value
= PR_TRUE
;
601 PLUGIN_ERROR ("Unknown plugin value requested.");
602 np_error
= NPERR_GENERIC_ERROR
;
606 PLUGIN_DEBUG ("GCJ_GetValue return");
612 GCJ_Destroy (NPP instance
, NPSavedData
** save
)
614 PLUGIN_DEBUG ("GCJ_Destroy");
616 GCJPluginData
* data
= (GCJPluginData
*) instance
->pdata
;
620 // Critical region. Stop the appletviewer.
621 g_mutex_lock (data
->appletviewer_mutex
);
623 // Tell the appletviewer to destroy its embedded plugin window.
624 plugin_send_message_to_appletviewer (data
, "destroy");
625 // Shut down the appletviewer.
626 plugin_stop_appletviewer (data
);
628 g_mutex_unlock (data
->appletviewer_mutex
);
631 plugin_data_destroy (&data
);
634 PLUGIN_DEBUG ("GCJ_Destroy return");
636 return NPERR_NO_ERROR
;
640 GCJ_SetWindow (NPP instance
, NPWindow
* window
)
642 PLUGIN_DEBUG ("GCJ_SetWindow");
644 if (instance
== NULL
)
646 PLUGIN_ERROR ("Invalid instance.");
648 return NPERR_INVALID_INSTANCE_ERROR
;
651 GCJPluginData
* data
= (GCJPluginData
*) instance
->pdata
;
653 // Simply return if we receive a NULL window.
654 if ((window
== NULL
) || (window
->window
== NULL
))
656 PLUGIN_DEBUG ("GCJ_SetWindow: got NULL window.");
658 return NPERR_NO_ERROR
;
661 if (data
->window_handle
)
663 // The window already exists.
664 if (data
->window_handle
== window
->window
)
666 // The parent window is the same as in previous calls.
667 PLUGIN_DEBUG ("GCJ_SetWindow: window already exists.");
669 // Critical region. Read data->appletviewer_mutex and send
670 // a message to the appletviewer.
671 g_mutex_lock (data
->appletviewer_mutex
);
673 if (data
->appletviewer_alive
)
675 // The window is the same as it was for the last
677 if (window
->width
!= data
->window_width
)
679 PLUGIN_DEBUG ("GCJ_SetWindow: window width changed.");
680 // The width of the plugin window has changed.
682 // Send the new width to the appletviewer.
683 plugin_send_message_to_appletviewer (data
,
684 data
->instance_string
);
685 gchar
* width_message
= g_strdup_printf ("width %d",
687 plugin_send_message_to_appletviewer (data
, width_message
);
688 g_free (width_message
);
689 width_message
= NULL
;
691 // Store the new width.
692 data
->window_width
= window
->width
;
695 if (window
->height
!= data
->window_height
)
697 PLUGIN_DEBUG ("GCJ_SetWindow: window height changed.");
698 // The height of the plugin window has changed.
700 // Send the new height to the appletviewer.
701 plugin_send_message_to_appletviewer (data
,
702 data
->instance_string
);
703 gchar
* height_message
= g_strdup_printf ("height %d",
705 plugin_send_message_to_appletviewer (data
, height_message
);
706 g_free (height_message
);
707 height_message
= NULL
;
709 // Store the new height.
710 data
->window_height
= window
->height
;
715 // The appletviewer is not running.
716 PLUGIN_DEBUG ("GCJ_SetWindow: appletviewer is not running.");
719 g_mutex_unlock (data
->appletviewer_mutex
);
723 // The parent window has changed. This branch does run but
724 // doing nothing in response seems to be sufficient.
725 PLUGIN_DEBUG ("GCJ_SetWindow: parent window changed.");
730 PLUGIN_DEBUG ("GCJ_SetWindow: setting window.");
732 // Critical region. Send messages to appletviewer.
733 g_mutex_lock (data
->appletviewer_mutex
);
735 plugin_send_message_to_appletviewer (data
, data
->instance_string
);
736 gchar
*window_message
= g_strdup_printf ("handle %ld",
737 (gulong
) window
->window
);
738 plugin_send_message_to_appletviewer (data
, window_message
);
739 g_free (window_message
);
740 window_message
= NULL
;
742 g_mutex_unlock (data
->appletviewer_mutex
);
744 // Store the window handle.
745 data
->window_handle
= window
->window
;
748 PLUGIN_DEBUG ("GCJ_SetWindow return");
750 return NPERR_NO_ERROR
;
754 GCJ_NewStream (NPP instance
, NPMIMEType type
, NPStream
* stream
,
755 NPBool seekable
, uint16
* stype
)
757 PLUGIN_DEBUG ("GCJ_NewStream");
759 PLUGIN_DEBUG ("GCJ_NewStream return");
761 return NPERR_NO_ERROR
;
765 GCJ_StreamAsFile (NPP instance
, NPStream
* stream
, const char* filename
)
767 PLUGIN_DEBUG ("GCJ_StreamAsFile");
769 PLUGIN_DEBUG ("GCJ_StreamAsFile return");
773 GCJ_DestroyStream (NPP instance
, NPStream
* stream
, NPReason reason
)
775 PLUGIN_DEBUG ("GCJ_DestroyStream");
777 PLUGIN_DEBUG ("GCJ_DestroyStream return");
779 return NPERR_NO_ERROR
;
783 GCJ_WriteReady (NPP instance
, NPStream
* stream
)
785 PLUGIN_DEBUG ("GCJ_WriteReady");
787 PLUGIN_DEBUG ("GCJ_WriteReady return");
793 GCJ_Write (NPP instance
, NPStream
* stream
, int32 offset
, int32 len
,
796 PLUGIN_DEBUG ("GCJ_Write");
798 PLUGIN_DEBUG ("GCJ_Write return");
804 GCJ_Print (NPP instance
, NPPrint
* platformPrint
)
806 PLUGIN_DEBUG ("GCJ_Print");
808 PLUGIN_DEBUG ("GCJ_Print return");
812 GCJ_HandleEvent (NPP instance
, void* event
)
814 PLUGIN_DEBUG ("GCJ_HandleEvent");
816 PLUGIN_DEBUG ("GCJ_HandleEvent return");
822 GCJ_URLNotify (NPP instance
, const char* url
, NPReason reason
,
825 PLUGIN_DEBUG ("GCJ_URLNotify");
827 PLUGIN_DEBUG ("GCJ_URLNotify return");
831 GCJ_GetJavaClass (void)
833 PLUGIN_DEBUG ("GCJ_GetJavaClass");
835 PLUGIN_DEBUG ("GCJ_GetJavaClass return");
843 plugin_data_new (GCJPluginData
** data
)
845 PLUGIN_DEBUG ("plugin_data_new");
847 *data
= (GCJPluginData
*)
848 (*browserFunctions
.memalloc
) (sizeof (struct GCJPluginData
));
850 // appletviewer_alive is false until the applet viewer is spawned.
852 memset (*data
, 0, sizeof (struct GCJPluginData
));
854 PLUGIN_DEBUG ("plugin_data_new return");
857 // Documentbase retrieval. This function gets the current document's
858 // documentbase. This function relies on browser-private data so it
859 // will only work when the plugin is loaded in a Mozilla-based
860 // browser. We could not find a way to retrieve the documentbase
861 // using the original Netscape plugin API so we use the XPCOM API
864 plugin_get_documentbase (NPP instance
)
866 PLUGIN_DEBUG ("plugin_get_documentbase");
868 nsIPluginInstance
* xpcom_instance
= NULL
;
869 nsIPluginInstancePeer
* peer
= NULL
;
871 nsIPluginTagInfo2
* pluginTagInfo2
= NULL
;
872 info_union u
= { NULL
};
873 char const* documentbase
= NULL
;
874 gchar
* documentbase_copy
= NULL
;
876 xpcom_instance
= (nsIPluginInstance
*) (instance
->ndata
);
879 PLUGIN_ERROR ("xpcom_instance is NULL.");
883 xpcom_instance
->GetPeer (&peer
);
886 PLUGIN_ERROR ("peer is NULL.");
890 u
.info_field
= &pluginTagInfo2
;
892 result
= peer
->QueryInterface (kIPluginTagInfo2IID
,
894 if (result
|| !pluginTagInfo2
)
896 PLUGIN_ERROR ("pluginTagInfo2 retrieval failed.");
900 pluginTagInfo2
->GetDocumentBase (&documentbase
);
904 PLUGIN_ERROR ("documentbase is NULL.");
905 goto cleanup_plugintaginfo2
;
908 documentbase_copy
= g_strdup (documentbase
);
910 // Release references.
911 cleanup_plugintaginfo2
:
912 NS_RELEASE (pluginTagInfo2
);
918 PLUGIN_DEBUG ("plugin_get_documentbase return");
920 return documentbase_copy
;
923 // This function shows a error message if the appletviewer has
924 // not been installed. It returns true, if the user presses the
929 GtkWidget
* dialog
= NULL
;
930 GtkWidget
* ok_button
= NULL
;
932 dialog
= gtk_message_dialog_new (NULL
,
937 ok_button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
940 gtk_widget_show_all (dialog
);
941 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_OK
)
943 gtk_widget_destroy (dialog
);
949 // plugin_user_trusts_documentbase returns true if the given
950 // documentbase is in the documentbase whitelist. Otherwise it asks
951 // the user if he trusts the given documentbase by calling
952 // plugin_ask_user_about_documentbase.
954 plugin_user_trusts_documentbase (char* documentbase
)
956 bool applet_in_whitelist
= false;
958 // Check if documentbase is in whitelist.
961 gchar
* whitelist_entry
= NULL
;
962 gchar
* newline_documentbase
= NULL
;
964 // If reading fails, break out of this loop with
965 // applet_in_whitelist still set to false.
966 if (g_io_channel_read_line (whitelist_file
, &whitelist_entry
,
967 NULL
, NULL
, &channel_error
)
968 != G_IO_STATUS_NORMAL
)
972 PLUGIN_ERROR_TWO ("Failed to read line from whitelist file",
973 channel_error
->message
);
974 g_error_free (channel_error
);
975 channel_error
= NULL
;
978 PLUGIN_ERROR ("Failed to read line from whitelist file.");
979 g_free (whitelist_entry
);
980 whitelist_entry
= NULL
;
984 newline_documentbase
= g_strdup_printf ("%s\n", documentbase
);
985 if (!strcmp (newline_documentbase
, whitelist_entry
))
987 applet_in_whitelist
= true;
988 g_free (newline_documentbase
);
989 newline_documentbase
= NULL
;
990 g_free (whitelist_entry
);
991 whitelist_entry
= NULL
;
994 g_free (whitelist_entry
);
995 whitelist_entry
= NULL
;
996 g_free (newline_documentbase
);
997 newline_documentbase
= NULL
;
1000 return applet_in_whitelist
? true
1001 : plugin_ask_user_about_documentbase (documentbase
);
1004 // plugin_add_documentbase_to_whitelist adds the given documentbase to
1005 // the user's documentbase whitelist.
1007 plugin_add_documentbase_to_whitelist (char* documentbase
)
1009 gsize bytes_written
= 0;
1010 char* newline_documentbase
= NULL
;
1011 GIOStatus status
= G_IO_STATUS_NORMAL
;
1013 newline_documentbase
= g_strdup_printf ("%s\n", documentbase
);
1014 status
= g_io_channel_write_chars (whitelist_file
,
1015 newline_documentbase
, -1, &bytes_written
,
1017 g_free (newline_documentbase
);
1018 newline_documentbase
= NULL
;
1020 if (status
!= G_IO_STATUS_NORMAL
)
1024 PLUGIN_ERROR_TWO ("Error writing to whitelist file",
1025 channel_error
->message
);
1026 g_error_free (channel_error
);
1027 channel_error
= NULL
;
1030 PLUGIN_ERROR ("Error writing to whitelist file.");
1033 if (g_io_channel_flush (whitelist_file
, &channel_error
)
1034 != G_IO_STATUS_NORMAL
)
1038 PLUGIN_ERROR_TWO ("Failed to write whitelist file",
1039 channel_error
->message
);
1040 g_error_free (channel_error
);
1041 channel_error
= NULL
;
1044 PLUGIN_ERROR ("Failed to write whitelist file.");
1047 if (g_io_channel_shutdown (whitelist_file
, TRUE
, &channel_error
)
1048 != G_IO_STATUS_NORMAL
)
1052 PLUGIN_ERROR_TWO ("Failed to close whitelist file",
1053 channel_error
->message
);
1054 g_error_free (channel_error
);
1055 channel_error
= NULL
;
1058 PLUGIN_ERROR ("Failed to close whitelist file.");
1062 // plugin_ask_user_about_documentbase puts up a dialog box that asks if the
1063 // user trusts applets from this documentbase. The user has three
1064 // options: "Cancel", "Trust Applet" and "Trust Applet and Add to
1065 // Whitelist". If the user selects Cancel (the default) then a
1066 // generic error code is returned from GCJ_New, telling the browser
1067 // that the applet failed to load. If the user selects "Trust Applet"
1068 // then plugin loading proceeds. If the user selects "Trust Applet
1069 // and Add to Whitelist" then this documentbase is added to the user's
1070 // applet whitelist and plugin loading proceeds.
1072 plugin_ask_user_about_documentbase (char* documentbase
)
1074 GtkWidget
* dialog
= NULL
;
1075 GtkWidget
* ok_button
= NULL
;
1076 GtkWidget
* cancel_button
= NULL
;
1077 GtkWidget
* whitelist_button
= NULL
;
1078 gint dialog_response
= GTK_RESPONSE_NONE
;
1080 dialog
= gtk_message_dialog_new (NULL
,
1082 GTK_MESSAGE_WARNING
,
1086 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog
),
1087 SECURITY_DESCRIPTION
, whitelist_filename
);
1089 cancel_button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
1091 GTK_RESPONSE_CANCEL
);
1092 ok_button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
1093 RESPONSE_TRUST_APPLET
,
1095 whitelist_button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
1096 RESPONSE_TRUST_APPLET_ADD_TO_LIST
,
1097 GTK_RESPONSE_APPLY
);
1098 gtk_widget_grab_focus (cancel_button
);
1100 gtk_widget_show_all (dialog
);
1101 dialog_response
= gtk_dialog_run (GTK_DIALOG (dialog
));
1102 gtk_widget_destroy (dialog
);
1103 if (dialog_response
== GTK_RESPONSE_CANCEL
)
1105 // The user does not trust this documentbase.
1108 else if (dialog_response
== GTK_RESPONSE_APPLY
)
1110 // The user wants this documentbase added to his documentbase
1112 plugin_add_documentbase_to_whitelist (documentbase
);
1114 // The user trusts this documentbase.
1118 // plugin_in_pipe_callback is called when data is available on the
1119 // input pipe, or when the appletviewer crashes or is killed. It may
1120 // be called after data has been destroyed in which case it simply
1121 // returns FALSE to remove itself from the glib main loop.
1123 plugin_in_pipe_callback (GIOChannel
* source
,
1124 GIOCondition condition
,
1125 gpointer plugin_data
)
1127 PLUGIN_DEBUG ("plugin_in_pipe_callback");
1129 GCJPluginData
* data
= (GCJPluginData
*) plugin_data
;
1130 gboolean keep_installed
= TRUE
;
1132 // If data is NULL then GCJ_Destroy has already been called and
1133 // plugin_in_pipe_callback is being called after plugin
1134 // destruction. In that case all we need to do is return FALSE so
1135 // that the plugin_in_pipe_callback watch is removed.
1138 // Critical region. Set or clear data->appletviewer_alive.
1139 g_mutex_lock (data
->appletviewer_mutex
);
1141 if (condition
& G_IO_IN
)
1143 gchar
* message
= NULL
;
1145 if (g_io_channel_read_line (data
->in_from_appletviewer
,
1146 &message
, NULL
, NULL
,
1148 != G_IO_STATUS_NORMAL
)
1152 PLUGIN_ERROR_TWO ("Failed to read line from input channel",
1153 channel_error
->message
);
1154 g_error_free (channel_error
);
1155 channel_error
= NULL
;
1158 PLUGIN_ERROR ("Failed to read line from input channel");
1162 if (g_str_has_prefix (message
, "url "))
1164 gchar
** parts
= g_strsplit (message
, " ", 3);
1165 PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback:"
1166 " opening URL", parts
[1]);
1167 PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback:"
1168 " URL target", parts
[2]);
1169 // Open the URL in a new browser window.
1171 (*browserFunctions
.geturl
) (data
->owner
, parts
[1], parts
[2]);
1172 if (np_error
!= NPERR_NO_ERROR
)
1173 PLUGIN_ERROR ("Failed to load URL.");
1177 else if (g_str_has_prefix (message
, "status "))
1179 gchar
** parts
= g_strsplit (message
, " ", 2);
1181 PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback:"
1182 " setting status", parts
[1]);
1183 (*browserFunctions
.status
) (data
->owner
, parts
[1]);
1187 g_print (" PIPE: plugin read %s\n", message
);
1193 keep_installed
= TRUE
;
1196 if (condition
& (G_IO_ERR
| G_IO_HUP
))
1198 PLUGIN_DEBUG ("appletviewer has stopped.");
1199 data
->appletviewer_alive
= FALSE
;
1200 keep_installed
= FALSE
;
1202 g_mutex_unlock (data
->appletviewer_mutex
);
1205 PLUGIN_DEBUG ("plugin_in_pipe_callback return");
1207 return keep_installed
;
1210 // plugin_out_pipe_callback is called when the appletviewer crashes or
1211 // is killed. It may be called after data has been destroyed in which
1212 // case it simply returns FALSE to remove itself from the glib main
1215 plugin_out_pipe_callback (GIOChannel
* source
,
1216 GIOCondition condition
,
1217 gpointer plugin_data
)
1219 PLUGIN_DEBUG ("plugin_out_pipe_callback");
1221 GCJPluginData
* data
= (GCJPluginData
*) plugin_data
;
1223 // If data is NULL then GCJ_Destroy has already been called and
1224 // plugin_out_pipe_callback is being called after plugin
1225 // destruction. In that case all we need to do is return FALSE so
1226 // that the plugin_out_pipe_callback watch is removed.
1229 // Critical region. Clear data->appletviewer_alive.
1230 g_mutex_lock (data
->appletviewer_mutex
);
1232 PLUGIN_DEBUG ("plugin_out_pipe_callback: appletviewer has stopped.");
1233 data
->appletviewer_alive
= FALSE
;
1235 g_mutex_unlock (data
->appletviewer_mutex
);
1238 PLUGIN_DEBUG ("plugin_out_pipe_callback return");
1244 plugin_start_appletviewer (GCJPluginData
* data
)
1246 PLUGIN_DEBUG ("plugin_start_appletviewer");
1247 NPError error
= NPERR_NO_ERROR
;
1249 if (!data
->appletviewer_alive
)
1251 gchar
* command_line
[3] = { NULL
, NULL
, NULL
};
1253 command_line
[0] = g_strdup (APPLETVIEWER_EXECUTABLE
);
1254 // Output from plugin's perspective is appletviewer's input.
1255 // Input from plugin's perspective is appletviewer's output.
1256 command_line
[1] = g_strdup_printf ("--plugin=%s,%s",
1257 data
->out_pipe_name
,
1258 data
->in_pipe_name
);
1259 command_line
[2] = NULL
;
1261 if (!g_spawn_async (NULL
, command_line
, NULL
, (GSpawnFlags
) 0,
1262 NULL
, NULL
, NULL
, &channel_error
))
1266 PLUGIN_ERROR_TWO ("Failed to spawn applet viewer",
1267 channel_error
->message
);
1268 g_error_free (channel_error
);
1269 channel_error
= NULL
;
1272 PLUGIN_ERROR ("Failed to spawn applet viewer");
1273 error
= NPERR_GENERIC_ERROR
;
1278 g_free (command_line
[0]);
1279 command_line
[0] = NULL
;
1280 g_free (command_line
[1]);
1281 command_line
[1] = NULL
;
1282 g_free (command_line
[2]);
1283 command_line
[2] = NULL
;
1286 PLUGIN_DEBUG ("plugin_start_appletviewer return");
1290 // Build up the applet tag string that we'll send to the applet
1293 plugin_create_applet_tag (int16 argc
, char* argn
[], char* argv
[])
1295 PLUGIN_DEBUG ("plugin_create_applet_tag");
1297 gchar
* applet_tag
= g_strdup ("<EMBED ");
1298 gchar
* parameters
= g_strdup ("");
1300 for (int16 i
= 0; i
< argc
; i
++)
1302 if (!g_ascii_strcasecmp (argn
[i
], "code"))
1304 gchar
* code
= g_strdup_printf ("CODE=\"%s\" ", argv
[i
]);
1305 applet_tag
= g_strconcat (applet_tag
, code
, NULL
);
1309 else if (!g_ascii_strcasecmp (argn
[i
], "codebase"))
1311 gchar
* codebase
= g_strdup_printf ("CODEBASE=\"%s\" ", argv
[i
]);
1312 applet_tag
= g_strconcat (applet_tag
, codebase
, NULL
);
1316 else if (!g_ascii_strcasecmp (argn
[i
], "archive"))
1318 gchar
* archive
= g_strdup_printf ("ARCHIVE=\"%s\" ", argv
[i
]);
1319 applet_tag
= g_strconcat (applet_tag
, archive
, NULL
);
1323 else if (!g_ascii_strcasecmp (argn
[i
], "width"))
1325 gchar
* width
= g_strdup_printf ("WIDTH=\"%s\" ", argv
[i
]);
1326 applet_tag
= g_strconcat (applet_tag
, width
, NULL
);
1330 else if (!g_ascii_strcasecmp (argn
[i
], "height"))
1332 gchar
* height
= g_strdup_printf ("HEIGHT=\"%s\" ", argv
[i
]);
1333 applet_tag
= g_strconcat (applet_tag
, height
, NULL
);
1339 // Escape the parameter value so that line termination
1340 // characters will pass through the pipe.
1341 if (argv
[i
] != '\0')
1343 gchar
* escaped
= NULL
;
1345 escaped
= g_strescape (argv
[i
], NULL
);
1346 parameters
= g_strconcat (parameters
, "<PARAM NAME=\"", argn
[i
],
1347 "\" VALUE=\"", escaped
, "\">", NULL
);
1355 applet_tag
= g_strconcat (applet_tag
, ">", parameters
, "</EMBED>", NULL
);
1357 g_free (parameters
);
1360 PLUGIN_DEBUG ("plugin_create_applet_tag return");
1365 // plugin_send_message_to_appletviewer must be called while holding
1366 // data->appletviewer_mutex.
1368 plugin_send_message_to_appletviewer (GCJPluginData
* data
, gchar
const* message
)
1370 PLUGIN_DEBUG ("plugin_send_message_to_appletviewer");
1372 if (data
->appletviewer_alive
)
1374 gchar
* newline_message
= NULL
;
1375 gsize bytes_written
= 0;
1377 // Send message to appletviewer.
1378 newline_message
= g_strdup_printf ("%s\n", message
);
1380 // g_io_channel_write_chars will return something other than
1381 // G_IO_STATUS_NORMAL if not all the data is written. In that
1382 // case we fail rather than retrying.
1383 if (g_io_channel_write_chars (data
->out_to_appletviewer
,
1384 newline_message
, -1, &bytes_written
,
1386 != G_IO_STATUS_NORMAL
)
1390 PLUGIN_ERROR_TWO ("Failed to write bytes to output channel",
1391 channel_error
->message
);
1392 g_error_free (channel_error
);
1393 channel_error
= NULL
;
1396 PLUGIN_ERROR ("Failed to write bytes to output channel");
1399 if (g_io_channel_flush (data
->out_to_appletviewer
, &channel_error
)
1400 != G_IO_STATUS_NORMAL
)
1404 PLUGIN_ERROR_TWO ("Failed to flush bytes to output channel",
1405 channel_error
->message
);
1406 g_error_free (channel_error
);
1407 channel_error
= NULL
;
1410 PLUGIN_ERROR ("Failed to flush bytes to output channel");
1412 g_free (newline_message
);
1413 newline_message
= NULL
;
1415 g_print (" PIPE: plugin wrote %s\n", message
);
1418 PLUGIN_DEBUG ("plugin_send_message_to_appletviewer return");
1421 // Stop the appletviewer process. When this is called the
1422 // appletviewer can be in any of three states: running, crashed or
1423 // hung. If the appletviewer is running then sending it "shutdown"
1424 // will cause it to exit. This will cause
1425 // plugin_out_pipe_callback/plugin_in_pipe_callback to be called and
1426 // the input and output channels to be shut down. If the appletviewer
1427 // has crashed then plugin_out_pipe_callback/plugin_in_pipe_callback
1428 // would already have been called and data->appletviewer_alive cleared
1429 // in which case this function simply returns. If the appletviewer is
1430 // hung then this function will be successful and the input and output
1431 // watches will be removed by plugin_data_destroy.
1432 // plugin_stop_appletviewer must be called with
1433 // data->appletviewer_mutex held.
1435 plugin_stop_appletviewer (GCJPluginData
* data
)
1437 PLUGIN_DEBUG ("plugin_stop_appletviewer");
1439 if (data
->appletviewer_alive
)
1441 // Shut down the appletviewer.
1442 gsize bytes_written
= 0;
1444 if (data
->out_to_appletviewer
)
1446 if (g_io_channel_write_chars (data
->out_to_appletviewer
, "shutdown",
1447 -1, &bytes_written
, &channel_error
)
1448 != G_IO_STATUS_NORMAL
)
1452 PLUGIN_ERROR_TWO ("Failed to write shutdown message to"
1453 " appletviewer", channel_error
->message
);
1454 g_error_free (channel_error
);
1455 channel_error
= NULL
;
1458 PLUGIN_ERROR ("Failed to write shutdown message to");
1461 if (g_io_channel_flush (data
->out_to_appletviewer
, &channel_error
)
1462 != G_IO_STATUS_NORMAL
)
1466 PLUGIN_ERROR_TWO ("Failed to write shutdown message to"
1467 " appletviewer", channel_error
->message
);
1468 g_error_free (channel_error
);
1469 channel_error
= NULL
;
1472 PLUGIN_ERROR ("Failed to write shutdown message to");
1475 if (g_io_channel_shutdown (data
->out_to_appletviewer
,
1476 TRUE
, &channel_error
)
1477 != G_IO_STATUS_NORMAL
)
1481 PLUGIN_ERROR_TWO ("Failed to shut down appletviewer"
1482 " output channel", channel_error
->message
);
1483 g_error_free (channel_error
);
1484 channel_error
= NULL
;
1487 PLUGIN_ERROR ("Failed to shut down appletviewer");
1491 if (data
->in_from_appletviewer
)
1493 if (g_io_channel_shutdown (data
->in_from_appletviewer
,
1494 TRUE
, &channel_error
)
1495 != G_IO_STATUS_NORMAL
)
1499 PLUGIN_ERROR_TWO ("Failed to shut down appletviewer"
1500 " input channel", channel_error
->message
);
1501 g_error_free (channel_error
);
1502 channel_error
= NULL
;
1505 PLUGIN_ERROR ("Failed to shut down appletviewer");
1510 PLUGIN_DEBUG ("plugin_stop_appletviewer return");
1514 plugin_data_destroy (GCJPluginData
** data
)
1516 PLUGIN_DEBUG ("plugin_data_destroy");
1518 GCJPluginData
* tofree
= *data
;
1520 tofree
->window_handle
= NULL
;
1521 tofree
->window_height
= 0;
1522 tofree
->window_width
= 0;
1524 // Copied from GCJ_New.
1526 // cleanup_in_watch_source:
1527 // Removing a source is harmless if it fails since it just means the
1528 // source has already been removed.
1529 g_source_remove (tofree
->in_watch_source
);
1530 tofree
->in_watch_source
= 0;
1532 // cleanup_in_from_appletviewer:
1533 if (tofree
->in_from_appletviewer
)
1534 g_io_channel_unref (tofree
->in_from_appletviewer
);
1535 tofree
->in_from_appletviewer
= NULL
;
1537 // cleanup_out_watch_source:
1538 g_source_remove (tofree
->out_watch_source
);
1539 tofree
->out_watch_source
= 0;
1541 // cleanup_out_to_appletviewer:
1542 if (tofree
->out_to_appletviewer
)
1543 g_io_channel_unref (tofree
->out_to_appletviewer
);
1544 tofree
->out_to_appletviewer
= NULL
;
1546 // cleanup_out_pipe:
1547 // Delete output pipe.
1548 unlink (tofree
->out_pipe_name
);
1550 // cleanup_out_pipe_name:
1551 g_free (tofree
->out_pipe_name
);
1552 tofree
->out_pipe_name
= NULL
;
1555 // Delete input pipe.
1556 unlink (tofree
->in_pipe_name
);
1558 // cleanup_in_pipe_name:
1559 g_free (tofree
->in_pipe_name
);
1560 tofree
->in_pipe_name
= NULL
;
1562 // cleanup_appletviewer_mutex:
1563 g_free (tofree
->appletviewer_mutex
);
1564 tofree
->appletviewer_mutex
= NULL
;
1566 // cleanup_instance_string:
1567 g_free (tofree
->instance_string
);
1568 tofree
->instance_string
= NULL
;
1571 // Eliminate back-pointer to plugin instance.
1572 tofree
->owner
= NULL
;
1573 (*browserFunctions
.memfree
) (tofree
);
1576 PLUGIN_DEBUG ("plugin_data_destroy return");
1579 // FACTORY FUNCTIONS
1581 // Provides the browser with pointers to the plugin functions that we
1582 // implement and initializes a local table with browser functions that
1583 // we may wish to call. Called once, after browser startup and before
1584 // the first plugin instance is created.
1585 // The field 'initialized' is set to true once this function has
1586 // finished. If 'initialized' is already true at the beginning of
1587 // this function, then it is evident that NP_Initialize has already
1588 // been called. There is no need to call this function more than once and
1589 // this workaround avoids any duplicate calls.
1591 NP_Initialize (NPNetscapeFuncs
* browserTable
, NPPluginFuncs
* pluginTable
)
1593 PLUGIN_DEBUG ("NP_Initialize");
1596 return NPERR_NO_ERROR
;
1597 else if ((browserTable
== NULL
) || (pluginTable
== NULL
))
1599 PLUGIN_ERROR ("Browser or plugin function table is NULL.");
1601 return NPERR_INVALID_FUNCTABLE_ERROR
;
1604 // Ensure that the major version of the plugin API that the browser
1605 // expects is not more recent than the major version of the API that
1606 // we've implemented.
1607 if ((browserTable
->version
>> 8) > NP_VERSION_MAJOR
)
1609 PLUGIN_ERROR ("Incompatible version.");
1611 return NPERR_INCOMPATIBLE_VERSION_ERROR
;
1614 // Ensure that the plugin function table we've received is large
1615 // enough to store the number of functions that we may provide.
1616 if (pluginTable
->size
< sizeof (NPPluginFuncs
))
1618 PLUGIN_ERROR ("Invalid plugin function table.");
1620 return NPERR_INVALID_FUNCTABLE_ERROR
;
1623 // Ensure that the browser function table is large enough to store
1624 // the number of browser functions that we may use.
1625 if (browserTable
->size
< sizeof (NPNetscapeFuncs
))
1627 PLUGIN_ERROR ("Invalid browser function table.");
1629 return NPERR_INVALID_FUNCTABLE_ERROR
;
1632 data_directory
= g_strconcat(getenv("HOME"), "/.gcjwebplugin", NULL
);
1633 whitelist_filename
= g_strconcat (data_directory
, "/whitelist.txt", NULL
);
1634 // Make sure the plugin data directory exists, creating it if
1636 if (!g_file_test (data_directory
,
1637 (GFileTest
) (G_FILE_TEST_EXISTS
| G_FILE_TEST_IS_DIR
)))
1641 file_error
= g_mkdir (data_directory
, 0700);
1642 if (file_error
!= 0)
1644 PLUGIN_ERROR_THREE ("Failed to create data directory",
1647 return NPERR_GENERIC_ERROR
;
1651 // Store in a local table the browser functions that we may use.
1652 browserFunctions
.version
= browserTable
->version
;
1653 browserFunctions
.size
= browserTable
->size
;
1654 browserFunctions
.posturl
= browserTable
->posturl
;
1655 browserFunctions
.geturl
= browserTable
->geturl
;
1656 browserFunctions
.geturlnotify
= browserTable
->geturlnotify
;
1657 browserFunctions
.requestread
= browserTable
->requestread
;
1658 browserFunctions
.newstream
= browserTable
->newstream
;
1659 browserFunctions
.write
= browserTable
->write
;
1660 browserFunctions
.destroystream
= browserTable
->destroystream
;
1661 browserFunctions
.status
= browserTable
->status
;
1662 browserFunctions
.uagent
= browserTable
->uagent
;
1663 browserFunctions
.memalloc
= browserTable
->memalloc
;
1664 browserFunctions
.memfree
= browserTable
->memfree
;
1665 browserFunctions
.memflush
= browserTable
->memflush
;
1666 browserFunctions
.reloadplugins
= browserTable
->reloadplugins
;
1667 browserFunctions
.getvalue
= browserTable
->getvalue
;
1669 // Return to the browser the plugin functions that we implement.
1670 pluginTable
->version
= (NP_VERSION_MAJOR
<< 8) + NP_VERSION_MINOR
;
1671 pluginTable
->size
= sizeof (NPPluginFuncs
);
1672 pluginTable
->newp
= NewNPP_NewProc (GCJ_New
);
1673 pluginTable
->destroy
= NewNPP_DestroyProc (GCJ_Destroy
);
1674 pluginTable
->setwindow
= NewNPP_SetWindowProc (GCJ_SetWindow
);
1675 pluginTable
->newstream
= NewNPP_NewStreamProc (GCJ_NewStream
);
1676 pluginTable
->destroystream
= NewNPP_DestroyStreamProc (GCJ_DestroyStream
);
1677 pluginTable
->asfile
= NewNPP_StreamAsFileProc (GCJ_StreamAsFile
);
1678 pluginTable
->writeready
= NewNPP_WriteReadyProc (GCJ_WriteReady
);
1679 pluginTable
->write
= NewNPP_WriteProc (GCJ_Write
);
1680 pluginTable
->print
= NewNPP_PrintProc (GCJ_Print
);
1681 pluginTable
->urlnotify
= NewNPP_URLNotifyProc (GCJ_URLNotify
);
1682 pluginTable
->getvalue
= NewNPP_GetValueProc (GCJ_GetValue
);
1686 // Initialize threads (needed for mutexes).
1687 if (!g_thread_supported ())
1688 g_thread_init (NULL
);
1690 plugin_instance_mutex
= g_mutex_new ();
1692 PLUGIN_DEBUG ("NP_Initialize: using " APPLETVIEWER_EXECUTABLE
".");
1694 PLUGIN_DEBUG ("NP_Initialize return");
1696 return NPERR_NO_ERROR
;
1699 // Returns a string describing the MIME type that this plugin
1702 NP_GetMIMEDescription (void)
1704 PLUGIN_DEBUG ("NP_GetMIMEDescription");
1706 PLUGIN_DEBUG ("NP_GetMIMEDescription return");
1708 return (char*) PLUGIN_MIME_DESC
;
1711 // Returns a value relevant to the plugin as a whole. The browser
1712 // calls this function to obtain information about the plugin.
1714 NP_GetValue (void* future
, NPPVariable variable
, void* value
)
1716 PLUGIN_DEBUG ("NP_GetValue");
1718 NPError result
= NPERR_NO_ERROR
;
1719 gchar
** char_value
= (gchar
**) value
;
1723 case NPPVpluginNameString
:
1724 PLUGIN_DEBUG ("NP_GetValue: returning plugin name.");
1725 *char_value
= g_strdup (PLUGIN_NAME
" " PACKAGE_VERSION
);
1728 case NPPVpluginDescriptionString
:
1729 PLUGIN_DEBUG ("NP_GetValue: returning plugin description.");
1730 *char_value
= g_strdup (PLUGIN_DESC
);
1734 PLUGIN_ERROR ("Unknown plugin value requested.");
1735 result
= NPERR_GENERIC_ERROR
;
1739 PLUGIN_DEBUG ("NP_GetValue return");
1744 // Shuts down the plugin. Called after the last plugin instance is
1749 PLUGIN_DEBUG ("NP_Shutdown");
1752 if (plugin_instance_mutex
)
1754 g_mutex_free (plugin_instance_mutex
);
1755 plugin_instance_mutex
= NULL
;
1760 g_io_channel_close (whitelist_file
);
1761 whitelist_file
= NULL
;
1766 g_free (data_directory
);
1767 data_directory
= NULL
;
1770 if (whitelist_filename
)
1772 g_free (whitelist_filename
);
1773 whitelist_filename
= NULL
;
1776 initialized
= false;
1778 PLUGIN_DEBUG ("NP_Shutdown return");
1780 return NPERR_NO_ERROR
;