source: trunk/icedtea-web/plugin/icedteanp/IcedTeaNPPlugin.cc@ 365

Last change on this file since 365 was 365, checked in by dmik, 13 years ago

icedtea-web: Hook up DLL initialization to call static C/C++ initializers.

File size: 78.2 KB
Line 
1/* IcedTeaNPPlugin.cc -- web browser plugin to execute Java applets
2 Copyright (C) 2003, 2004, 2006, 2007 Free Software Foundation, Inc.
3 Copyright (C) 2009, 2010 Red Hat
4
5This file is part of GNU Classpath.
6
7GNU Classpath is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU Classpath is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Classpath; see the file COPYING. If not, write to the
19Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
2002110-1301 USA.
21
22Linking this library statically or dynamically with other modules is
23making a combined work based on this library. Thus, the terms and
24conditions of the GNU General Public License cover the whole
25combination.
26
27As a special exception, the copyright holders of this library give you
28permission to link this library with independent modules to produce an
29executable, regardless of the license terms of these independent
30modules, and to copy and distribute the resulting executable under
31terms of your choice, provided that you also meet, for each linked
32independent module, the terms and conditions of the license of that
33module. An independent module is a module which is not derived from
34or based on this library. If you modify this library, you may extend
35this exception to your version of the library, but you are not
36obligated to do so. If you do not wish to do so, delete this
37exception statement from your version. */
38
39#ifdef __OS2__
40// OS/2 includes.
41#define INCL_DOSPROCESS
42#define INCL_DOSERRORS
43#define INCL_WINDIALOG
44#include <os2.h>
45#include <emx/startup.h>
46#endif
47
48// System includes.
49#include <dlfcn.h>
50#include <errno.h>
51#include <libgen.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <sys/stat.h>
56#include <sys/types.h>
57#include <unistd.h>
58
59// Liveconnect extension
60#include "IcedTeaScriptablePluginObject.h"
61#include "IcedTeaNPPlugin.h"
62
63#include "OS.h"
64
65#if MOZILLA_VERSION_COLLAPSED < 1090100
66// Documentbase retrieval includes.
67#include <nsIPluginInstance.h>
68#include <nsIPluginInstancePeer.h>
69#include <nsIPluginTagInfo2.h>
70
71// API's into Mozilla
72#include <nsCOMPtr.h>
73#include <nsICookieService.h>
74#include <nsIDNSRecord.h>
75#include <nsIDNSService.h>
76#include <nsINetUtil.h>
77#include <nsIProxyInfo.h>
78#include <nsIProtocolProxyService.h>
79#include <nsIScriptSecurityManager.h>
80#include <nsIIOService.h>
81#include <nsIURI.h>
82#include <nsNetCID.h>
83#include <nsStringAPI.h>
84#include <nsServiceManagerUtils.h>
85#endif
86
87// Error reporting macros.
88#define PLUGIN_ERROR(message) \
89 g_printerr ("%s:%d: thread %p: Error: %s\n", __FILE__, __LINE__, \
90 g_thread_self (), message)
91
92#define PLUGIN_ERROR_TWO(first, second) \
93 g_printerr ("%s:%d: thread %p: Error: %s: %s\n", __FILE__, __LINE__, \
94 g_thread_self (), first, second)
95
96#define PLUGIN_ERROR_THREE(first, second, third) \
97 g_printerr ("%s:%d: thread %p: Error: %s: %s: %s\n", __FILE__, \
98 __LINE__, g_thread_self (), first, second, third)
99
100// Plugin information passed to about:plugins.
101#define PLUGIN_FULL_NAME PLUGIN_NAME " (using " PLUGIN_VERSION ")"
102#define PLUGIN_DESC "The <a href=\"" PACKAGE_URL "\">" PLUGIN_NAME "</a> executes Java applets."
103
104#define PLUGIN_MIME_DESC \
105 "application/x-java-vm:class,jar:IcedTea;" \
106 "application/x-java-applet:class,jar:IcedTea;" \
107 "application/x-java-applet;version=1.1:class,jar:IcedTea;" \
108 "application/x-java-applet;version=1.1.1:class,jar:IcedTea;" \
109 "application/x-java-applet;version=1.1.2:class,jar:IcedTea;" \
110 "application/x-java-applet;version=1.1.3:class,jar:IcedTea;" \
111 "application/x-java-applet;version=1.2:class,jar:IcedTea;" \
112 "application/x-java-applet;version=1.2.1:class,jar:IcedTea;" \
113 "application/x-java-applet;version=1.2.2:class,jar:IcedTea;" \
114 "application/x-java-applet;version=1.3:class,jar:IcedTea;" \
115 "application/x-java-applet;version=1.3.1:class,jar:IcedTea;" \
116 "application/x-java-applet;version=1.4:class,jar:IcedTea;" \
117 "application/x-java-applet;version=1.4.1:class,jar:IcedTea;" \
118 "application/x-java-applet;version=1.4.2:class,jar:IcedTea;" \
119 "application/x-java-applet;version=1.5:class,jar:IcedTea;" \
120 "application/x-java-applet;version=1.6:class,jar:IcedTea;" \
121 "application/x-java-applet;jpi-version=1.6.0_" JDK_UPDATE_VERSION ":class,jar:IcedTea;" \
122 "application/x-java-bean:class,jar:IcedTea;" \
123 "application/x-java-bean;version=1.1:class,jar:IcedTea;" \
124 "application/x-java-bean;version=1.1.1:class,jar:IcedTea;" \
125 "application/x-java-bean;version=1.1.2:class,jar:IcedTea;" \
126 "application/x-java-bean;version=1.1.3:class,jar:IcedTea;" \
127 "application/x-java-bean;version=1.2:class,jar:IcedTea;" \
128 "application/x-java-bean;version=1.2.1:class,jar:IcedTea;" \
129 "application/x-java-bean;version=1.2.2:class,jar:IcedTea;" \
130 "application/x-java-bean;version=1.3:class,jar:IcedTea;" \
131 "application/x-java-bean;version=1.3.1:class,jar:IcedTea;" \
132 "application/x-java-bean;version=1.4:class,jar:IcedTea;" \
133 "application/x-java-bean;version=1.4.1:class,jar:IcedTea;" \
134 "application/x-java-bean;version=1.4.2:class,jar:IcedTea;" \
135 "application/x-java-bean;version=1.5:class,jar:IcedTea;" \
136 "application/x-java-bean;version=1.6:class,jar:IcedTea;" \
137 "application/x-java-bean;jpi-version=1.6.0_" JDK_UPDATE_VERSION ":class,jar:IcedTea;" \
138 "application/x-java-vm-npruntime::IcedTea;"
139
140#define PLUGIN_URL NS_INLINE_PLUGIN_CONTRACTID_PREFIX NS_JVM_MIME_TYPE
141#define PLUGIN_MIME_TYPE "application/x-java-vm"
142#define PLUGIN_FILE_EXTS "class,jar,zip"
143#define PLUGIN_MIME_COUNT 1
144
145#define FAILURE_MESSAGE "icedteanp plugin error: Failed to run %s." \
146 " For more detail rerun \"firefox -g\" in a terminal window."
147
148#if MOZILLA_VERSION_COLLAPSED < 1090100
149// Documentbase retrieval required definition.
150static NS_DEFINE_IID (kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID);
151#endif
152
153// Data directory for plugin.
154static gchar* data_directory = NULL;
155
156// Fully-qualified appletviewer executable.
157static gchar* appletviewer_executable = NULL;
158
159// Applet viewer input channel (needs to be static because it is used in plugin_in_pipe_callback)
160static GIOChannel* in_from_appletviewer = NULL;
161
162// Applet viewer input pipe name.
163gchar* in_pipe_name;
164
165// Applet viewer input watch source.
166gint in_watch_source;
167
168// Applet viewer output pipe name.
169gchar* out_pipe_name;
170
171// Applet viewer output watch source.
172gint out_watch_source;
173
174// Thread ID of plug-in thread
175pthread_t itnp_plugin_thread_id;
176
177// Mutex to lock async call queue
178pthread_mutex_t pluginAsyncCallMutex;
179
180// Applet viewer output channel.
181GIOChannel* out_to_appletviewer;
182
183// Tracks jvm status
184gboolean jvm_up = FALSE;
185
186// Keeps track of initialization. NP_Initialize should only be
187// called once.
188gboolean initialized = false;
189
190// browser functions into mozilla
191NPNetscapeFuncs browser_functions;
192
193// Various message buses carrying information to/from Java, and internally
194MessageBus* plugin_to_java_bus;
195MessageBus* java_to_plugin_bus;
196//MessageBus* internal_bus = new MessageBus();
197
198// Processor for plugin requests
199PluginRequestProcessor* plugin_req_proc;
200
201// Sends messages to Java over the bus
202JavaMessageSender* java_req_proc;
203
204#if MOZILLA_VERSION_COLLAPSED < 1090100
205// Documentbase retrieval type-punning union.
206typedef union
207{
208 void** void_field;
209 nsIPluginTagInfo2** info_field;
210} info_union;
211#endif
212
213// Static instance helper functions.
214// Have the browser allocate a new ITNPPluginData structure.
215static void plugin_data_new (ITNPPluginData** data);
216// Retrieve the current document's documentbase.
217static gchar* plugin_get_documentbase (NPP instance);
218// Notify the user that the appletviewer is not installed correctly.
219static void plugin_display_failure_dialog ();
220// Callback used to monitor input pipe status.
221static gboolean plugin_in_pipe_callback (GIOChannel* source,
222 GIOCondition condition,
223 gpointer plugin_data);
224// Callback used to monitor output pipe status.
225static gboolean plugin_out_pipe_callback (GIOChannel* source,
226 GIOCondition condition,
227 gpointer plugin_data);
228static NPError plugin_start_appletviewer (ITNPPluginData* data);
229static gchar* plugin_create_applet_tag (int16_t argc, char* argn[],
230 char* argv[]);
231static void plugin_stop_appletviewer ();
232// Uninitialize ITNPPluginData structure
233static void plugin_data_destroy (NPP instance);
234
235NPError get_cookie_info(const char* siteAddr, char** cookieString, uint32_t* len);
236NPError get_proxy_info(const char* siteAddr, char** proxy, uint32_t* len);
237void consume_message(gchar* message);
238void start_jvm_if_needed();
239static void appletviewer_monitor(GPid pid, gint status, gpointer data);
240void plugin_send_initialization_message(char* instance, gulong handle,
241 int width, int height,
242 char* url);
243
244// Global instance counter.
245// Mutex to protect plugin_instance_counter.
246static GMutex* plugin_instance_mutex = NULL;
247// A global variable for reporting GLib errors. This must be free'd
248// and set to NULL after each use.
249static GError* channel_error = NULL;
250
251static GHashTable* instance_to_id_map = g_hash_table_new(NULL, NULL);
252static GHashTable* id_to_instance_map = g_hash_table_new(NULL, NULL);
253static gint instance_counter = 1;
254static GPid appletviewer_pid = -1;
255static guint appletviewer_watch_id = -1;
256
257int plugin_debug = getenv ("ICEDTEAPLUGIN_DEBUG") != NULL;
258int plugin_debug_suspend = (getenv("ICEDTEAPLUGIN_DEBUG") != NULL) &&
259 (strcmp(getenv("ICEDTEAPLUGIN_DEBUG"), "suspend") == 0);
260
261pthread_cond_t cond_message_available = PTHREAD_COND_INITIALIZER;
262
263// Functions prefixed by ITNP_ are instance functions. They are called
264// by the browser and operate on instances of ITNPPluginData.
265// Functions prefixed by plugin_ are static helper functions.
266// Functions prefixed by NP_ are factory functions. They are called
267// by the browser and provide functionality needed to create plugin
268// instances.
269
270// INSTANCE FUNCTIONS
271
272// Creates a new icedtea np plugin instance. This function creates a
273// ITNPPluginData* and stores it in instance->pdata. The following
274// ITNPPluginData fiels are initialized: instance_id, in_pipe_name,
275// in_from_appletviewer, in_watch_source, out_pipe_name,
276// out_to_appletviewer, out_watch_source, appletviewer_mutex, owner,
277// appletviewer_alive. In addition two pipe files are created. All
278// of those fields must be properly destroyed, and the pipes deleted,
279// by ITNP_Destroy. If an error occurs during initialization then this
280// function will free anything that's been allocated so far, set
281// instance->pdata to NULL and return an error code.
282NPError
283ITNP_New (NPMIMEType pluginType, NPP instance, uint16_t mode,
284 int16_t argc, char* argn[], char* argv[],
285 NPSavedData* saved)
286{
287 PLUGIN_DEBUG("ITNP_New\n");
288
289 static NPObject *window_ptr;
290 NPIdentifier identifier;
291 NPVariant member_ptr;
292 browser_functions.getvalue(instance, NPNVWindowNPObject, &window_ptr);
293 identifier = browser_functions.getstringidentifier("document");
294 if (!browser_functions.hasproperty(instance, window_ptr, identifier))
295 {
296 printf("%s not found!\n", "document");
297 }
298 browser_functions.getproperty(instance, window_ptr, identifier, &member_ptr);
299
300 PLUGIN_DEBUG("Got variant %p\n", &member_ptr);
301
302
303 NPError np_error = NPERR_NO_ERROR;
304 ITNPPluginData* data = NULL;
305
306 gchar* documentbase = NULL;
307 gchar* read_message = NULL;
308 gchar* applet_tag = NULL;
309 gchar* cookie_info = NULL;
310
311 NPObject* npPluginObj = NULL;
312
313 if (!instance)
314 {
315 PLUGIN_ERROR ("Browser-provided instance pointer is NULL.");
316 np_error = NPERR_INVALID_INSTANCE_ERROR;
317 goto cleanup_done;
318 }
319
320 // data
321 plugin_data_new (&data);
322 if (data == NULL)
323 {
324 PLUGIN_ERROR ("Failed to allocate plugin data.");
325 np_error = NPERR_OUT_OF_MEMORY_ERROR;
326 goto cleanup_done;
327 }
328
329 // start the jvm if needed
330 start_jvm_if_needed();
331
332 // Initialize data->instance_id.
333 //
334 // instance_id should be unique for this process so we use a
335 // combination of getpid and plugin_instance_counter.
336 //
337 // Critical region. Reference and increment plugin_instance_counter
338 // global.
339 g_mutex_lock (plugin_instance_mutex);
340
341 // data->instance_id
342 data->instance_id = g_strdup_printf ("%d",
343 instance_counter);
344
345 g_mutex_unlock (plugin_instance_mutex);
346
347 // data->appletviewer_mutex
348 data->appletviewer_mutex = g_mutex_new ();
349
350 g_mutex_lock (data->appletviewer_mutex);
351
352 // Documentbase retrieval.
353 documentbase = plugin_get_documentbase (instance);
354 if (documentbase && argc != 0)
355 {
356 // Send applet tag message to appletviewer.
357 applet_tag = plugin_create_applet_tag (argc, argn, argv);
358
359 data->applet_tag = (gchar*) malloc(strlen(applet_tag)*sizeof(gchar) + strlen(documentbase)*sizeof(gchar) + 32);
360 g_sprintf(data->applet_tag, "tag %s %s", documentbase, applet_tag);
361
362 data->is_applet_instance = true;
363 }
364
365 if (argc == 0)
366 {
367 data->is_applet_instance = false;
368 }
369
370 g_mutex_unlock (data->appletviewer_mutex);
371
372 // If initialization succeeded entirely then we store the plugin
373 // data in the instance structure and return. Otherwise we free the
374 // data we've allocated so far and set instance->pdata to NULL.
375
376 // Set back-pointer to owner instance.
377 data->owner = instance;
378
379 // source of this instance
380 // don't use documentbase, it is cleared later
381 data->source = plugin_get_documentbase(instance);
382
383 instance->pdata = data;
384
385 goto cleanup_done;
386
387 cleanup_appletviewer_mutex:
388 g_free (data->appletviewer_mutex);
389 data->appletviewer_mutex = NULL;
390
391 // cleanup_instance_string:
392 g_free (data->instance_id);
393 data->instance_id = NULL;
394
395 // cleanup applet tag:
396 g_free (data->applet_tag);
397 data->applet_tag = NULL;
398
399 // cleanup_data:
400 // Eliminate back-pointer to plugin instance.
401 data->owner = NULL;
402 (*browser_functions.memfree) (data);
403 data = NULL;
404
405 // Initialization failed so return a NULL pointer for the browser
406 // data.
407 instance->pdata = NULL;
408
409 cleanup_done:
410 g_free (applet_tag);
411 applet_tag = NULL;
412 g_free (read_message);
413 read_message = NULL;
414 g_free (documentbase);
415 documentbase = NULL;
416
417 // store an identifier for this plugin
418 PLUGIN_DEBUG("Mapping id %d and instance %p\n", instance_counter, instance);
419 g_hash_table_insert(instance_to_id_map, instance, GINT_TO_POINTER(instance_counter));
420 g_hash_table_insert(id_to_instance_map, GINT_TO_POINTER(instance_counter), instance);
421 instance_counter++;
422
423 PLUGIN_DEBUG ("ITNP_New return\n");
424
425 return np_error;
426}
427
428// Starts the JVM if it is not already running
429void start_jvm_if_needed()
430{
431
432 // This is asynchronized function. It must
433 // have exclusivity when running.
434
435 GMutex *vm_start_mutex = g_mutex_new();
436 g_mutex_lock(vm_start_mutex);
437
438 PLUGIN_DEBUG("Checking JVM status...\n");
439
440 // If the jvm is already up, do nothing
441 if (jvm_up)
442 {
443 PLUGIN_DEBUG("JVM is up. Returning.\n");
444 return;
445 }
446
447 PLUGIN_DEBUG("No JVM is running. Attempting to start one...\n");
448
449 NPError np_error = NPERR_NO_ERROR;
450 ITNPPluginData* data = NULL;
451
452 // Create appletviewer-to-plugin pipe which we refer to as the input
453 // pipe.
454
455 // in_pipe_name
456 in_pipe_name = g_strdup_printf ("%s/%d-icedteanp-appletviewer-to-plugin",
457 data_directory, getpid());
458 if (!in_pipe_name)
459 {
460 PLUGIN_ERROR ("Failed to create input pipe name.");
461 np_error = NPERR_OUT_OF_MEMORY_ERROR;
462 // If in_pipe_name is NULL then the g_free at
463 // cleanup_in_pipe_name will simply return.
464 goto cleanup_in_pipe_name;
465 }
466
467 // clean up any older pip
468 unlink (in_pipe_name);
469
470 PLUGIN_DEBUG ("ITNP_New: creating input fifo: %s\n", in_pipe_name);
471 if (mkfifo (in_pipe_name, 0600) == -1 && errno != EEXIST)
472 {
473 PLUGIN_ERROR_TWO ("Failed to create input pipe", strerror (errno));
474 np_error = NPERR_GENERIC_ERROR;
475 goto cleanup_in_pipe_name;
476 }
477 PLUGIN_DEBUG ("ITNP_New: created input fifo: %s\n", in_pipe_name);
478
479 // Create plugin-to-appletviewer pipe which we refer to as the
480 // output pipe.
481
482 // out_pipe_name
483 out_pipe_name = g_strdup_printf ("%s/%d-icedteanp-plugin-to-appletviewer",
484 data_directory, getpid());
485
486 if (!out_pipe_name)
487 {
488 PLUGIN_ERROR ("Failed to create output pipe name.");
489 np_error = NPERR_OUT_OF_MEMORY_ERROR;
490 goto cleanup_out_pipe_name;
491 }
492
493 // clean up any older pip
494 unlink (out_pipe_name);
495
496 PLUGIN_DEBUG ("ITNP_New: creating output fifo: %s\n", out_pipe_name);
497 if (mkfifo (out_pipe_name, 0600) == -1 && errno != EEXIST)
498 {
499 PLUGIN_ERROR_TWO ("Failed to create output pipe", strerror (errno));
500 np_error = NPERR_GENERIC_ERROR;
501 goto cleanup_out_pipe_name;
502 }
503 PLUGIN_DEBUG ("ITNP_New: created output fifo: %s\n", out_pipe_name);
504
505 // Start a separate appletviewer process for each applet, even if
506 // there are multiple applets in the same page. We may need to
507 // change this behaviour if we find pages with multiple applets that
508 // rely on being run in the same VM.
509
510 np_error = plugin_start_appletviewer (data);
511
512 // Create plugin-to-appletviewer channel. The default encoding for
513 // the file is UTF-8.
514 // out_to_appletviewer
515 out_to_appletviewer = g_io_channel_new_file (out_pipe_name,
516 "w", &channel_error);
517 if (!out_to_appletviewer)
518 {
519 if (channel_error)
520 {
521 PLUGIN_ERROR_TWO ("Failed to create output channel",
522 channel_error->message);
523 g_error_free (channel_error);
524 channel_error = NULL;
525 }
526 else
527 PLUGIN_ERROR ("Failed to create output channel");
528
529 np_error = NPERR_GENERIC_ERROR;
530 goto cleanup_out_to_appletviewer;
531 }
532
533 // Watch for hangup and error signals on the output pipe.
534 out_watch_source =
535 g_io_add_watch (out_to_appletviewer,
536 (GIOCondition) (G_IO_ERR | G_IO_HUP),
537 plugin_out_pipe_callback, (gpointer) out_to_appletviewer);
538
539 // Create appletviewer-to-plugin channel. The default encoding for
540 // the file is UTF-8.
541 // in_from_appletviewer
542 in_from_appletviewer = g_io_channel_new_file (in_pipe_name,
543 "r", &channel_error);
544 if (!in_from_appletviewer)
545 {
546 if (channel_error)
547 {
548 PLUGIN_ERROR_TWO ("Failed to create input channel",
549 channel_error->message);
550 g_error_free (channel_error);
551 channel_error = NULL;
552 }
553 else
554 PLUGIN_ERROR ("Failed to create input channel");
555
556 np_error = NPERR_GENERIC_ERROR;
557 goto cleanup_in_from_appletviewer;
558 }
559
560 // Watch for hangup and error signals on the input pipe.
561 in_watch_source =
562 g_io_add_watch (in_from_appletviewer,
563 (GIOCondition) (G_IO_IN | G_IO_ERR | G_IO_HUP),
564 plugin_in_pipe_callback, (gpointer) in_from_appletviewer);
565
566 jvm_up = TRUE;
567
568 goto done;
569
570 // Free allocated data
571
572 cleanup_in_watch_source:
573 // Removing a source is harmless if it fails since it just means the
574 // source has already been removed.
575 g_source_remove (in_watch_source);
576 in_watch_source = 0;
577
578 cleanup_in_from_appletviewer:
579 if (in_from_appletviewer)
580 g_io_channel_unref (in_from_appletviewer);
581 in_from_appletviewer = NULL;
582
583 // cleanup_out_watch_source:
584 g_source_remove (out_watch_source);
585 out_watch_source = 0;
586
587 cleanup_out_to_appletviewer:
588 if (out_to_appletviewer)
589 g_io_channel_unref (out_to_appletviewer);
590 out_to_appletviewer = NULL;
591
592 // cleanup_out_pipe:
593 // Delete output pipe.
594 PLUGIN_DEBUG ("ITNP_New: deleting input fifo: %s\n", in_pipe_name);
595 unlink (out_pipe_name);
596 PLUGIN_DEBUG ("ITNP_New: deleted input fifo: %s\n", in_pipe_name);
597
598 cleanup_out_pipe_name:
599 g_free (out_pipe_name);
600 out_pipe_name = NULL;
601
602 // cleanup_in_pipe:
603 // Delete input pipe.
604 PLUGIN_DEBUG ("ITNP_New: deleting output fifo: %s\n", out_pipe_name);
605 unlink (in_pipe_name);
606 PLUGIN_DEBUG ("ITNP_New: deleted output fifo: %s\n", out_pipe_name);
607
608 cleanup_in_pipe_name:
609 g_free (in_pipe_name);
610 in_pipe_name = NULL;
611
612 done:
613
614 // Now other threads may re-enter.. unlock the mutex
615 g_mutex_unlock(vm_start_mutex);
616
617}
618
619NPError
620ITNP_GetValue (NPP instance, NPPVariable variable, void* value)
621{
622 PLUGIN_DEBUG ("ITNP_GetValue\n");
623
624 NPError np_error = NPERR_NO_ERROR;
625
626 switch (variable)
627 {
628 // This plugin needs XEmbed support.
629 case NPPVpluginNeedsXEmbed:
630 {
631 PLUGIN_DEBUG ("ITNP_GetValue: returning TRUE for NeedsXEmbed.\n");
632 bool* bool_value = (bool*) value;
633 *bool_value = true;
634 }
635 break;
636 case NPPVpluginScriptableNPObject:
637 {
638 *(NPObject **)value = get_scriptable_object(instance);
639 }
640 break;
641 default:
642 PLUGIN_ERROR ("Unknown plugin value requested.");
643 np_error = NPERR_GENERIC_ERROR;
644 break;
645 }
646
647 PLUGIN_DEBUG ("ITNP_GetValue return\n");
648
649 return np_error;
650}
651
652NPError
653ITNP_Destroy (NPP instance, NPSavedData** save)
654{
655 PLUGIN_DEBUG ("ITNP_Destroy %p\n", instance);
656
657 ITNPPluginData* data = (ITNPPluginData*) instance->pdata;
658
659 int id = get_id_from_instance(instance);
660
661 // Let Java know that this applet needs to be destroyed
662 gchar* msg = (gchar*) g_malloc(512*sizeof(gchar)); // 512 is more than enough. We need < 100
663 g_sprintf(msg, "instance %d destroy", id);
664 plugin_send_message_to_appletviewer(msg);
665 g_free(msg);
666 msg = NULL;
667
668 if (data)
669 {
670 // Free plugin data.
671 plugin_data_destroy (instance);
672 }
673
674 g_hash_table_remove(instance_to_id_map, instance);
675 g_hash_table_remove(id_to_instance_map, GINT_TO_POINTER(id));
676
677 IcedTeaPluginUtilities::invalidateInstance(instance);
678
679 PLUGIN_DEBUG ("ITNP_Destroy return\n");
680
681 return NPERR_NO_ERROR;
682}
683
684NPError
685ITNP_SetWindow (NPP instance, NPWindow* window)
686{
687 PLUGIN_DEBUG ("ITNP_SetWindow\n");
688
689 if (instance == NULL)
690 {
691 PLUGIN_ERROR ("Invalid instance.");
692
693 return NPERR_INVALID_INSTANCE_ERROR;
694 }
695
696 gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance);
697
698 gint id = 0;
699 if (id_ptr)
700 {
701 id = GPOINTER_TO_INT(id_ptr);
702 }
703
704 ITNPPluginData* data = (ITNPPluginData*) instance->pdata;
705
706 // Simply return if we receive a NULL window.
707 if ((window == NULL) || (window->window == NULL))
708 {
709 PLUGIN_DEBUG ("ITNP_SetWindow: got NULL window.\n");
710
711 return NPERR_NO_ERROR;
712 }
713
714 if (data->window_handle)
715 {
716 // The window already exists.
717 if (data->window_handle == window->window)
718 {
719 // The parent window is the same as in previous calls.
720 PLUGIN_DEBUG ("ITNP_SetWindow: window already exists.\n");
721
722 // Critical region. Read data->appletviewer_mutex and send
723 // a message to the appletviewer.
724 g_mutex_lock (data->appletviewer_mutex);
725
726 if (jvm_up)
727 {
728 gboolean dim_changed = FALSE;
729
730 // The window is the same as it was for the last
731 // SetWindow call.
732 if (window->width != data->window_width)
733 {
734 PLUGIN_DEBUG ("ITNP_SetWindow: window width changed.\n");
735 // The width of the plugin window has changed.
736
737 // Store the new width.
738 data->window_width = window->width;
739 dim_changed = TRUE;
740 }
741
742 if (window->height != data->window_height)
743 {
744 PLUGIN_DEBUG ("ITNP_SetWindow: window height changed.\n");
745 // The height of the plugin window has changed.
746
747 // Store the new height.
748 data->window_height = window->height;
749
750 dim_changed = TRUE;
751 }
752
753 if (dim_changed) {
754 gchar* message = g_strdup_printf ("instance %d width %d height %d",
755 id, window->width, window->height);
756 plugin_send_message_to_appletviewer (message);
757 g_free (message);
758 message = NULL;
759 }
760
761
762 }
763 else
764 {
765 // The appletviewer is not running.
766 PLUGIN_DEBUG ("ITNP_SetWindow: appletviewer is not running.\n");
767 }
768
769 g_mutex_unlock (data->appletviewer_mutex);
770 }
771 else
772 {
773 // The parent window has changed. This branch does run but
774 // doing nothing in response seems to be sufficient.
775 PLUGIN_DEBUG ("ITNP_SetWindow: parent window changed.\n");
776 }
777 }
778 else
779 {
780
781 // Else this is initialization
782 PLUGIN_DEBUG ("ITNP_SetWindow: setting window.\n");
783
784 // Critical region. Send messages to appletviewer.
785 g_mutex_lock (data->appletviewer_mutex);
786
787 // Store the window handle and dimensions
788 data->window_handle = window->window;
789 data->window_width = window->width;
790 data->window_height = window->height;
791
792 // Now we have everything. Send this data to the Java side
793 plugin_send_initialization_message(
794 data->instance_id, (gulong) data->window_handle,
795 data->window_width, data->window_height, data->applet_tag);
796
797 g_mutex_unlock (data->appletviewer_mutex);
798
799 }
800
801 PLUGIN_DEBUG ("ITNP_SetWindow return\n");
802
803 return NPERR_NO_ERROR;
804}
805
806NPError
807ITNP_NewStream (NPP instance, NPMIMEType type, NPStream* stream,
808 NPBool seekable, uint16_t* stype)
809{
810 PLUGIN_DEBUG ("ITNP_NewStream\n");
811
812 PLUGIN_DEBUG ("ITNP_NewStream return\n");
813
814 return NPERR_GENERIC_ERROR;
815}
816
817void
818ITNP_StreamAsFile (NPP instance, NPStream* stream, const char* filename)
819{
820 PLUGIN_DEBUG ("ITNP_StreamAsFile\n");
821
822 PLUGIN_DEBUG ("ITNP_StreamAsFile return\n");
823}
824
825NPError
826ITNP_DestroyStream (NPP instance, NPStream* stream, NPReason reason)
827{
828 PLUGIN_DEBUG ("ITNP_DestroyStream\n");
829
830 PLUGIN_DEBUG ("ITNP_DestroyStream return\n");
831
832 return NPERR_NO_ERROR;
833}
834
835int32_t
836ITNP_WriteReady (NPP instance, NPStream* stream)
837{
838 PLUGIN_DEBUG ("ITNP_WriteReady\n");
839
840 PLUGIN_DEBUG ("ITNP_WriteReady return\n");
841
842 return 0;
843}
844
845int32_t
846ITNP_Write (NPP instance, NPStream* stream, int32_t offset, int32_t len,
847 void* buffer)
848{
849 PLUGIN_DEBUG ("ITNP_Write\n");
850
851 PLUGIN_DEBUG ("ITNP_Write return\n");
852
853 return 0;
854}
855
856void
857ITNP_Print (NPP instance, NPPrint* platformPrint)
858{
859 PLUGIN_DEBUG ("ITNP_Print\n");
860
861 PLUGIN_DEBUG ("ITNP_Print return\n");
862}
863
864int16_t
865ITNP_HandleEvent (NPP instance, void* event)
866{
867 PLUGIN_DEBUG ("ITNP_HandleEvent\n");
868
869 PLUGIN_DEBUG ("ITNP_HandleEvent return\n");
870
871 return 0;
872}
873
874void
875ITNP_URLNotify (NPP instance, const char* url, NPReason reason,
876 void* notifyData)
877{
878 PLUGIN_DEBUG ("ITNP_URLNotify\n");
879
880 PLUGIN_DEBUG ("ITNP_URLNotify return\n");
881}
882
883NPError
884get_cookie_info(const char* siteAddr, char** cookieString, uint32_t* len)
885{
886#if MOZILLA_VERSION_COLLAPSED < 1090100
887 nsresult rv;
888 nsCOMPtr<nsIScriptSecurityManager> sec_man =
889 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
890
891 if (!sec_man) {
892 return NPERR_GENERIC_ERROR;
893 }
894
895 nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv);
896
897 if (NS_FAILED(rv) || !io_svc) {
898 return NPERR_GENERIC_ERROR;
899 }
900
901 nsCOMPtr<nsIURI> uri;
902 io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri));
903
904 nsCOMPtr<nsICookieService> cookie_svc = do_GetService("@mozilla.org/cookieService;1", &rv);
905
906 if (NS_FAILED(rv) || !cookie_svc) {
907 return NPERR_GENERIC_ERROR;
908 }
909
910 rv = cookie_svc->GetCookieString(uri, NULL, cookieString);
911
912 if (NS_FAILED(rv) || !*cookieString) {
913 return NPERR_GENERIC_ERROR;
914 }
915
916#else
917
918 // getvalueforurl needs an NPP instance. Quite frankly, there is no easy way
919 // to know which instance needs the information, as applets on Java side can
920 // be multi-threaded and the thread making a proxy.cookie request cannot be
921 // easily tracked.
922
923 // Fortunately, XULRunner does not care about the instance as long as it is
924 // valid. So we just pick the first valid one and use it. Proxy/Cookie
925 // information is not instance specific anyway, it is URL specific.
926
927 if (browser_functions.getvalueforurl)
928 {
929 GHashTableIter iter;
930 gpointer id, instance;
931
932 g_hash_table_iter_init (&iter, instance_to_id_map);
933 g_hash_table_iter_next (&iter, &instance, &id);
934
935 return browser_functions.getvalueforurl((NPP) instance, NPNURLVCookie, siteAddr, cookieString, len);
936 } else
937 {
938 return NPERR_GENERIC_ERROR;
939 }
940
941#endif
942
943 return NPERR_NO_ERROR;
944}
945
946// HELPER FUNCTIONS
947
948static void
949plugin_data_new (ITNPPluginData** data)
950{
951 PLUGIN_DEBUG ("plugin_data_new\n");
952
953 *data = (ITNPPluginData*)
954 (*browser_functions.memalloc) (sizeof (struct ITNPPluginData));
955
956 // appletviewer_alive is false until the applet viewer is spawned.
957 if (*data)
958 memset (*data, 0, sizeof (struct ITNPPluginData));
959
960 PLUGIN_DEBUG ("plugin_data_new return\n");
961}
962
963
964
965// Documentbase retrieval. This function gets the current document's
966// documentbase. This function relies on browser-private data so it
967// will only work when the plugin is loaded in a Mozilla-based
968// browser. We could not find a way to retrieve the documentbase
969// using the original Netscape plugin API so we use the XPCOM API
970// instead.
971#if MOZILLA_VERSION_COLLAPSED < 1090100
972static gchar*
973plugin_get_documentbase (NPP instance)
974{
975 PLUGIN_DEBUG ("plugin_get_documentbase\n");
976
977 nsIPluginInstance* xpcom_instance = NULL;
978 nsIPluginInstancePeer* peer = NULL;
979 nsresult result = 0;
980 nsIPluginTagInfo2* pluginTagInfo2 = NULL;
981 info_union u = { NULL };
982 char const* documentbase = NULL;
983 gchar* documentbase_copy = NULL;
984
985 xpcom_instance = (nsIPluginInstance*) (instance->ndata);
986 if (!xpcom_instance)
987 {
988 PLUGIN_ERROR ("xpcom_instance is NULL.");
989 goto cleanup_done;
990 }
991
992 xpcom_instance->GetPeer (&peer);
993 if (!peer)
994 {
995 PLUGIN_ERROR ("peer is NULL.");
996 goto cleanup_done;
997 }
998
999 u.info_field = &pluginTagInfo2;
1000
1001 result = peer->QueryInterface (kIPluginTagInfo2IID,
1002 u.void_field);
1003 if (result || !pluginTagInfo2)
1004 {
1005 PLUGIN_ERROR ("pluginTagInfo2 retrieval failed.");
1006 goto cleanup_peer;
1007 }
1008
1009 pluginTagInfo2->GetDocumentBase (&documentbase);
1010
1011 if (!documentbase)
1012 {
1013 // NULL => dummy instantiation for LiveConnect
1014 goto cleanup_plugintaginfo2;
1015 }
1016
1017 documentbase_copy = g_strdup (documentbase);
1018
1019 // Release references.
1020 cleanup_plugintaginfo2:
1021 NS_RELEASE (pluginTagInfo2);
1022
1023 cleanup_peer:
1024 NS_RELEASE (peer);
1025
1026 cleanup_done:
1027 PLUGIN_DEBUG ("plugin_get_documentbase return\n");
1028
1029 PLUGIN_DEBUG("plugin_get_documentbase returning: %s\n", documentbase_copy);
1030 return documentbase_copy;
1031}
1032#else
1033static gchar*
1034plugin_get_documentbase (NPP instance)
1035{
1036 PLUGIN_DEBUG ("plugin_get_documentbase\n");
1037
1038 char const* documentbase = NULL;
1039 gchar* documentbase_copy = NULL;
1040
1041 // FIXME: This method is not ideal, but there are no known NPAPI call
1042 // for this. See thread for more information:
1043 // http://www.mail-archive.com/chromium-dev@googlegroups.com/msg04844.html
1044
1045 // Additionally, since it is insecure, we cannot use it for making
1046 // security decisions.
1047 NPObject* window;
1048 browser_functions.getvalue(instance, NPNVWindowNPObject, &window);
1049
1050 NPVariant location;
1051 NPIdentifier location_id = browser_functions.getstringidentifier("location");
1052 browser_functions.getproperty(instance, window, location_id, &location);
1053
1054 NPVariant href;
1055 NPIdentifier href_id = browser_functions.getstringidentifier("href");
1056 browser_functions.getproperty(instance, NPVARIANT_TO_OBJECT(location),
1057 href_id, &href);
1058
1059 // Strip everything after the last "/"
1060#if MOZILLA_VERSION_COLLAPSED < 1090200
1061 gchar** parts = g_strsplit (NPVARIANT_TO_STRING(href).utf8characters, "/", -1);
1062#else
1063 gchar** parts = g_strsplit (NPVARIANT_TO_STRING(href).UTF8Characters, "/", -1);
1064#endif
1065 guint parts_sz = g_strv_length (parts);
1066
1067 std::string location_str;
1068 for (int i=0; i < parts_sz - 1; i++)
1069 {
1070 location_str += parts[i];
1071 location_str += "/";
1072 }
1073
1074 documentbase_copy = g_strdup (location_str.c_str());
1075
1076 // Release references.
1077 browser_functions.releasevariantvalue(&href);
1078 browser_functions.releasevariantvalue(&location);
1079 cleanup_done:
1080 PLUGIN_DEBUG ("plugin_get_documentbase return\n");
1081 PLUGIN_DEBUG("plugin_get_documentbase returning: %s\n", documentbase_copy);
1082
1083 return documentbase_copy;
1084}
1085#endif
1086
1087// This function displays an error message if the appletviewer has not
1088// been installed correctly.
1089static void
1090plugin_display_failure_dialog ()
1091{
1092#ifdef __OS2__
1093 gchar *msg = NULL;
1094
1095 PLUGIN_DEBUG ("plugin_display_failure_dialog\n");
1096
1097 msg = g_strdup_printf (FAILURE_MESSAGE, appletviewer_executable);
1098 WinMessageBox (HWND_DESKTOP, HWND_DESKTOP,
1099 msg, "Error", 0, MB_ERROR | MB_OK | MB_MOVEABLE);
1100 g_free(msg);
1101#else
1102 GtkWidget* dialog = NULL;
1103
1104 PLUGIN_DEBUG ("plugin_display_failure_dialog\n");
1105
1106 dialog = gtk_message_dialog_new (NULL,
1107 GTK_DIALOG_DESTROY_WITH_PARENT,
1108 GTK_MESSAGE_ERROR,
1109 GTK_BUTTONS_CLOSE,
1110 FAILURE_MESSAGE,
1111 appletviewer_executable);
1112 gtk_widget_show_all (dialog);
1113 gtk_dialog_run (GTK_DIALOG (dialog));
1114 gtk_widget_destroy (dialog);
1115#endif
1116
1117 PLUGIN_DEBUG ("plugin_display_failure_dialog return\n");
1118}
1119
1120
1121
1122// plugin_in_pipe_callback is called when data is available on the
1123// input pipe, or when the appletviewer crashes or is killed. It may
1124// be called after data has been destroyed in which case it simply
1125// returns FALSE to remove itself from the glib main loop.
1126static gboolean
1127plugin_in_pipe_callback (GIOChannel* source,
1128 GIOCondition condition,
1129 gpointer plugin_data)
1130{
1131 PLUGIN_DEBUG ("plugin_in_pipe_callback\n");
1132
1133 gboolean keep_installed = TRUE;
1134
1135 if (condition & G_IO_IN)
1136 {
1137 gchar* message = NULL;
1138
1139 if (g_io_channel_read_line (in_from_appletviewer,
1140 &message, NULL, NULL,
1141 &channel_error)
1142 != G_IO_STATUS_NORMAL)
1143 {
1144 if (channel_error)
1145 {
1146 PLUGIN_ERROR_TWO ("Failed to read line from input channel",
1147 channel_error->message);
1148 g_error_free (channel_error);
1149 channel_error = NULL;
1150 }
1151 else
1152 PLUGIN_ERROR ("Failed to read line from input channel");
1153 } else
1154 {
1155 consume_message(message);
1156 }
1157
1158 g_free (message);
1159 message = NULL;
1160
1161 keep_installed = TRUE;
1162 }
1163
1164 if (condition & (G_IO_ERR | G_IO_HUP))
1165 {
1166 PLUGIN_DEBUG ("appletviewer has stopped.\n");
1167 keep_installed = FALSE;
1168 }
1169
1170 PLUGIN_DEBUG ("plugin_in_pipe_callback return\n");
1171
1172 return keep_installed;
1173}
1174
1175void consume_message(gchar* message) {
1176
1177 PLUGIN_DEBUG (" PIPE: plugin read: %s\n", message);
1178
1179 if (g_str_has_prefix (message, "instance"))
1180 {
1181
1182 ITNPPluginData* data;
1183 gchar** parts = g_strsplit (message, " ", -1);
1184 guint parts_sz = g_strv_length (parts);
1185
1186 int instance_id = atoi(parts[1]);
1187 NPP instance = (NPP) g_hash_table_lookup(id_to_instance_map,
1188 GINT_TO_POINTER(instance_id));
1189
1190 if (instance_id > 0 && !instance)
1191 {
1192 PLUGIN_DEBUG("Instance %d is not active. Refusing to consume message \"%s\"\n", instance_id, message);
1193 return;
1194 }
1195 else if (instance)
1196 {
1197 data = (ITNPPluginData*) instance->pdata;
1198 }
1199
1200 if (g_str_has_prefix (parts[2], "status"))
1201 {
1202
1203 // clear the "instance X status" parts
1204 sprintf(parts[0], "");
1205 sprintf(parts[1], "");
1206 sprintf(parts[2], "");
1207
1208 // join the rest
1209 gchar* status_message = g_strjoinv(" ", parts);
1210 PLUGIN_DEBUG ("plugin_in_pipe_callback: setting status %s\n", status_message);
1211 (*browser_functions.status) (data->owner, status_message);
1212
1213 g_free(status_message);
1214 status_message = NULL;
1215 }
1216 else if (g_str_has_prefix (parts[1], "internal"))
1217 {
1218 //s->post(message);
1219 }
1220 else
1221 {
1222 // All other messages are posted to the bus, and subscribers are
1223 // expected to take care of them. They better!
1224
1225 java_to_plugin_bus->post(message);
1226 }
1227
1228 g_strfreev (parts);
1229 parts = NULL;
1230 }
1231 else if (g_str_has_prefix (message, "context"))
1232 {
1233 java_to_plugin_bus->post(message);
1234 }
1235 else if (g_str_has_prefix (message, "plugin "))
1236 {
1237 // internal plugin related message
1238 gchar** parts = g_strsplit (message, " ", 5);
1239 if (g_str_has_prefix(parts[1], "PluginProxyInfo"))
1240 {
1241 gchar* proxy;
1242 uint32_t len;
1243
1244 gchar* decoded_url = (gchar*) calloc(strlen(parts[4]) + 1, sizeof(gchar));
1245 IcedTeaPluginUtilities::decodeURL(parts[4], &decoded_url);
1246 PLUGIN_DEBUG("parts[0]=%s, parts[1]=%s, reference, parts[3]=%s, parts[4]=%s -- decoded_url=%s\n", parts[0], parts[1], parts[3], parts[4], decoded_url);
1247
1248 gchar* proxy_info;
1249
1250#if MOZILLA_VERSION_COLLAPSED < 1090100
1251 proxy = (char*) malloc(sizeof(char)*2048);
1252#endif
1253
1254 proxy_info = g_strconcat ("plugin PluginProxyInfo reference ", parts[3], " ", NULL);
1255 if (get_proxy_info(decoded_url, &proxy, &len) == NPERR_NO_ERROR)
1256 {
1257 proxy_info = g_strconcat (proxy_info, proxy, NULL);
1258 }
1259
1260 PLUGIN_DEBUG("Proxy info: %s\n", proxy_info);
1261 plugin_send_message_to_appletviewer(proxy_info);
1262
1263 g_free(decoded_url);
1264 decoded_url = NULL;
1265 g_free(proxy_info);
1266 proxy_info = NULL;
1267
1268#if MOZILLA_VERSION_COLLAPSED < 1090100
1269 g_free(proxy);
1270 proxy = NULL;
1271#endif
1272
1273 } else if (g_str_has_prefix(parts[1], "PluginCookieInfo"))
1274 {
1275 gchar* decoded_url = (gchar*) calloc(strlen(parts[4])+1, sizeof(gchar));
1276 IcedTeaPluginUtilities::decodeURL(parts[4], &decoded_url);
1277
1278 gchar* cookie_info = g_strconcat ("plugin PluginCookieInfo reference ", parts[3], " ", NULL);
1279 gchar* cookie_string;
1280 uint32_t len;
1281 if (get_cookie_info(decoded_url, &cookie_string, &len) == NPERR_NO_ERROR)
1282 {
1283 cookie_info = g_strconcat (cookie_info, cookie_string, NULL);
1284 }
1285
1286 PLUGIN_DEBUG("Cookie info: %s\n", cookie_info);
1287 plugin_send_message_to_appletviewer(cookie_info);
1288
1289 g_free(decoded_url);
1290 decoded_url = NULL;
1291 g_free(cookie_info);
1292 cookie_info = NULL;
1293 }
1294 }
1295 else
1296 {
1297 g_print (" Unable to handle message: %s\n", message);
1298 }
1299}
1300
1301void get_instance_from_id(int id, NPP& instance)
1302{
1303 instance = (NPP) g_hash_table_lookup(id_to_instance_map,
1304 GINT_TO_POINTER(id));
1305}
1306
1307int get_id_from_instance(NPP instance)
1308{
1309 int id = GPOINTER_TO_INT(g_hash_table_lookup(instance_to_id_map,
1310 instance));
1311 PLUGIN_DEBUG("Returning id %d for instance %p\n", id, instance);
1312 return id;
1313}
1314
1315NPError
1316get_proxy_info(const char* siteAddr, char** proxy, uint32_t* len)
1317{
1318#if MOZILLA_VERSION_COLLAPSED < 1090100
1319 nsresult rv;
1320
1321 // Initialize service variables
1322 nsCOMPtr<nsIProtocolProxyService> proxy_svc = do_GetService("@mozilla.org/network/protocol-proxy-service;1", &rv);
1323
1324 if (!proxy_svc) {
1325 printf("Cannot initialize proxy service\n");
1326 return NPERR_GENERIC_ERROR;
1327 }
1328
1329 nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv);
1330
1331 if (NS_FAILED(rv) || !io_svc) {
1332 printf("Cannot initialize io service\n");
1333 return NPERR_GENERIC_ERROR;
1334 }
1335
1336 // uri which needs to be accessed
1337 nsCOMPtr<nsIURI> uri;
1338 io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri));
1339
1340 // find the proxy address if any
1341 nsCOMPtr<nsIProxyInfo> info;
1342 proxy_svc->Resolve(uri, 0, getter_AddRefs(info));
1343
1344 // if there is no proxy found, return immediately
1345 if (!info) {
1346 PLUGIN_DEBUG("%s does not need a proxy\n", siteAddr);
1347 return NPERR_GENERIC_ERROR;
1348 }
1349
1350 // if proxy info is available, extract it
1351 nsCString phost;
1352 PRInt32 pport;
1353 nsCString ptype;
1354
1355 info->GetHost(phost);
1356 info->GetPort(&pport);
1357 info->GetType(ptype);
1358
1359 // resolve the proxy address to an IP
1360 nsCOMPtr<nsIDNSService> dns_svc = do_GetService("@mozilla.org/network/dns-service;1", &rv);
1361
1362 if (!dns_svc) {
1363 printf("Cannot initialize DNS service\n");
1364 return NPERR_GENERIC_ERROR;
1365 }
1366
1367 nsCOMPtr<nsIDNSRecord> record;
1368 dns_svc->Resolve(phost, 0U, getter_AddRefs(record));
1369
1370 // TODO: Add support for multiple ips
1371 nsDependentCString ipAddr;
1372 record->GetNextAddrAsString(ipAddr);
1373
1374 if (!strcmp(ptype.get(), "http"))
1375 {
1376 snprintf(*proxy, sizeof(char)*1024, "%s %s:%d", "PROXY", ipAddr.get(), pport);
1377 } else
1378 {
1379 snprintf(*proxy, sizeof(char)*1024, "%s %s:%d", "SOCKS", ipAddr.get(), pport);
1380 }
1381
1382 *len = strlen(*proxy);
1383
1384 PLUGIN_DEBUG("Proxy info for %s: %s\n", siteAddr, *proxy);
1385
1386#else
1387
1388 if (browser_functions.getvalueforurl)
1389 {
1390
1391 // As in get_cookie_info, we use the first active instance
1392 GHashTableIter iter;
1393 gpointer id, instance;
1394
1395 g_hash_table_iter_init (&iter, instance_to_id_map);
1396 g_hash_table_iter_next (&iter, &instance, &id);
1397
1398 browser_functions.getvalueforurl((NPP) instance, NPNURLVProxy, siteAddr, proxy, len);
1399 } else
1400 {
1401 return NPERR_GENERIC_ERROR;
1402 }
1403#endif
1404
1405 return NPERR_NO_ERROR;
1406}
1407
1408// plugin_out_pipe_callback is called when the appletviewer crashes or
1409// is killed. It may be called after data has been destroyed in which
1410// case it simply returns FALSE to remove itself from the glib main
1411// loop.
1412static gboolean
1413plugin_out_pipe_callback (GIOChannel* source,
1414 GIOCondition condition,
1415 gpointer plugin_data)
1416{
1417 PLUGIN_DEBUG ("plugin_out_pipe_callback\n");
1418
1419 ITNPPluginData* data = (ITNPPluginData*) plugin_data;
1420
1421 PLUGIN_DEBUG ("plugin_out_pipe_callback: appletviewer has stopped.\n");
1422
1423 PLUGIN_DEBUG ("plugin_out_pipe_callback return\n");
1424
1425 return FALSE;
1426}
1427
1428// remove all components from LD_LIBRARY_PATH, which start with
1429// MOZILLA_FIVE_HOME; firefox has its own NSS based security provider,
1430// which conflicts with the one configured in nss.cfg.
1431static gchar*
1432plugin_filter_ld_library_path(gchar *path_old)
1433{
1434 gchar *moz_home = g_strdup (g_getenv ("MOZILLA_FIVE_HOME"));
1435 gchar *moz_prefix;
1436 gchar *path_new;
1437 gchar** components;
1438 int i1, i2;
1439
1440 if (moz_home == NULL || path_old == NULL || strlen (path_old) == 0)
1441 return path_old;
1442 if (g_str_has_suffix (moz_home, "/"))
1443 moz_home[strlen (moz_home - 1)] = '\0';
1444 moz_prefix = g_strconcat (moz_home, "/", NULL);
1445
1446 components = g_strsplit (path_old, ":", -1);
1447 for (i1 = 0, i2 = 0; components[i1] != NULL; i1++)
1448 {
1449 if (g_strcmp0 (components[i1], moz_home) == 0
1450 || g_str_has_prefix (components[i1], moz_home))
1451 components[i2] = components[i1];
1452 else
1453 components[i2++] = components[i1];
1454 }
1455 components[i2] = NULL;
1456
1457 if (i1 > i2)
1458 path_new = g_strjoinv (":", components);
1459 g_strfreev (components);
1460 g_free (moz_home);
1461 g_free (moz_prefix);
1462 g_free (path_old);
1463
1464 if (path_new == NULL || strlen (path_new) == 0)
1465 {
1466 PLUGIN_DEBUG("Unset LD_LIBRARY_PATH\n");
1467 return NULL;
1468 }
1469 else
1470 {
1471 PLUGIN_DEBUG ("Set LD_LIBRARY_PATH: %s\n", path_new);
1472 return path_new;
1473 }
1474}
1475
1476// build the environment to pass to the external plugin process
1477static gchar**
1478plugin_filter_environment(void)
1479{
1480 gchar **var_names = g_listenv();
1481 gchar **new_env = (gchar**) malloc(sizeof(gchar*) * (g_strv_length (var_names) + 1));
1482 int i_var, i_env;
1483
1484 for (i_var = 0, i_env = 0; var_names[i_var] != NULL; i_var++)
1485 {
1486 gchar *env_value = g_strdup (g_getenv (var_names[i_var]));
1487
1488 if (g_str_has_prefix (var_names[i_var], "LD_LIBRARY_PATH"))
1489 env_value = plugin_filter_ld_library_path (env_value);
1490 if (env_value != NULL)
1491 {
1492 new_env[i_env++] = g_strdup_printf ("%s=%s", var_names[i_var], env_value);
1493 g_free (env_value);
1494 }
1495 }
1496 new_env[i_env] = NULL;
1497 return new_env;
1498}
1499
1500static NPError
1501plugin_test_appletviewer ()
1502{
1503 PLUGIN_DEBUG ("plugin_test_appletviewer: %s\n", appletviewer_executable);
1504 NPError error = NPERR_NO_ERROR;
1505
1506 gchar* command_line[3] = { NULL, NULL, NULL };
1507 gchar** environment;
1508
1509 command_line[0] = g_strdup (appletviewer_executable);
1510 command_line[1] = g_strdup("-version");
1511 command_line[2] = NULL;
1512
1513 environment = plugin_filter_environment();
1514
1515 if (!g_spawn_async (NULL, command_line, environment,
1516 (GSpawnFlags) 0,
1517 NULL, NULL, NULL, &channel_error))
1518 {
1519 if (channel_error)
1520 {
1521 PLUGIN_ERROR_TWO ("Failed to spawn applet viewer",
1522 channel_error->message);
1523 g_error_free (channel_error);
1524 channel_error = NULL;
1525 }
1526 else
1527 PLUGIN_ERROR ("Failed to spawn applet viewer");
1528 error = NPERR_GENERIC_ERROR;
1529 }
1530
1531 g_strfreev (environment);
1532
1533 g_free (command_line[0]);
1534 command_line[0] = NULL;
1535 g_free (command_line[1]);
1536 command_line[1] = NULL;
1537 g_free (command_line[2]);
1538 command_line[2] = NULL;
1539
1540 PLUGIN_DEBUG ("plugin_test_appletviewer return\n");
1541 return error;
1542}
1543
1544static NPError
1545plugin_start_appletviewer (ITNPPluginData* data)
1546{
1547 PLUGIN_DEBUG ("plugin_start_appletviewer\n");
1548 NPError error = NPERR_NO_ERROR;
1549
1550 gchar** command_line;
1551 gchar** environment;
1552
1553 int cmd_num = 0;
1554 if (plugin_debug)
1555 {
1556 command_line = (gchar**) malloc(sizeof(gchar*)*11);
1557 command_line[cmd_num++] = g_strdup(appletviewer_executable);
1558 command_line[cmd_num++] = g_strdup_printf(PLUGIN_BOOTCLASSPATH);
1559 // set the classpath to avoid using the default (cwd).
1560 command_line[cmd_num++] = g_strdup("-classpath");
1561 command_line[cmd_num++] = g_strdup_printf("%s/lib/rt.jar", ICEDTEA_WEB_JRE);
1562 command_line[cmd_num++] = g_strdup("-Xdebug");
1563 command_line[cmd_num++] = g_strdup("-Xnoagent");
1564 if (plugin_debug_suspend)
1565 {
1566 command_line[cmd_num++] = g_strdup("-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y");
1567 } else
1568 {
1569 command_line[cmd_num++] = g_strdup("-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n");
1570 }
1571 command_line[cmd_num++] = g_strdup("sun.applet.PluginMain");
1572 command_line[cmd_num++] = g_strdup(out_pipe_name);
1573 command_line[cmd_num++] = g_strdup(in_pipe_name);
1574 command_line[cmd_num] = NULL;
1575 } else
1576 {
1577 command_line = (gchar**) malloc(sizeof(gchar*)*8);
1578 command_line[cmd_num++] = g_strdup(appletviewer_executable);
1579 command_line[cmd_num++] = g_strdup_printf(PLUGIN_BOOTCLASSPATH);
1580 command_line[cmd_num++] = g_strdup("-classpath");
1581 command_line[cmd_num++] = g_strdup_printf("%s/lib/rt.jar", ICEDTEA_WEB_JRE);
1582 command_line[cmd_num++] = g_strdup("sun.applet.PluginMain");
1583 command_line[cmd_num++] = g_strdup(out_pipe_name);
1584 command_line[cmd_num++] = g_strdup(in_pipe_name);
1585 command_line[cmd_num] = NULL;
1586 }
1587
1588 environment = plugin_filter_environment();
1589
1590 if (!g_spawn_async (NULL, command_line, environment,
1591 (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD,
1592 NULL, NULL, &appletviewer_pid, &channel_error))
1593 {
1594 if (channel_error)
1595 {
1596 PLUGIN_ERROR_TWO ("Failed to spawn applet viewer",
1597 channel_error->message);
1598 g_error_free (channel_error);
1599 channel_error = NULL;
1600 }
1601 else
1602 PLUGIN_ERROR ("Failed to spawn applet viewer");
1603 error = NPERR_GENERIC_ERROR;
1604 }
1605
1606 g_strfreev (environment);
1607
1608 for (int i = 0; i < cmd_num; i++) {
1609 g_free (command_line[i]);
1610 command_line[i] = NULL;
1611 }
1612
1613 g_free(command_line);
1614 command_line = NULL;
1615
1616 if (appletviewer_pid)
1617 {
1618 PLUGIN_DEBUG("Initialized VM with pid=%d\n", appletviewer_pid);
1619 appletviewer_watch_id = g_child_watch_add(appletviewer_pid, (GChildWatchFunc) appletviewer_monitor, (gpointer) appletviewer_pid);
1620 }
1621
1622
1623 PLUGIN_DEBUG ("plugin_start_appletviewer return\n");
1624 return error;
1625}
1626
1627/*
1628 * Replaces certain characters (\r, \n, etc) with HTML escape equivalents.
1629 *
1630 * Return string is allocated on the heap. Caller assumes responsibility
1631 * for freeing the memory via free()
1632 */
1633static char*
1634encode_string(char* to_encode)
1635{
1636
1637 // Do nothing for an empty string
1638 if (to_encode == '\0')
1639 return to_encode;
1640
1641 // worst case scenario -> all characters are newlines or
1642 // returns, each of which translates to 5 substitutions
1643 char* encoded = (char*) calloc(((strlen(to_encode)*5)+1), sizeof(char));
1644
1645 strcpy(encoded, "");
1646
1647 for (int i=0; i < strlen(to_encode); i++)
1648 {
1649 if (to_encode[i] == '\r')
1650 encoded = strcat(encoded, "&#13;");
1651 else if (to_encode[i] == '\n')
1652 encoded = strcat(encoded, "&#10;");
1653 else if (to_encode[i] == '>')
1654 encoded = strcat(encoded, "&gt;");
1655 else if (to_encode[i] == '<')
1656 encoded = strcat(encoded, "&lt;");
1657 else if (to_encode[i] == '&')
1658 encoded = strcat(encoded, "&amp;");
1659 else if (to_encode[i] == '"')
1660 encoded = strcat(encoded, "&quot;");
1661 else
1662 {
1663 char* orig_char = (char*) calloc(2, sizeof(char));
1664 orig_char[0] = to_encode[i];
1665 orig_char[1] = '\0';
1666
1667 strcat(encoded, orig_char);
1668
1669 free(orig_char);
1670 orig_char = NULL;
1671 }
1672 }
1673
1674 return encoded;
1675}
1676
1677// Build up the applet tag string that we'll send to the applet
1678// viewer.
1679static gchar*
1680plugin_create_applet_tag (int16_t argc, char* argn[], char* argv[])
1681{
1682 PLUGIN_DEBUG ("plugin_create_applet_tag\n");
1683
1684 gchar* applet_tag = g_strdup ("<EMBED ");
1685 gchar* parameters = g_strdup ("");
1686
1687 for (int16_t i = 0; i < argc; i++)
1688 {
1689 gchar* argn_escaped = encode_string(argn[i]);
1690 gchar* argv_escaped = encode_string(argv[i]);
1691
1692 if (!g_ascii_strcasecmp (argn_escaped, "code"))
1693 {
1694 gchar* code = g_strdup_printf ("CODE=\"%s\" ", argv_escaped);
1695 applet_tag = g_strconcat (applet_tag, code, NULL);
1696 g_free (code);
1697 code = NULL;
1698 }
1699 else if (!g_ascii_strcasecmp (argn_escaped, "java_code"))
1700 {
1701 gchar* java_code = g_strdup_printf ("JAVA_CODE=\"%s\" ", argv_escaped);
1702 applet_tag = g_strconcat (applet_tag, java_code, NULL);
1703 g_free (java_code);
1704 java_code = NULL;
1705 }
1706 else if (!g_ascii_strcasecmp (argn_escaped, "codebase"))
1707 {
1708 gchar* codebase = g_strdup_printf ("CODEBASE=\"%s\" ", argv_escaped);
1709 applet_tag = g_strconcat (applet_tag, codebase, NULL);
1710 g_free (codebase);
1711 codebase = NULL;
1712 }
1713 else if (!g_ascii_strcasecmp (argn_escaped, "java_codebase"))
1714 {
1715 gchar* java_codebase = g_strdup_printf ("JAVA_CODEBASE=\"%s\" ", argv_escaped);
1716 applet_tag = g_strconcat (applet_tag, java_codebase, NULL);
1717 g_free (java_codebase);
1718 java_codebase = NULL;
1719 }
1720 else if (!g_ascii_strcasecmp (argn_escaped, "classid"))
1721 {
1722 gchar* classid = g_strdup_printf ("CLASSID=\"%s\" ", argv_escaped);
1723 applet_tag = g_strconcat (applet_tag, classid, NULL);
1724 g_free (classid);
1725 classid = NULL;
1726 }
1727 else if (!g_ascii_strcasecmp (argn_escaped, "archive"))
1728 {
1729 gchar* archive = g_strdup_printf ("ARCHIVE=\"%s\" ", argv_escaped);
1730 applet_tag = g_strconcat (applet_tag, archive, NULL);
1731 g_free (archive);
1732 archive = NULL;
1733 }
1734 else if (!g_ascii_strcasecmp (argn_escaped, "java_archive"))
1735 {
1736 gchar* java_archive = g_strdup_printf ("JAVA_ARCHIVE=\"%s\" ", argv_escaped);
1737 applet_tag = g_strconcat (applet_tag, java_archive, NULL);
1738 g_free (java_archive);
1739 java_archive = NULL;
1740 }
1741 else if (!g_ascii_strcasecmp (argn_escaped, "width"))
1742 {
1743 gchar* width = g_strdup_printf ("width=\"%s\" ", argv_escaped);
1744 applet_tag = g_strconcat (applet_tag, width, NULL);
1745 g_free (width);
1746 width = NULL;
1747 }
1748 else if (!g_ascii_strcasecmp (argn_escaped, "height"))
1749 {
1750 gchar* height = g_strdup_printf ("height=\"%s\" ", argv_escaped);
1751 applet_tag = g_strconcat (applet_tag, height, NULL);
1752 g_free (height);
1753 height = NULL;
1754 }
1755 else
1756 {
1757
1758 if (argv_escaped != '\0')
1759 {
1760 parameters = g_strconcat (parameters, "<PARAM NAME=\"", argn_escaped,
1761 "\" VALUE=\"", argv_escaped, "\">", NULL);
1762 }
1763 }
1764
1765 free(argn_escaped);
1766 free(argv_escaped);
1767
1768 argn_escaped = NULL;
1769 argv_escaped = NULL;
1770 }
1771
1772 applet_tag = g_strconcat (applet_tag, ">", parameters, "</EMBED>", NULL);
1773
1774 g_free (parameters);
1775 parameters = NULL;
1776
1777 PLUGIN_DEBUG ("plugin_create_applet_tag return\n");
1778
1779 return applet_tag;
1780}
1781
1782// plugin_send_message_to_appletviewer must be called while holding
1783// data->appletviewer_mutex.
1784void
1785plugin_send_message_to_appletviewer (gchar const* message)
1786{
1787 PLUGIN_DEBUG ("plugin_send_message_to_appletviewer\n");
1788
1789 if (jvm_up)
1790 {
1791 gchar* newline_message = NULL;
1792 gsize bytes_written = 0;
1793
1794 // Send message to appletviewer.
1795 newline_message = g_strdup_printf ("%s\n", message);
1796
1797 // g_io_channel_write_chars will return something other than
1798 // G_IO_STATUS_NORMAL if not all the data is written. In that
1799 // case we fail rather than retrying.
1800 if (g_io_channel_write_chars (out_to_appletviewer,
1801 newline_message, -1, &bytes_written,
1802 &channel_error)
1803 != G_IO_STATUS_NORMAL)
1804 {
1805 if (channel_error)
1806 {
1807 PLUGIN_ERROR_TWO ("Failed to write bytes to output channel",
1808 channel_error->message);
1809 g_error_free (channel_error);
1810 channel_error = NULL;
1811 }
1812 else
1813 PLUGIN_ERROR ("Failed to write bytes to output channel");
1814 }
1815
1816 if (g_io_channel_flush (out_to_appletviewer, &channel_error)
1817 != G_IO_STATUS_NORMAL)
1818 {
1819 if (channel_error)
1820 {
1821 PLUGIN_ERROR_TWO ("Failed to flush bytes to output channel",
1822 channel_error->message);
1823 g_error_free (channel_error);
1824 channel_error = NULL;
1825 }
1826 else
1827 PLUGIN_ERROR ("Failed to flush bytes to output channel");
1828 }
1829 g_free (newline_message);
1830 newline_message = NULL;
1831
1832 PLUGIN_DEBUG (" PIPE: plugin wrote: %s\n", message);
1833 }
1834
1835 PLUGIN_DEBUG ("plugin_send_message_to_appletviewer return\n");
1836}
1837
1838/*
1839 * Sends the initialization message (handle/size/url) to the plugin
1840 */
1841void
1842plugin_send_initialization_message(char* instance, gulong handle,
1843 int width, int height, char* url)
1844{
1845 PLUGIN_DEBUG ("plugin_send_initialization_message\n");
1846
1847 gchar *window_message = g_strdup_printf ("instance %s handle %ld width %d height %d %s",
1848 instance, handle, width, height, url);
1849 plugin_send_message_to_appletviewer (window_message);
1850 g_free (window_message);
1851 window_message = NULL;
1852
1853 PLUGIN_DEBUG ("plugin_send_initialization_message return\n");
1854}
1855
1856
1857// Stop the appletviewer process. When this is called the
1858// appletviewer can be in any of three states: running, crashed or
1859// hung. If the appletviewer is running then sending it "shutdown"
1860// will cause it to exit. This will cause
1861// plugin_out_pipe_callback/plugin_in_pipe_callback to be called and
1862// the input and output channels to be shut down. If the appletviewer
1863// has crashed then plugin_out_pipe_callback/plugin_in_pipe_callback
1864// would already have been called and data->appletviewer_alive cleared
1865// in which case this function simply returns. If the appletviewer is
1866// hung then this function will be successful and the input and output
1867// watches will be removed by plugin_data_destroy.
1868// plugin_stop_appletviewer must be called with
1869// data->appletviewer_mutex held.
1870static void
1871plugin_stop_appletviewer ()
1872{
1873 PLUGIN_DEBUG ("plugin_stop_appletviewer\n");
1874
1875 if (jvm_up)
1876 {
1877 // Shut down the appletviewer.
1878 gsize bytes_written = 0;
1879
1880 if (out_to_appletviewer)
1881 {
1882 if (g_io_channel_write_chars (out_to_appletviewer, "shutdown",
1883 -1, &bytes_written, &channel_error)
1884 != G_IO_STATUS_NORMAL)
1885 {
1886 if (channel_error)
1887 {
1888 PLUGIN_ERROR_TWO ("Failed to write shutdown message to"
1889 " appletviewer", channel_error->message);
1890 g_error_free (channel_error);
1891 channel_error = NULL;
1892 }
1893 else
1894 PLUGIN_ERROR ("Failed to write shutdown message to");
1895 }
1896
1897 if (g_io_channel_flush (out_to_appletviewer, &channel_error)
1898 != G_IO_STATUS_NORMAL)
1899 {
1900 if (channel_error)
1901 {
1902 PLUGIN_ERROR_TWO ("Failed to write shutdown message to"
1903 " appletviewer", channel_error->message);
1904 g_error_free (channel_error);
1905 channel_error = NULL;
1906 }
1907 else
1908 PLUGIN_ERROR ("Failed to write shutdown message to");
1909 }
1910
1911 if (g_io_channel_shutdown (out_to_appletviewer,
1912 TRUE, &channel_error)
1913 != G_IO_STATUS_NORMAL)
1914 {
1915 if (channel_error)
1916 {
1917 PLUGIN_ERROR_TWO ("Failed to shut down appletviewer"
1918 " output channel", channel_error->message);
1919 g_error_free (channel_error);
1920 channel_error = NULL;
1921 }
1922 else
1923 PLUGIN_ERROR ("Failed to shut down appletviewer");
1924 }
1925 }
1926
1927 if (in_from_appletviewer)
1928 {
1929 if (g_io_channel_shutdown (in_from_appletviewer,
1930 TRUE, &channel_error)
1931 != G_IO_STATUS_NORMAL)
1932 {
1933 if (channel_error)
1934 {
1935 PLUGIN_ERROR_TWO ("Failed to shut down appletviewer"
1936 " input channel", channel_error->message);
1937 g_error_free (channel_error);
1938 channel_error = NULL;
1939 }
1940 else
1941 PLUGIN_ERROR ("Failed to shut down appletviewer");
1942 }
1943 }
1944 }
1945
1946 jvm_up = FALSE;
1947 sleep(2); /* Needed to prevent crashes during debug (when JDWP port is not freed by the kernel right away) */
1948
1949 PLUGIN_DEBUG ("plugin_stop_appletviewer return\n");
1950}
1951
1952static void appletviewer_monitor(GPid pid, gint status, gpointer data)
1953{
1954 PLUGIN_DEBUG ("appletviewer_monitor\n");
1955 jvm_up = FALSE;
1956 pid = -1;
1957 PLUGIN_DEBUG ("appletviewer_monitor return\n");
1958}
1959
1960static void
1961plugin_data_destroy (NPP instance)
1962{
1963 PLUGIN_DEBUG ("plugin_data_destroy\n");
1964
1965 ITNPPluginData* tofree = (ITNPPluginData*) instance->pdata;
1966
1967 // Remove instance from map
1968 gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance);
1969
1970 if (id_ptr)
1971 {
1972 gint id = GPOINTER_TO_INT(id_ptr);
1973 g_hash_table_remove(instance_to_id_map, instance);
1974 g_hash_table_remove(id_to_instance_map, id_ptr);
1975 }
1976
1977 tofree->window_handle = NULL;
1978 tofree->window_height = 0;
1979 tofree->window_width = 0;
1980
1981 // cleanup_appletviewer_mutex:
1982 g_free (tofree->appletviewer_mutex);
1983 tofree->appletviewer_mutex = NULL;
1984
1985 // cleanup_instance_string:
1986 g_free (tofree->instance_id);
1987 tofree->instance_id = NULL;
1988
1989 // cleanup applet tag
1990 g_free (tofree->applet_tag);
1991 tofree->applet_tag = NULL;
1992
1993 g_free(tofree->source);
1994 tofree->source = NULL;
1995
1996 // cleanup_data:
1997 // Eliminate back-pointer to plugin instance.
1998 tofree->owner = NULL;
1999 (*browser_functions.memfree) (tofree);
2000 tofree = NULL;
2001
2002 PLUGIN_DEBUG ("plugin_data_destroy return\n");
2003}
2004
2005static NPError
2006plugin_get_entry_points (NPPluginFuncs* pluginTable)
2007{
2008 // Ensure that the plugin function table we've received is large
2009 // enough to store the number of functions that we may provide.
2010 if (pluginTable->size < sizeof (NPPluginFuncs))
2011 {
2012 PLUGIN_ERROR ("Invalid plugin function table.");
2013
2014 return NPERR_INVALID_FUNCTABLE_ERROR;
2015 }
2016
2017 // Return to the browser the plugin functions that we implement.
2018 pluginTable->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
2019 pluginTable->size = sizeof (NPPluginFuncs);
2020
2021#if MOZILLA_VERSION_COLLAPSED < 1090100
2022 pluginTable->newp = NewNPP_NewProc (ITNP_New);
2023 pluginTable->destroy = NewNPP_DestroyProc (ITNP_Destroy);
2024 pluginTable->setwindow = NewNPP_SetWindowProc (ITNP_SetWindow);
2025 pluginTable->newstream = NewNPP_NewStreamProc (ITNP_NewStream);
2026 pluginTable->destroystream = NewNPP_DestroyStreamProc (ITNP_DestroyStream);
2027 pluginTable->asfile = NewNPP_StreamAsFileProc (ITNP_StreamAsFile);
2028 pluginTable->writeready = NewNPP_WriteReadyProc (ITNP_WriteReady);
2029 pluginTable->write = NewNPP_WriteProc (ITNP_Write);
2030 pluginTable->print = NewNPP_PrintProc (ITNP_Print);
2031 pluginTable->urlnotify = NewNPP_URLNotifyProc (ITNP_URLNotify);
2032 pluginTable->getvalue = NewNPP_GetValueProc (ITNP_GetValue);
2033#else
2034 pluginTable->newp = NPP_NewProcPtr (ITNP_New);
2035 pluginTable->destroy = NPP_DestroyProcPtr (ITNP_Destroy);
2036 pluginTable->setwindow = NPP_SetWindowProcPtr (ITNP_SetWindow);
2037 pluginTable->newstream = NPP_NewStreamProcPtr (ITNP_NewStream);
2038 pluginTable->destroystream = NPP_DestroyStreamProcPtr (ITNP_DestroyStream);
2039 pluginTable->asfile = NPP_StreamAsFileProcPtr (ITNP_StreamAsFile);
2040 pluginTable->writeready = NPP_WriteReadyProcPtr (ITNP_WriteReady);
2041 pluginTable->write = NPP_WriteProcPtr (ITNP_Write);
2042 pluginTable->print = NPP_PrintProcPtr (ITNP_Print);
2043 pluginTable->urlnotify = NPP_URLNotifyProcPtr (ITNP_URLNotify);
2044 pluginTable->getvalue = NPP_GetValueProcPtr (ITNP_GetValue);
2045#endif
2046
2047 return NPERR_NO_ERROR;
2048}
2049
2050// FACTORY FUNCTIONS
2051
2052// Provides the browser with pointers to the plugin functions that we
2053// implement and initializes a local table with browser functions that
2054// we may wish to call. Called once, after browser startup and before
2055// the first plugin instance is created.
2056// The field 'initialized' is set to true once this function has
2057// finished. If 'initialized' is already true at the beginning of
2058// this function, then it is evident that NP_Initialize has already
2059// been called. There is no need to call this function more than once and
2060// this workaround avoids any duplicate calls.
2061NPError
2062#if defined(_WIN32) || defined (__OS2__)
2063OSCALL NP_Initialize (NPNetscapeFuncs* browserTable)
2064#else
2065OSCALL NP_Initialize (NPNetscapeFuncs* browserTable, NPPluginFuncs* pluginTable)
2066#endif
2067{
2068 PLUGIN_DEBUG ("NP_Initialize\n");
2069
2070#if defined(_WIN32) || defined (__OS2__)
2071 if (browserTable == NULL)
2072 {
2073 PLUGIN_ERROR ("Browser function table is NULL.");
2074
2075 return NPERR_INVALID_FUNCTABLE_ERROR;
2076 }
2077#else
2078 if ((browserTable == NULL) || (pluginTable == NULL))
2079 {
2080 PLUGIN_ERROR ("Browser or plugin function table is NULL.");
2081
2082 return NPERR_INVALID_FUNCTABLE_ERROR;
2083 }
2084#endif
2085
2086 // Ensure that the major version of the plugin API that the browser
2087 // expects is not more recent than the major version of the API that
2088 // we've implemented.
2089 if ((browserTable->version >> 8) > NP_VERSION_MAJOR)
2090 {
2091 PLUGIN_ERROR ("Incompatible version.");
2092
2093 return NPERR_INCOMPATIBLE_VERSION_ERROR;
2094 }
2095
2096 // Ensure that the browser function table is large enough to store
2097 // the number of browser functions that we may use.
2098 if (browserTable->size < sizeof (NPNetscapeFuncs))
2099 {
2100 fprintf (stderr, "ERROR: Invalid browser function table. Some functionality may be restricted.\n");
2101 }
2102
2103 // Store in a local table the browser functions that we may use.
2104 browser_functions.size = browserTable->size;
2105 browser_functions.version = browserTable->version;
2106 browser_functions.geturlnotify = browserTable->geturlnotify;
2107 browser_functions.geturl = browserTable->geturl;
2108 browser_functions.posturlnotify = browserTable->posturlnotify;
2109 browser_functions.posturl = browserTable->posturl;
2110 browser_functions.requestread = browserTable->requestread;
2111 browser_functions.newstream = browserTable->newstream;
2112 browser_functions.write = browserTable->write;
2113 browser_functions.destroystream = browserTable->destroystream;
2114 browser_functions.status = browserTable->status;
2115 browser_functions.uagent = browserTable->uagent;
2116 browser_functions.memalloc = browserTable->memalloc;
2117 browser_functions.memfree = browserTable->memfree;
2118 browser_functions.memflush = browserTable->memflush;
2119 browser_functions.reloadplugins = browserTable->reloadplugins;
2120 browser_functions.getJavaEnv = browserTable->getJavaEnv;
2121 browser_functions.getJavaPeer = browserTable->getJavaPeer;
2122 browser_functions.getvalue = browserTable->getvalue;
2123 browser_functions.setvalue = browserTable->setvalue;
2124 browser_functions.invalidaterect = browserTable->invalidaterect;
2125 browser_functions.invalidateregion = browserTable->invalidateregion;
2126 browser_functions.forceredraw = browserTable->forceredraw;
2127 browser_functions.getstringidentifier = browserTable->getstringidentifier;
2128 browser_functions.getstringidentifiers = browserTable->getstringidentifiers;
2129 browser_functions.getintidentifier = browserTable->getintidentifier;
2130 browser_functions.identifierisstring = browserTable->identifierisstring;
2131 browser_functions.utf8fromidentifier = browserTable->utf8fromidentifier;
2132 browser_functions.intfromidentifier = browserTable->intfromidentifier;
2133 browser_functions.createobject = browserTable->createobject;
2134 browser_functions.retainobject = browserTable->retainobject;
2135 browser_functions.releaseobject = browserTable->releaseobject;
2136 browser_functions.invoke = browserTable->invoke;
2137 browser_functions.invokeDefault = browserTable->invokeDefault;
2138 browser_functions.evaluate = browserTable->evaluate;
2139 browser_functions.getproperty = browserTable->getproperty;
2140 browser_functions.setproperty = browserTable->setproperty;
2141 browser_functions.removeproperty = browserTable->removeproperty;
2142 browser_functions.hasproperty = browserTable->hasproperty;
2143 browser_functions.hasmethod = browserTable->hasmethod;
2144 browser_functions.releasevariantvalue = browserTable->releasevariantvalue;
2145 browser_functions.setexception = browserTable->setexception;
2146 browser_functions.pluginthreadasynccall = browserTable->pluginthreadasynccall;
2147#if MOZILLA_VERSION_COLLAPSED >= 1090100
2148 browser_functions.getvalueforurl = browserTable->getvalueforurl;
2149 browser_functions.setvalueforurl = browserTable->setvalueforurl;
2150#endif
2151
2152 NPError np_error = NPERR_NO_ERROR;
2153#if !defined(_WIN32) && !defined (__OS2__)
2154 np_error = plugin_get_entry_points (pluginTable);
2155 if (np_error != NPERR_NO_ERROR)
2156 return np_error;
2157#endif
2158
2159 // Re-setting the above tables multiple times is OK (as the
2160 // browser may change its function locations). However
2161 // anything beyond this point should only run once.
2162 if (initialized)
2163 return NPERR_NO_ERROR;
2164
2165 // Make sure the plugin data directory exists, creating it if
2166 // necessary.
2167 data_directory = g_strconcat (P_tmpdir, NULL);
2168 if (!data_directory)
2169 {
2170 PLUGIN_ERROR ("Failed to create data directory name.");
2171 return NPERR_OUT_OF_MEMORY_ERROR;
2172 }
2173 gchar* filename = NULL;
2174
2175 // If P_tmpdir does not exist, try /tmp directly
2176
2177 if (!g_file_test (data_directory,
2178 (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
2179 {
2180 int file_error = 0;
2181
2182 data_directory = g_strconcat ("/tmp", NULL);
2183 if (!data_directory)
2184 {
2185 PLUGIN_ERROR ("Failed to create data directory name.");
2186 return NPERR_OUT_OF_MEMORY_ERROR;
2187 }
2188
2189 }
2190
2191 data_directory = g_strconcat (data_directory, "/icedteaplugin-", getenv("USER"), NULL);
2192
2193 if (!data_directory)
2194 {
2195 PLUGIN_ERROR ("Failed to create data directory name.");
2196 return NPERR_OUT_OF_MEMORY_ERROR;
2197 }
2198
2199 // Now create a icedteaplugin subdir
2200 if (!g_file_test (data_directory,
2201 (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
2202 {
2203 int file_error = 0;
2204
2205 file_error = g_mkdir (data_directory, 0700);
2206 if (file_error != 0)
2207 {
2208 PLUGIN_ERROR_THREE ("Failed to create data directory",
2209 data_directory,
2210 strerror (errno));
2211 np_error = NPERR_GENERIC_ERROR;
2212 goto cleanup_data_directory;
2213 }
2214 }
2215
2216
2217 // If data directory doesn't exit by this point, bail
2218 if (!g_file_test (data_directory,
2219 (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
2220 {
2221 PLUGIN_ERROR_THREE ("Temp directory does not exist: ",
2222 data_directory,
2223 strerror (errno));
2224
2225 np_error = NPERR_GENERIC_ERROR;
2226 goto cleanup_data_directory;
2227
2228 }
2229
2230 // Set appletviewer_executable.
2231 filename = g_strdup(ICEDTEA_WEB_JRE);
2232 appletviewer_executable = g_strdup_printf ("%s/bin/java",
2233 filename);
2234 PLUGIN_DEBUG("Executing java at %s\n", appletviewer_executable);
2235 if (!appletviewer_executable)
2236 {
2237 PLUGIN_ERROR ("Failed to create appletviewer executable name.");
2238 np_error = NPERR_OUT_OF_MEMORY_ERROR;
2239 goto cleanup_filename;
2240 }
2241
2242 np_error = plugin_test_appletviewer ();
2243 if (np_error != NPERR_NO_ERROR)
2244 {
2245 plugin_display_failure_dialog ();
2246 goto cleanup_appletviewer_executable;
2247 }
2248 g_free (filename);
2249
2250 initialized = true;
2251
2252 // Initialize threads (needed for mutexes).
2253 if (!g_thread_supported ())
2254 g_thread_init (NULL);
2255
2256 plugin_instance_mutex = g_mutex_new ();
2257
2258 PLUGIN_DEBUG ("NP_Initialize: using %s\n", appletviewer_executable);
2259
2260 plugin_req_proc = new PluginRequestProcessor();
2261 java_req_proc = new JavaMessageSender();
2262
2263 java_to_plugin_bus = new MessageBus();
2264 plugin_to_java_bus = new MessageBus();
2265
2266 java_to_plugin_bus->subscribe(plugin_req_proc);
2267 plugin_to_java_bus->subscribe(java_req_proc);
2268
2269 pthread_create (&plugin_request_processor_thread1, NULL, &queue_processor, (void*) plugin_req_proc);
2270 pthread_create (&plugin_request_processor_thread2, NULL, &queue_processor, (void*) plugin_req_proc);
2271 pthread_create (&plugin_request_processor_thread3, NULL, &queue_processor, (void*) plugin_req_proc);
2272
2273 itnp_plugin_thread_id = pthread_self();
2274
2275 pthread_mutexattr_t attribute;
2276 pthread_mutexattr_init(&attribute);
2277 pthread_mutexattr_settype(&attribute, PTHREAD_MUTEX_RECURSIVE);
2278 pthread_mutex_init(&pluginAsyncCallMutex, &attribute);
2279 pthread_mutexattr_destroy(&attribute);
2280
2281 PLUGIN_DEBUG ("NP_Initialize return\n");
2282
2283 return NPERR_NO_ERROR;
2284
2285 cleanup_appletviewer_executable:
2286 if (appletviewer_executable)
2287 {
2288 g_free (appletviewer_executable);
2289 appletviewer_executable = NULL;
2290 }
2291
2292 cleanup_filename:
2293 if (filename)
2294 {
2295 g_free (filename);
2296 filename = NULL;
2297 }
2298
2299 cleanup_data_directory:
2300 if (data_directory)
2301 {
2302 g_free (data_directory);
2303 data_directory = NULL;
2304 }
2305
2306
2307 return np_error;
2308}
2309
2310#if defined(_WIN32) || defined (__OS2__)
2311NPError
2312OSCALL NP_GetEntryPoints (NPPluginFuncs* pluginTable)
2313{
2314 PLUGIN_DEBUG ("NP_GetEntryPoints\n");
2315
2316 if (pluginTable == NULL)
2317 {
2318 PLUGIN_ERROR ("Plugin function table is NULL.");
2319
2320 return NPERR_INVALID_FUNCTABLE_ERROR;
2321 }
2322
2323 return plugin_get_entry_points (pluginTable);
2324}
2325#endif
2326
2327// Returns a string describing the MIME type that this plugin
2328// handles.
2329const char*
2330OSCALL NP_GetMIMEDescription ()
2331{
2332 PLUGIN_DEBUG ("NP_GetMIMEDescription\n");
2333
2334 PLUGIN_DEBUG ("NP_GetMIMEDescription return\n");
2335
2336 return PLUGIN_MIME_DESC;
2337}
2338
2339// Returns a value relevant to the plugin as a whole. The browser
2340// calls this function to obtain information about the plugin.
2341NPError
2342OSCALL NP_GetValue (void* future, NPPVariable variable, void* value)
2343{
2344 PLUGIN_DEBUG ("NP_GetValue\n");
2345
2346 NPError result = NPERR_NO_ERROR;
2347 gchar** char_value = (gchar**) value;
2348
2349 switch (variable)
2350 {
2351 case NPPVpluginNameString:
2352 PLUGIN_DEBUG ("NP_GetValue: returning plugin name.\n");
2353 *char_value = g_strdup (PLUGIN_FULL_NAME);
2354 break;
2355
2356 case NPPVpluginDescriptionString:
2357 PLUGIN_DEBUG ("NP_GetValue: returning plugin description.\n");
2358 *char_value = g_strdup (PLUGIN_DESC);
2359 break;
2360
2361 default:
2362 PLUGIN_ERROR ("Unknown plugin value requested.");
2363 result = NPERR_GENERIC_ERROR;
2364 break;
2365 }
2366
2367 PLUGIN_DEBUG ("NP_GetValue return\n");
2368
2369 return result;
2370}
2371
2372// Shuts down the plugin. Called after the last plugin instance is
2373// destroyed.
2374NPError
2375OSCALL NP_Shutdown (void)
2376{
2377 PLUGIN_DEBUG ("NP_Shutdown\n");
2378
2379 // Free mutex.
2380 if (plugin_instance_mutex)
2381 {
2382 g_mutex_free (plugin_instance_mutex);
2383 plugin_instance_mutex = NULL;
2384 }
2385
2386 if (data_directory)
2387 {
2388 g_free (data_directory);
2389 data_directory = NULL;
2390 }
2391
2392 if (appletviewer_executable)
2393 {
2394 g_free (appletviewer_executable);
2395 appletviewer_executable = NULL;
2396 }
2397
2398 // stop the appletviewer
2399 plugin_stop_appletviewer();
2400
2401 // remove monitor
2402 if (appletviewer_watch_id != -1)
2403 g_source_remove(appletviewer_watch_id);
2404
2405 // Removing a source is harmless if it fails since it just means the
2406 // source has already been removed.
2407 g_source_remove (in_watch_source);
2408 in_watch_source = 0;
2409
2410 // cleanup_in_from_appletviewer:
2411 if (in_from_appletviewer)
2412 g_io_channel_unref (in_from_appletviewer);
2413 in_from_appletviewer = NULL;
2414
2415 // cleanup_out_watch_source:
2416 g_source_remove (out_watch_source);
2417 out_watch_source = 0;
2418
2419 // cleanup_out_to_appletviewer:
2420 if (out_to_appletviewer)
2421 g_io_channel_unref (out_to_appletviewer);
2422 out_to_appletviewer = NULL;
2423
2424 // cleanup_out_pipe:
2425 // Delete output pipe.
2426 PLUGIN_DEBUG ("NP_Shutdown: deleting output fifo: %s\n", out_pipe_name);
2427 unlink (out_pipe_name);
2428 PLUGIN_DEBUG ("NP_Shutdown: deleted output fifo: %s\n", out_pipe_name);
2429
2430 // cleanup_out_pipe_name:
2431 g_free (out_pipe_name);
2432 out_pipe_name = NULL;
2433
2434 // cleanup_in_pipe:
2435 // Delete input pipe.
2436 PLUGIN_DEBUG ("NP_Shutdown: deleting input fifo: %s\n", in_pipe_name);
2437 unlink (in_pipe_name);
2438 PLUGIN_DEBUG ("NP_Shutdown: deleted input fifo: %s\n", in_pipe_name);
2439
2440 // cleanup_in_pipe_name:
2441 g_free (in_pipe_name);
2442 in_pipe_name = NULL;
2443
2444 // Destroy the call queue mutex
2445 pthread_mutex_destroy(&pluginAsyncCallMutex);
2446
2447 initialized = false;
2448
2449 pthread_cancel(plugin_request_processor_thread1);
2450 pthread_cancel(plugin_request_processor_thread2);
2451 pthread_cancel(plugin_request_processor_thread3);
2452
2453 pthread_join(plugin_request_processor_thread1, NULL);
2454 pthread_join(plugin_request_processor_thread2, NULL);
2455 pthread_join(plugin_request_processor_thread3, NULL);
2456
2457 java_to_plugin_bus->unSubscribe(plugin_req_proc);
2458 plugin_to_java_bus->unSubscribe(java_req_proc);
2459 //internal_bus->unSubscribe(java_req_proc);
2460 //internal_bus->unSubscribe(plugin_req_proc);
2461
2462 delete plugin_req_proc;
2463 delete java_req_proc;
2464 delete java_to_plugin_bus;
2465 delete plugin_to_java_bus;
2466 //delete internal_bus;
2467
2468 PLUGIN_DEBUG ("NP_Shutdown return\n");
2469
2470 return NPERR_NO_ERROR;
2471}
2472
2473NPObject*
2474get_scriptable_object(NPP instance)
2475{
2476 NPObject* obj;
2477 ITNPPluginData* data = (ITNPPluginData*) instance->pdata;
2478
2479 if (data->is_applet_instance) // dummy instance/package?
2480 {
2481 JavaRequestProcessor java_request = JavaRequestProcessor();
2482 JavaResultData* java_result;
2483 std::string instance_id = std::string();
2484 std::string applet_class_id = std::string();
2485
2486 int id = get_id_from_instance(instance);
2487 gchar* id_str = g_strdup_printf ("%d", id);
2488
2489 // Some browsers.. (e.g. chromium) don't call NPP_SetWindow
2490 // for 0x0 plugins and therefore require initialization with
2491 // a 0 handle
2492 if (!data->window_handle)
2493 {
2494 plugin_send_initialization_message(data->instance_id, 0, 0, 0, data->applet_tag);
2495 }
2496
2497 java_result = java_request.getAppletObjectInstance(id_str);
2498
2499 g_free(id_str);
2500
2501 if (java_result->error_occurred)
2502 {
2503 printf("Error: Unable to fetch applet instance id from Java side.\n");
2504 return NULL;
2505 }
2506
2507 instance_id.append(*(java_result->return_string));
2508
2509 java_result = java_request.getClassID(instance_id);
2510
2511 if (java_result->error_occurred)
2512 {
2513 printf("Error: Unable to fetch applet instance id from Java side.\n");
2514 return NULL;
2515 }
2516
2517 applet_class_id.append(*(java_result->return_string));
2518
2519 obj = IcedTeaScriptableJavaPackageObject::get_scriptable_java_object(instance, applet_class_id, instance_id, false);
2520
2521 } else
2522 {
2523 obj = IcedTeaScriptablePluginObject::get_scriptable_java_package_object(instance, "");
2524 }
2525
2526 return obj;
2527}
2528
2529NPObject*
2530allocate_scriptable_object(NPP npp, NPClass *aClass)
2531{
2532 PLUGIN_DEBUG("Allocating new scriptable object\n");
2533 return new IcedTeaScriptablePluginObject(npp);
2534}
2535
2536#ifdef __OS2__
2537
2538// Make sure static initializers in the plugin DLL are called
2539
2540static APIENTRY void cleanup(ULONG ulReason)
2541{
2542 __ctordtorTerm();
2543 _CRT_term();
2544 DosExitList(EXLST_EXIT, cleanup);
2545}
2546
2547unsigned long _System _DLL_InitTerm(unsigned long hModule,
2548 unsigned long ulFlag)
2549{
2550 APIRET arc;
2551
2552 if (ulFlag == 0)
2553 {
2554 arc = DosExitList (EXLST_ADD, cleanup);
2555 if (arc != NO_ERROR)
2556 return 0;
2557
2558 if (_CRT_init() != 0) // failure?
2559 return 0;
2560 __ctordtorInit();
2561 }
2562
2563 return 1;
2564}
2565
2566#endif /* __OS2__ */
Note: See TracBrowser for help on using the repository browser.