Merge branch 'loaded-branch'
[moon.git] / plugin / plugin.cpp
bloba9f8419eedd8561124088644f58956493cdbfb9c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * moon-plugin.cpp: MoonLight browser plugin.
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include <config.h>
16 #include <glib.h>
17 #include <fcntl.h>
18 #include <stdlib.h>
19 #include <dlfcn.h>
21 #include "plugin.h"
22 #include "plugin-spinner.h"
23 #include "plugin-class.h"
24 #include "plugin-debug.h"
25 #include "browser-bridge.h"
26 #include "downloader.h"
27 #include "pipeline-ui.h"
28 #include "plugin-downloader.h"
29 #include "npstream-request.h"
30 #include "xap.h"
31 #include "windowless.h"
32 #include "window-gtk.h"
33 #include "unzip.h"
34 #include "deployment.h"
35 #include "uri.h"
36 #include "timemanager.h"
38 #define Visual _XxVisual
39 #define Region _XxRegion
40 #include "gdk/gdkx.h"
41 #undef Visual
42 #undef Region
44 #ifdef DEBUG
45 #define d(x) x
46 #else
47 #define d(x)
48 #endif
50 #define w(x) x
51 // Debug NPStreams
52 #define nps(x)//x
54 extern guint32 moonlight_flags;
56 /* gleaned from svn log of the moon module, as well as olive/class/{agclr,agmono,System.Silverlight} */
57 static const char *moonlight_authors[] = {
58 "Aaron Bockover <abockover@novell.com>",
59 "Alan McGovern <amcgovern@novell.com>",
60 "Alp Toker <alp@nuanti.com>",
61 "Andreia Gaita <avidigal@novell.com>",
62 "Andrew Jorgensen <ajorgensen@novell.com>",
63 "Argiris Kirtzidis <akyrtzi@gmail.com>",
64 "Atsushi Enomoto <atsushi@ximian.com>",
65 "Chris Toshok <toshok@ximian.com>",
66 "Dick Porter <dick@ximian.com>",
67 "Everaldo Canuto <ecanuto@novell.com>",
68 "Fernando Herrera <fherrera@novell.com>",
69 "Geoff Norton <gnorton@novell.com>",
70 "Jackson Harper <jackson@ximian.com>",
71 "Jb Evain <jbevain@novell.com>",
72 "Jeffrey Stedfast <fejj@novell.com>",
73 "Larry Ewing <lewing@novell.com>",
74 "Manuel Ceron <ceronman@unicauca.edu.co>",
75 "Marek Habersack <mhabersack@novell.com>",
76 "Michael Dominic K. <mdk@mdk.am>",
77 "Michael Hutchinson <mhutchinson@novell.com>",
78 "Miguel de Icaza <miguel@novell.com>",
79 "Paolo Molaro <lupus@ximian.com>",
80 "Raja R Harinath <harinath@hurrynot.org>",
81 "Rodrigo Kumpera <rkumpera@novell.com>",
82 "Rolf Bjarne Kvinge <RKvinge@novell.com>",
83 "Rusty Howell <rhowell@novell.com>",
84 "Sebastien Pouliot <sebastien@ximian.com>",
85 "Stephane Delcroix <sdelcroix@novell.com>",
86 "Zoltan Varga <vargaz@gmail.com>",
87 NULL
90 void
91 plugin_menu_about (PluginInstance *plugin)
93 GtkAboutDialog *about = GTK_ABOUT_DIALOG (gtk_about_dialog_new ());
95 gtk_about_dialog_set_name (about, PLUGIN_OURNAME);
96 gtk_about_dialog_set_version (about, VERSION);
98 gtk_about_dialog_set_copyright (about, "Copyright 2007-2009 Novell, Inc. (http://www.novell.com/)");
99 #if FINAL_RELEASE
100 gtk_about_dialog_set_website (about, "http://moonlight-project.com/");
101 #else
102 gtk_about_dialog_set_website (about, "http://moonlight-project.com/Beta");
103 #endif
105 gtk_about_dialog_set_website_label (about, "Project Website");
107 gtk_about_dialog_set_authors (about, moonlight_authors);
109 /* Newer gtk+ versions require this for the close button to work */
110 g_signal_connect_swapped (about,
111 "response",
112 G_CALLBACK (gtk_widget_destroy),
113 about);
115 gtk_dialog_run (GTK_DIALOG (about));
118 void
119 plugin_media_pack (PluginInstance *plugin)
121 CodecDownloader::ShowUI (plugin->GetSurface ());
124 void
125 plugin_properties (PluginInstance *plugin)
127 plugin->Properties ();
130 void
131 plugin_show_menu (PluginInstance *plugin)
133 GtkWidget *menu;
134 GtkWidget *menu_item;
135 char *name;
137 menu = gtk_menu_new();
139 name = g_strdup_printf ("%s %s", PLUGIN_OURNAME, VERSION);
140 menu_item = gtk_menu_item_new_with_label (name);
141 g_free (name);
143 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
144 g_signal_connect_swapped (G_OBJECT(menu_item), "activate", G_CALLBACK (plugin_menu_about), plugin);
146 menu_item = gtk_menu_item_new_with_label ("Properties");
147 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
148 g_signal_connect_swapped (G_OBJECT(menu_item), "activate", G_CALLBACK (plugin_properties), plugin);
150 if (!Media::IsMSCodecsInstalled ()) {
151 menu_item = gtk_menu_item_new_with_label ("Install Microsoft Media Pack");
152 } else {
153 menu_item = gtk_menu_item_new_with_label ("Reinstall Microsoft Media Pack");
155 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
156 g_signal_connect_swapped (G_OBJECT(menu_item), "activate", G_CALLBACK (plugin_media_pack), plugin);
158 #ifdef DEBUG
159 menu_item = gtk_menu_item_new_with_label ("Show XAML Hierarchy");
160 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
161 g_signal_connect_swapped (G_OBJECT(menu_item), "activate", G_CALLBACK (plugin_debug), plugin);
163 menu_item = gtk_menu_item_new_with_label ("Sources");
164 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
165 g_signal_connect_swapped (G_OBJECT(menu_item), "activate", G_CALLBACK (plugin_sources), plugin);
166 #endif
168 gtk_widget_show_all (menu);
169 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
172 gboolean
173 PluginInstance::plugin_button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer user_data)
175 PluginInstance *plugin = (PluginInstance *) user_data;
177 if (event->button == 3) {
178 plugin_show_menu (plugin);
179 return TRUE;
182 return FALSE;
185 char *
186 NPN_strdup (const char *tocopy)
188 int len = strlen(tocopy);
189 char *ptr = (char *)NPN_MemAlloc (len+1);
190 if (ptr != NULL) {
191 strcpy (ptr, tocopy);
192 // WebKit should calloc so we dont have to do this
193 ptr[len] = 0;
196 return ptr;
199 /*** PluginInstance:: *********************************************************/
201 GSList *plugin_instances = NULL;
203 static void
204 table_add (GtkWidget *table, const char *txt, int col, int row)
206 GtkWidget *l = gtk_label_new (txt);
208 gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
209 gtk_table_attach (GTK_TABLE(table), l, col, col+1, row, row+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) 0, 4, 0);
212 static GtkWidget *
213 title (const char *txt)
215 char *fmt = g_strdup_printf ("<b>%s</b>", txt);
216 GtkWidget *label = gtk_label_new (NULL);
218 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
219 gtk_label_set_markup (GTK_LABEL (label), fmt);
220 g_free (fmt);
222 return label;
225 static void
226 expose_regions (GtkToggleButton *checkbox, gpointer user_data)
228 if (gtk_toggle_button_get_active (checkbox))
229 moonlight_flags |= RUNTIME_INIT_SHOW_EXPOSE;
230 else
231 moonlight_flags &= ~RUNTIME_INIT_SHOW_EXPOSE;
234 static void
235 clipping_regions (GtkToggleButton *checkbox, gpointer user_data)
237 if (gtk_toggle_button_get_active (checkbox))
238 moonlight_flags |= RUNTIME_INIT_SHOW_CLIPPING;
239 else
240 moonlight_flags &= ~RUNTIME_INIT_SHOW_CLIPPING;
243 static void
244 bounding_boxes (GtkToggleButton *checkbox, gpointer user_data)
246 if (gtk_toggle_button_get_active (checkbox))
247 moonlight_flags |= RUNTIME_INIT_SHOW_BOUNDING_BOXES;
248 else
249 moonlight_flags &= ~RUNTIME_INIT_SHOW_BOUNDING_BOXES;
252 static void
253 textboxes (GtkToggleButton *checkbox, gpointer user_data)
255 if (gtk_toggle_button_get_active (checkbox))
256 moonlight_flags |= RUNTIME_INIT_SHOW_TEXTBOXES;
257 else
258 moonlight_flags &= ~RUNTIME_INIT_SHOW_TEXTBOXES;
261 static void
262 show_fps (GtkToggleButton *checkbox, gpointer user_data)
264 if (gtk_toggle_button_get_active (checkbox))
265 moonlight_flags |= RUNTIME_INIT_SHOW_FPS;
266 else
267 moonlight_flags &= ~RUNTIME_INIT_SHOW_FPS;
270 void
271 PluginInstance::properties_dialog_response (GtkWidget *dialog, int response, PluginInstance *plugin)
273 plugin->properties_fps_label = NULL;
274 plugin->properties_cache_label = NULL;
275 gtk_widget_destroy (dialog);
278 void
279 PluginInstance::Properties ()
281 GtkWidget *dialog, *table, *checkbox;
282 char buffer[40];
283 GtkBox *vbox;
284 int row = 0;
286 Deployment::SetCurrent (deployment);
288 dialog = gtk_dialog_new_with_buttons ("Object Properties", NULL, (GtkDialogFlags)
289 GTK_DIALOG_NO_SEPARATOR,
290 GTK_STOCK_CLOSE, GTK_RESPONSE_NONE, NULL);
291 gtk_container_set_border_width (GTK_CONTAINER (dialog), 8);
293 vbox = GTK_BOX (GTK_DIALOG (dialog)->vbox);
295 // Silverlight Application properties
296 gtk_box_pack_start (vbox, title ("Properties"), FALSE, FALSE, 0);
297 gtk_box_pack_start (vbox, gtk_hseparator_new (), FALSE, FALSE, 8);
299 table = gtk_table_new (11, 2, FALSE);
300 gtk_box_pack_start (vbox, table, TRUE, TRUE, 0);
302 table_add (table, "Source:", 0, row++);
303 table_add (table, "Width:", 0, row++);
304 table_add (table, "Height:", 0, row++);
305 table_add (table, "Background:", 0, row++);
306 table_add (table, "RuntimeVersion:", 0, row++);
307 table_add (table, "Windowless:", 0, row++);
308 table_add (table, "MaxFrameRate:", 0, row++);
309 table_add (table, "Codecs:", 0, row++);
311 row = 0;
312 table_add (table, source, 1, row++);
313 snprintf (buffer, sizeof (buffer), "%dpx", GetActualWidth ());
314 table_add (table, buffer, 1, row++);
315 snprintf (buffer, sizeof (buffer), "%dpx", GetActualHeight ());
316 table_add (table, buffer, 1, row++);
317 table_add (table, background, 1, row++);
318 if (!xaml_loader || xaml_loader->IsManaged ()) {
319 Deployment *deployment = GetDeployment ();
321 if (deployment && deployment->GetRuntimeVersion ()) {
322 table_add (table, deployment->GetRuntimeVersion (), 1, row++);
323 } else {
324 table_add (table, "(Unknown)", 1, row++);
326 } else {
327 table_add (table, "1.0 (Pure XAML)", 1, row++);
329 table_add (table, windowless ? "yes" : "no", 1, row++);
330 snprintf (buffer, sizeof (buffer), "%i", maxFrameRate);
331 table_add (table, buffer, 1, row++);
332 #if INCLUDE_FFMPEG
333 table_add (table, Media::IsMSCodecsInstalled () ? "ms-codecs" : "ffmpeg", 1, row++);
334 #else
335 table_add (table, Media::IsMSCodecsInstalled () ? "ms-codecs" : "none", 1, row++);
336 #endif
338 row++;
339 properties_fps_label = gtk_label_new ("");
340 gtk_misc_set_alignment (GTK_MISC (properties_fps_label), 0.0, 0.5);
341 gtk_table_attach (GTK_TABLE(table), properties_fps_label, 0, 2, row, row+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) 0, 4, 0);
343 row++;
344 properties_cache_label = gtk_label_new ("");
345 gtk_misc_set_alignment (GTK_MISC (properties_cache_label), 0.0, 0.5);
346 gtk_table_attach (GTK_TABLE(table), properties_cache_label, 0, 2, row, row+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) 0, 4, 0);
348 // Runtime debug options
349 gtk_box_pack_start (vbox, title ("Runtime Debug Options"), FALSE, FALSE, 0);
350 gtk_box_pack_start (vbox, gtk_hseparator_new (), FALSE, FALSE, 8);
352 checkbox = gtk_check_button_new_with_label ("Show exposed regions");
353 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox), moonlight_flags & RUNTIME_INIT_SHOW_EXPOSE);
354 g_signal_connect (checkbox, "toggled", G_CALLBACK (expose_regions), NULL);
355 gtk_box_pack_start (vbox, checkbox, FALSE, FALSE, 0);
357 checkbox = gtk_check_button_new_with_label ("Show clipping regions");
358 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox), moonlight_flags & RUNTIME_INIT_SHOW_CLIPPING);
359 g_signal_connect (checkbox, "toggled", G_CALLBACK (clipping_regions), NULL);
360 gtk_box_pack_start (vbox, checkbox, FALSE, FALSE, 0);
362 checkbox = gtk_check_button_new_with_label ("Show bounding boxes");
363 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox), moonlight_flags & RUNTIME_INIT_SHOW_BOUNDING_BOXES);
364 g_signal_connect (checkbox, "toggled", G_CALLBACK (bounding_boxes), NULL);
365 gtk_box_pack_start (vbox, checkbox, FALSE, FALSE, 0);
367 checkbox = gtk_check_button_new_with_label ("Show text boxes");
368 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox), moonlight_flags & RUNTIME_INIT_SHOW_TEXTBOXES);
369 g_signal_connect (checkbox, "toggled", G_CALLBACK (textboxes), NULL);
370 gtk_box_pack_start (vbox, checkbox, FALSE, FALSE, 0);
372 checkbox = gtk_check_button_new_with_label ("Show Frames Per Second");
373 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox), moonlight_flags & RUNTIME_INIT_SHOW_FPS);
374 g_signal_connect (checkbox, "toggled", G_CALLBACK (show_fps), NULL);
375 gtk_box_pack_start (vbox, checkbox, FALSE, FALSE, 0);
377 g_signal_connect (dialog, "response", G_CALLBACK (properties_dialog_response), this);
378 gtk_widget_show_all (dialog);
381 PluginInstance::PluginInstance (NPP instance, guint16 mode)
383 refcount = 1;
384 this->instance = instance;
385 this->mode = mode;
386 window = NULL;
387 connected_to_container = false;
389 properties_fps_label = NULL;
390 properties_cache_label = NULL;
392 rootobject = NULL;
394 container = NULL;
395 surface = NULL;
396 moon_window = NULL;
398 // Property fields
399 source_location = NULL;
400 source_location_original = NULL;
401 initParams = NULL;
402 source = NULL;
403 source_original = NULL;
404 source_idle = 0;
405 onLoad = NULL;
406 onError = NULL;
407 onResize = NULL;
408 onSourceDownloadProgressChanged = NULL;
409 onSourceDownloadComplete = NULL;
410 splashscreensource = NULL;
411 background = NULL;
412 id = NULL;
413 culture = NULL;
414 uiCulture = NULL;
416 windowless = false;
417 cross_domain_app = false; // false, since embedded xaml (in html) won't load anything (to change this value)
418 default_enable_html_access = true; // should we use the default value (wrt the HTML script supplied value)
419 enable_html_access = true; // an empty plugin must return TRUE before loading anything else (e.g. scripting)
420 allow_html_popup_window = false;
421 xembed_supported = FALSE;
422 loading_splash = false;
423 is_splash = false;
424 is_shutting_down = false;
425 has_shutdown = false;
427 bridge = NULL;
429 // MSDN says the default is 24: http://msdn2.microsoft.com/en-us/library/bb979688.aspx
430 // blog says the default is 60: http://blogs.msdn.com/seema/archive/2007/10/07/perf-debugging-tips-enableredrawregions-a-performance-bug-in-videobrush.aspx
431 // testing seems to confirm that the default is 60.
432 maxFrameRate = 60;
433 enable_framerate_counter = false;
435 xaml_loader = NULL;
436 timers = NULL;
438 wrapped_objects = g_hash_table_new (g_direct_hash, g_direct_equal);
440 cleanup_pointers = NULL;
442 plugin_instances = g_slist_append (plugin_instances, instance);
444 /* back pointer to us */
445 instance->pdata = this;
447 #if DEBUG
448 moon_sources = NULL;
449 #endif
452 void
453 PluginInstance::Recreate (const char *source)
455 //printf ("PluginInstance::Recreate (%s) this: %p, instance->pdata: %p\n", source, this, instance->pdata);
457 int argc = 16;
458 char *maxFramerate = g_strdup_printf ("%i", maxFrameRate);
459 const char *argn [] =
460 { "initParams", "onLoad", "onError", "onResize",
461 "source", "background", "windowless", "maxFramerate", "id",
462 "enablehtmlaccess", "allowhtmlpopupwindow", "splashscreensource",
463 "onSourceDownloadProgressChanged", "onSourceDownloadComplete",
464 "culture", "uiculture", NULL };
465 const char *argv [] =
466 { initParams, onLoad, onError, onResize,
467 source, background, windowless ? "true" : "false", maxFramerate, id,
468 enable_html_access ? "true" : "false", allow_html_popup_window ? "true" : "false", splashscreensource,
469 onSourceDownloadProgressChanged, onSourceDownloadComplete,
470 culture, uiCulture, NULL };
473 instance->pdata = NULL;
475 PluginInstance *result;
476 result = new PluginInstance (instance, mode);
478 // printf ("PluginInstance::Recreate (%s), created %p\n", source, result);
480 /* steal the root object, we need to use the same instance */
481 result->rootobject = rootobject;
482 rootobject = NULL;
483 if (result->rootobject)
484 result->rootobject->PreSwitchPlugin (this, result);
486 result->cross_domain_app = cross_domain_app;
487 result->default_enable_html_access = default_enable_html_access;
488 result->enable_framerate_counter = enable_framerate_counter;
489 result->connected_to_container = connected_to_container;
490 result->Initialize (argc, (char **) argn, (char **) argv);
491 // printf ("PluginInstance::Recreate (%s), new plugin's deployment: %p, current deployment: %p\n", source, result->deployment, Deployment::GetCurrent ());
492 if (surface) {
493 result->moon_window = surface->DetachWindow (); /* we reuse the same MoonWindow */
494 } else {
495 result->moon_window = NULL;
497 result->window = window;
498 result->CreateWindow ();
500 g_free (maxFramerate);
502 /* destroy the current plugin instance */
503 Deployment::SetCurrent (deployment);
504 Shutdown ();
505 unref (); /* the ref instance->pdata has */
507 /* put in the new plugin instance */
508 Deployment::SetCurrent (result->deployment);
509 instance->pdata = result;
511 if (result->rootobject) {
512 /* We need to reconnect all event handlers js might have to our root objects */
513 result->rootobject->PostSwitchPlugin (this, result);
516 // printf ("PluginInstance::Recreate (%s) [Done], new plugin: %p\n", source, result);
519 PluginInstance::~PluginInstance ()
521 deployment->unref_delayed ();
524 void
525 PluginInstance::ref ()
527 g_assert (refcount > 0);
528 g_atomic_int_inc (&refcount);
531 void
532 PluginInstance::unref ()
534 g_assert (refcount > 0);
535 int v = g_atomic_int_exchange_and_add (&refcount, -1) - 1;
536 if (v == 0)
537 delete this;
540 bool
541 PluginInstance::IsShuttingDown ()
543 VERIFY_MAIN_THREAD;
544 return is_shutting_down;
547 bool
548 PluginInstance::HasShutdown ()
550 VERIFY_MAIN_THREAD;
551 return has_shutdown;
554 void
555 PluginInstance::Shutdown ()
557 // Kill timers
558 GSList *p;
560 g_return_if_fail (!is_shutting_down);
561 g_return_if_fail (!has_shutdown);
563 is_shutting_down = true;
565 Deployment::SetCurrent (deployment);
567 #if PLUGIN_SL_2_0
568 // Destroy the XAP application
569 DestroyApplication ();
570 #endif
572 for (p = timers; p != NULL; p = p->next){
573 guint32 source_id = GPOINTER_TO_INT (p->data);
575 g_source_remove (source_id);
577 g_slist_free (p);
578 timers = NULL;
580 g_hash_table_destroy (wrapped_objects);
581 wrapped_objects = NULL;
583 // Remove us from the list.
584 plugin_instances = g_slist_remove (plugin_instances, instance);
586 for (GSList *l = cleanup_pointers; l; l = l->next) {
587 gpointer* p = (gpointer*)l->data;
588 *p = NULL;
590 g_slist_free (cleanup_pointers);
591 cleanup_pointers = NULL;
593 if (rootobject) {
594 NPN_ReleaseObject ((NPObject*)rootobject);
595 rootobject = NULL;
598 g_free (background);
599 background = NULL;
600 g_free (id);
601 id = NULL;
602 g_free (onSourceDownloadProgressChanged);
603 onSourceDownloadProgressChanged = NULL;
604 g_free (onSourceDownloadComplete);
605 onSourceDownloadComplete = NULL;
606 g_free (splashscreensource);
607 splashscreensource = NULL;
608 g_free (culture);
609 culture = NULL;
610 g_free (uiCulture);
611 uiCulture = NULL;
612 g_free (initParams);
613 initParams = NULL;
614 delete xaml_loader;
615 xaml_loader = NULL;
617 g_free (source);
618 source = NULL;
619 g_free (source_original);
620 source_original = NULL;
621 g_free (source_location);
622 source_location = NULL;
623 g_free (source_location_original);
624 source_location_original = NULL;
626 if (source_idle) {
627 g_source_remove (source_idle);
628 source_idle = 0;
632 // The code below was an attempt at fixing this, but we are still getting spurious errors
633 // we might have another source of problems
635 //fprintf (stderr, "Destroying the surface: %p, plugin: %p\n", surface, this);
636 if (surface != NULL) {
637 //gdk_error_trap_push ();
638 surface->Zombify();
639 surface->Dispose ();
640 surface->unref_delayed();
641 //gdk_error_trap_pop ();
642 surface = NULL;
645 if (bridge) {
646 delete bridge;
647 bridge = NULL;
650 deployment->Shutdown ();
651 #if DEBUG
652 delete moon_sources;
653 moon_sources = NULL;
654 #endif
656 is_shutting_down = false;
657 has_shutdown = true;
660 #if DEBUG
661 void
662 PluginInstance::AddSource (const char *uri, const char *filename)
664 moon_source *src = new moon_source ();
665 src->uri = g_strdup (uri);
666 src->filename = g_strdup (filename);
667 if (moon_sources == NULL)
668 moon_sources = new List ();
669 moon_sources->Append (src);
672 List*
673 PluginInstance::GetSources ()
675 return moon_sources;
677 #endif
680 static bool
681 same_site_of_origin (const char *url1, const char *url2)
683 bool result = false;
684 Uri *uri1;
686 if (url1 == NULL || url2 == NULL)
687 return true;
689 uri1 = new Uri ();
690 if (uri1->Parse (url1)) {
691 Uri *uri2 = new Uri ();
693 if (uri2->Parse (url2)) {
694 // if only one of the two URI is absolute then the second one is relative to the first,
695 // which makes it part of the same site of origin
696 if ((uri1->isAbsolute && !uri2->isAbsolute) || (!uri1->isAbsolute && uri2->isAbsolute))
697 result = true;
698 else
699 result = Uri::SameSiteOfOrigin (uri1, uri2);
701 delete uri2;
703 delete uri1;
704 return result;
707 static bool
708 parse_bool_arg (const char *arg)
710 bool b;
711 return xaml_bool_from_str (arg, &b) && b;
714 void
715 PluginInstance::Initialize (int argc, char* argn[], char* argv[])
717 for (int i = 0; i < argc; i++) {
718 if (argn[i] == NULL) {
719 //g_warning ("PluginInstance::Initialize, arg %d == NULL", i);
720 continue;
722 else if (!g_ascii_strcasecmp (argn[i], "initParams")) {
723 initParams = g_strdup (argv[i]);
725 else if (!g_ascii_strcasecmp (argn[i], "onLoad")) {
726 onLoad = argv[i];
728 else if (!g_ascii_strcasecmp (argn[i], "onError")) {
729 onError = argv[i];
731 else if (!g_ascii_strcasecmp (argn[i], "onResize")) {
732 onResize = argv[i];
734 else if (!g_ascii_strcasecmp (argn[i], "src") || !g_ascii_strcasecmp (argn[i], "source")) {
735 /* There is a new design pattern that creates a silverlight object with data="data:application/x-silverlight,"
736 * firefox is passing this to us as the src element. We need to ensure we dont set source to this value
737 * as this design pattern sets a xap up after the fact, but checks to ensure Source hasn't been set yet.
739 * eg: http://theamazingalbumcoveratlas.org/
741 * TODO: Find a site that has data:application/x-silverlight,SOMEDATA and figure out what we do with it
743 if (g_ascii_strncasecmp (argv[i], "data:application/x-silverlight", 30) != 0 && argv[i][strlen(argv[i])-1] != ',') {
744 source = g_strdup (argv[i]);
745 // we must be able to retrieve the original source, e.g. after a redirect
746 source_original = g_strdup (source);
749 else if (!g_ascii_strcasecmp (argn[i], "background")) {
750 background = g_strdup (argv[i]);
752 else if (!g_ascii_strcasecmp (argn [i], "windowless")) {
753 windowless = parse_bool_arg (argv [i]);
755 else if (!g_ascii_strcasecmp (argn [i], "maxFramerate")) {
756 maxFrameRate = atoi (argv [i]);
758 else if (!g_ascii_strcasecmp (argn [i], "id")) {
759 id = g_strdup (argv [i]);
761 else if (!g_ascii_strcasecmp (argn [i], "enablehtmlaccess")) {
762 default_enable_html_access = false; // we're using the application value, not the default one
763 enable_html_access = parse_bool_arg (argv [i]);
765 else if (!g_ascii_strcasecmp (argn [i], "allowhtmlpopupwindow")) {
766 allow_html_popup_window = parse_bool_arg (argv [i]);
768 else if (!g_ascii_strcasecmp (argn [i], "splashscreensource")) {
769 splashscreensource = g_strdup (argv [i]);
771 else if (!g_ascii_strcasecmp (argn [i], "onSourceDownloadProgressChanged")) {
772 onSourceDownloadProgressChanged = g_strdup (argv [i]);
774 else if (!g_ascii_strcasecmp (argn [i], "onSourceDownloadComplete")) {
775 onSourceDownloadComplete = g_strdup (argv [i]);
777 else if (!g_ascii_strcasecmp (argn [i], "culture")) {
778 culture = g_strdup (argv[i]);
780 else if (!g_ascii_strcasecmp (argn [i], "uiCulture")) {
781 uiCulture = g_strdup (argv[i]);
783 else {
784 //fprintf (stderr, "unhandled attribute %s='%s' in PluginInstance::Initialize\n", argn[i], argv[i]);
788 // like 'source' the original location can also be required later (for cross-domain checks after redirections)
789 source_location_original = GetPageLocation ();
791 guint32 supportsWindowless = FALSE; // NPBool + padding
793 int plugin_major, plugin_minor;
794 int netscape_major, netscape_minor;
795 bool try_opera_quirks = FALSE;
797 /* Find the version numbers. */
798 NPN_Version(&plugin_major, &plugin_minor,
799 &netscape_major, &netscape_minor);
801 //d(printf ("Plugin NPAPI version = %d.%d\n", plugin_major, netscape_minor));
802 //d(printf ("Browser NPAPI version = %d.%d\n", netscape_major, netscape_minor));
804 NPError error;
805 error = NPN_GetValue (instance, NPNVSupportsXEmbedBool, &xembed_supported);
806 if (error || !xembed_supported) {
807 // This should be an error but we'll use it to detect
808 // that we are running in opera
809 //return NPERR_INCOMPATIBLE_VERSION_ERROR;
810 if (!windowless)
811 d(printf ("*** XEmbed not supported\n"));
813 try_opera_quirks = true;
816 error = NPN_GetValue (instance, NPNVSupportsWindowless, &supportsWindowless);
817 supportsWindowless = (error == NPERR_NO_ERROR) && supportsWindowless;
819 #ifdef DEBUG
820 if ((moonlight_flags & RUNTIME_INIT_ALLOW_WINDOWLESS) == 0) {
821 printf ("plugin wants to be windowless, but we're not going to let it\n");
822 windowless = false;
824 #endif
825 if (windowless) {
826 if (supportsWindowless) {
827 NPN_SetValue (instance, NPPVpluginWindowBool, (void *) FALSE);
828 NPN_SetValue (instance, NPPVpluginTransparentBool, (void *) TRUE);
829 d(printf ("windowless mode\n"));
830 } else {
831 d(printf ("browser doesn't support windowless mode.\n"));
832 windowless = false;
836 // grovel around in the useragent and try to figure out which
837 // browser bridge we should use.
838 const char *useragent = NPN_UserAgent (instance);
840 if (strstr (useragent, "Opera")) {
841 // opera based
842 TryLoadBridge ("opera");
844 else if (strstr (useragent, "AppleWebKit")) {
845 // webkit based
846 TryLoadBridge ("webkit");
848 else if (strstr (useragent, "Gecko")) {
849 // gecko based, let's look for 'rv:1.8' vs 'rv:1.9'
850 if (strstr (useragent, "rv:1.8")) {
851 TryLoadBridge ("ff2");
853 else if (strstr (useragent, "rv:1.9")) {
854 TryLoadBridge ("ff3");
858 // XXX Opera currently claims to be mozilla when we query it
859 if (!bridge && try_opera_quirks)
860 TryLoadBridge ("opera");
862 if (!bridge) {
863 g_warning ("probing for browser type failed, user agent = `%s'",
864 useragent);
867 if (!CreatePluginDeployment ()) {
868 g_warning ("Couldn't initialize Mono or create the plugin Deployment");
872 typedef BrowserBridge* (*create_bridge_func)();
874 const char*
875 get_plugin_dir (void)
877 static char *plugin_dir = NULL;
879 if (!plugin_dir) {
880 Dl_info dlinfo;
881 if (dladdr((void *) &plugin_show_menu, &dlinfo) == 0) {
882 fprintf (stderr, "Unable to find the location of libmoonplugin.so: %s\n", dlerror ());
883 return NULL;
885 plugin_dir = g_path_get_dirname (dlinfo.dli_fname);
887 return plugin_dir;
890 void
891 PluginInstance::TryLoadBridge (const char *prefix)
893 char *bridge_name = g_strdup_printf ("libmoonplugin-%sbridge.so", prefix);
894 char *bridge_path;
896 bridge_path = g_build_filename (get_plugin_dir (), bridge_name, NULL);
898 void* bridge_handle = dlopen (bridge_path, RTLD_LAZY);
900 g_free (bridge_name);
901 g_free (bridge_path);
903 if (bridge_handle == NULL) {
904 g_warning ("failed to load browser bridge: %s", dlerror());
905 return;
908 create_bridge_func bridge_ctor = (create_bridge_func)dlsym (bridge_handle, "CreateBrowserBridge");
909 if (bridge_ctor == NULL) {
910 g_warning ("failed to locate CreateBrowserBridge symbol: %s", dlerror());
911 return;
914 bridge = bridge_ctor ();
917 NPError
918 PluginInstance::GetValue (NPPVariable variable, void *result)
920 NPError err = NPERR_NO_ERROR;
922 switch (variable) {
923 case NPPVpluginNeedsXEmbed:
924 *((NPBool *)result) = !windowless;
925 break;
926 case NPPVpluginScriptableNPObject:
927 *((NPObject**) result) = GetRootObject ();
928 break;
929 default:
930 err = NPERR_INVALID_PARAM;
931 break;
934 return err;
937 NPError
938 PluginInstance::SetValue (NPNVariable variable, void *value)
940 return NPERR_NO_ERROR;
943 NPError
944 PluginInstance::SetWindow (NPWindow *window)
946 Deployment::SetCurrent (deployment);
948 if (moon_window) {
949 // XXX opera Window lifetime hack needs this
950 this->window = window;
952 if (!surface)
953 return NPERR_GENERIC_ERROR;
955 moon_window->Resize (window->width, window->height);
956 return NPERR_NO_ERROR;
959 this->window = window;
960 CreateWindow ();
962 return NPERR_NO_ERROR;
965 NPObject*
966 PluginInstance::GetHost()
968 NPObject *object = NULL;
969 if (NPERR_NO_ERROR != NPN_GetValue(instance, NPNVPluginElementNPObject, &object)) {
970 d(printf ("Failed to get plugin host object\n"));
972 return object;
975 void
976 PluginInstance::ReportFPS (Surface *surface, int nframes, float nsecs, void *user_data)
978 PluginInstance *plugin = (PluginInstance *) user_data;
979 char *msg;
981 msg = g_strdup_printf ("Rendered %d frames in %.3fs = %.3f FPS",
982 nframes, nsecs, nframes / nsecs);
984 NPN_Status (plugin->instance, msg);
986 if (plugin->properties_fps_label)
987 gtk_label_set_text (GTK_LABEL (plugin->properties_fps_label), msg);
989 g_free (msg);
992 void
993 PluginInstance::ReportCache (Surface *surface, long bytes, void *user_data)
995 PluginInstance *plugin = (PluginInstance *) user_data;
996 char *msg;
998 if (bytes < 1048576)
999 msg = g_strdup_printf ("Cache size is ~%d KB", (int) (bytes / 1024));
1000 else
1001 msg = g_strdup_printf ("Cache size is ~%.2f MB", bytes / 1048576.0);
1003 NPN_Status (plugin->instance, msg);
1005 if (plugin->properties_cache_label)
1006 gtk_label_set_text (GTK_LABEL (plugin->properties_cache_label), msg);
1008 g_free (msg);
1011 static void
1012 register_event (NPP instance, const char *event_name, char *script_name, NPObject *npobj)
1014 if (!script_name)
1015 return;
1017 char *retval = NPN_strdup (script_name);
1018 NPVariant npvalue;
1020 STRINGZ_TO_NPVARIANT (retval, npvalue);
1021 NPIdentifier identifier = NPN_GetStringIdentifier (event_name);
1022 NPN_SetProperty (instance, npobj, identifier, &npvalue);
1023 NPN_MemFree (retval);
1026 bool
1027 PluginInstance::IsLoaded ()
1029 if (!GetSurface () || is_splash)
1030 return false;
1032 return GetSurface()->IsLoaded();
1035 void
1036 PluginInstance::CreateWindow ()
1038 bool created = false;
1039 bool success = true;
1041 if (moon_window == NULL) {
1042 if (windowless) {
1043 moon_window = new MoonWindowless (window->width, window->height, this);
1044 moon_window->SetTransparent (true);
1046 else {
1047 moon_window = new MoonWindowGtk (false, window->width, window->height);
1049 created = true;
1050 } else {
1051 created = false;
1054 surface = new Surface (moon_window);
1055 deployment->SetSurface (surface);
1056 if (!created)
1057 moon_window->SetSurface (surface);
1059 MoonlightScriptControlObject *root = GetRootObject ();
1060 register_event (instance, "onSourceDownloadProgressChanged", onSourceDownloadProgressChanged, root);
1061 register_event (instance, "onSourceDownloadComplete", onSourceDownloadComplete, root);
1062 register_event (instance, "onError", onError, root);
1063 // register_event (instance, "onResize", onResize, rootx->content);
1065 // NOTE: last testing showed this call causes opera to reenter but moving it is trouble and
1066 // the bug is on opera's side.
1067 SetPageURL ();
1068 success = LoadSplash ();
1070 surface->SetFPSReportFunc (ReportFPS, this);
1071 surface->SetCacheReportFunc (ReportCache, this);
1072 surface->SetDownloaderContext (this);
1074 surface->GetTimeManager()->SetMaximumRefreshRate (maxFrameRate);
1076 if (background) {
1077 Color *c = color_from_str (background);
1079 if (c == NULL) {
1080 d(printf ("error setting background color\n"));
1081 c = new Color (0x00FFFFFF);
1084 surface->SetBackgroundColor (c);
1085 delete c;
1088 if (success && !windowless && !connected_to_container) {
1089 // GtkPlug container and surface inside
1090 container = gtk_plug_new ((GdkNativeWindow) window->window);
1092 // Connect signals to container
1093 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (container), GTK_CAN_FOCUS);
1095 gtk_widget_add_events (container,
1096 GDK_BUTTON_PRESS_MASK |
1097 GDK_BUTTON_RELEASE_MASK |
1098 GDK_KEY_PRESS_MASK |
1099 GDK_KEY_RELEASE_MASK |
1100 GDK_POINTER_MOTION_MASK |
1101 GDK_SCROLL_MASK |
1102 GDK_EXPOSURE_MASK |
1103 GDK_VISIBILITY_NOTIFY_MASK |
1104 GDK_ENTER_NOTIFY_MASK |
1105 GDK_LEAVE_NOTIFY_MASK |
1106 GDK_FOCUS_CHANGE_MASK
1109 g_signal_connect (G_OBJECT(container), "button-press-event", G_CALLBACK (PluginInstance::plugin_button_press_callback), this);
1111 gtk_container_add (GTK_CONTAINER (container), ((MoonWindowGtk*)moon_window)->GetWidget());
1112 gtk_widget_show_all (container);
1113 connected_to_container = true;
1117 void
1118 PluginInstance::UpdateSource ()
1120 if (source_idle) {
1121 g_source_remove (source_idle);
1122 source_idle = 0;
1125 if (surface != NULL)
1126 surface->DetachDownloaders ();
1128 if (!source || strlen (source) == 0)
1129 return;
1131 char *pos = strchr (source, '#');
1132 if (pos) {
1133 // FIXME: this will crash if this object has been deleted by the time IdleUpdateSourceByReference is called.
1134 source_idle = g_idle_add (IdleUpdateSourceByReference, this);
1135 SetPageURL ();
1136 } else {
1137 StreamNotify *notify = new StreamNotify (StreamNotify::SOURCE, source);
1139 // FIXME: check for errors
1140 NPN_GetURLNotify (instance, source, NULL, notify);
1144 gboolean
1145 PluginInstance::IdleUpdateSourceByReference (gpointer data)
1147 PluginInstance *instance = (PluginInstance*)data;
1148 char *pos = NULL;
1150 instance->source_idle = 0;
1152 if (instance->source)
1153 pos = strchr (instance->source, '#');
1155 if (pos && strlen (pos+1) > 0)
1156 instance->UpdateSourceByReference (pos+1);
1158 instance->GetSurface ()->EmitSourceDownloadProgressChanged (new DownloadProgressEventArgs (1.0));
1159 instance->GetSurface ()->EmitSourceDownloadComplete ();
1160 return FALSE;
1163 void
1164 PluginInstance::UpdateSourceByReference (const char *value)
1166 // basically do the equivalent of document.getElementById('@value').textContent
1167 // all using NPAPI.
1169 NPVariant _document;
1170 NPVariant _element;
1171 NPVariant _elementName;
1172 NPVariant _textContent;
1174 Deployment::SetCurrent (deployment);
1176 NPIdentifier id_ownerDocument = NPN_GetStringIdentifier ("ownerDocument");
1177 NPIdentifier id_getElementById = NPN_GetStringIdentifier ("getElementById");
1178 NPIdentifier id_textContent = NPN_GetStringIdentifier ("textContent");
1180 NPObject *host = GetHost();
1181 if (!host) {
1182 // printf ("no host\n");
1183 return;
1186 // get host.ownerDocument
1187 bool nperr;
1188 if (!(nperr = NPN_GetProperty (instance, host, id_ownerDocument, &_document))
1189 || !NPVARIANT_IS_OBJECT (_document)) {
1190 // printf ("no document (type == %d, nperr = %d)\n", _document.type, nperr);
1191 return;
1194 // _element = document.getElementById ('@value')
1195 string_to_npvariant (value, &_elementName);
1196 if (!(nperr = NPN_Invoke (instance, NPVARIANT_TO_OBJECT (_document), id_getElementById,
1197 &_elementName, 1, &_element))
1198 || !NPVARIANT_IS_OBJECT (_element)) {
1199 // printf ("no valid element named #%s (type = %d, nperr = %d)\n", value, _element.type, nperr);
1200 NPN_ReleaseVariantValue (&_document);
1203 // _textContent = _element.textContent
1204 if (!(nperr = NPN_GetProperty (instance, NPVARIANT_TO_OBJECT (_element), id_textContent, &_textContent))
1205 || !NPVARIANT_IS_STRING (_textContent)) {
1206 // printf ("no text content for element named #%s (type = %d, nperr = %d)\n", value, _textContent.type, nperr);
1207 NPN_ReleaseVariantValue (&_document);
1208 NPN_ReleaseVariantValue (&_element);
1209 return;
1212 char *xaml = g_strndup ((char *) NPVARIANT_TO_STRING (_textContent).utf8characters, NPVARIANT_TO_STRING (_textContent).utf8length);
1214 // printf ("yay, xaml = %s\n", xaml);
1216 if (xaml_loader)
1217 delete xaml_loader;
1219 xaml_loader = PluginXamlLoader::FromStr (NULL/*FIXME*/, xaml, this, surface);
1220 LoadXAML ();
1222 g_free (xaml);
1224 NPN_ReleaseVariantValue (&_document);
1225 NPN_ReleaseVariantValue (&_element);
1226 NPN_ReleaseVariantValue (&_textContent);
1230 Downloader *
1231 PluginInstance::CreateDownloader (PluginInstance *instance)
1233 if (instance) {
1234 return instance->surface->CreateDownloader ();
1235 } else {
1236 printf ("PluginInstance::CreateDownloader (%p): Unable to create contextual downloader.\n", instance);
1237 return new Downloader ();
1241 void
1242 PluginInstance::SetInitParams (const char *value)
1244 g_free (initParams);
1245 initParams = g_strdup (value);
1248 char*
1249 PluginInstance::GetPageLocation ()
1251 char *location = NULL;
1252 NPIdentifier str_location = NPN_GetStringIdentifier ("location");
1253 NPIdentifier str_href = NPN_GetStringIdentifier ("href");
1254 NPVariant location_property;
1255 NPVariant location_object;
1256 NPObject *window;
1258 if (NPERR_NO_ERROR == NPN_GetValue (instance, NPNVWindowNPObject, &window)) {
1259 // Get the location property from the window object (which is another object).
1260 if (NPN_GetProperty (instance, window, str_location, &location_property)) {
1261 // Get the location property from the location object.
1262 if (NPN_GetProperty (instance, location_property.value.objectValue, str_href, &location_object )) {
1263 location = g_strndup (NPVARIANT_TO_STRING (location_object).utf8characters, NPVARIANT_TO_STRING (location_object).utf8length);
1264 NPN_ReleaseVariantValue (&location_object);
1266 NPN_ReleaseVariantValue (&location_property);
1269 NPN_ReleaseObject (window);
1270 return location;
1273 void
1274 PluginInstance::SetPageURL ()
1276 if (source_location != NULL)
1277 return;
1279 char* location = GetPageLocation ();
1280 if (location && surface) {
1281 this->source_location = location;
1282 surface->SetSourceLocation (this->source_location);
1287 NPError
1288 PluginInstance::NewStream (NPMIMEType type, NPStream *stream, NPBool seekable, guint16 *stype)
1290 Deployment::SetCurrent (deployment);
1292 nps (printf ("PluginInstance::NewStream (%p, %p, %i, %p)\n", type, stream, seekable, stype));
1294 if (IS_NOTIFY_SPLASHSOURCE (stream->notifyData)) {
1295 SetPageURL ();
1297 *stype = NP_ASFILEONLY;
1298 return NPERR_NO_ERROR;
1300 if (IS_NOTIFY_SOURCE (stream->notifyData)) {
1301 // See http://developer.mozilla.org/En/Getting_the_page_URL_in_NPAPI_plugin
1303 // but don't call GetProperty inside SetWindow because it breaks opera by
1304 // causing it to reenter
1306 // this->source_location = g_strdup (stream->url);
1307 SetPageURL ();
1309 *stype = NP_ASFILE;
1310 return NPERR_NO_ERROR;
1313 if (IS_NOTIFY_DOWNLOADER (stream->notifyData)) {
1314 StreamNotify *notify = (StreamNotify *) stream->notifyData;
1315 Downloader *dl = (Downloader *) notify->pdata;
1316 // check if (a) it's a redirection and (b) if it is allowed for the current downloader policy
1317 if (!dl->CheckRedirectionPolicy (stream->url))
1318 return NPERR_INVALID_URL;
1320 npstream_request_set_stream_data (dl, instance, stream);
1321 *stype = NP_ASFILE;
1322 return NPERR_NO_ERROR;
1325 *stype = NP_NORMAL;
1327 return NPERR_NO_ERROR;
1330 NPError
1331 PluginInstance::DestroyStream (NPStream *stream, NPError reason)
1333 nps (printf ("PluginInstance::DestroyStream (%p, %i)\n", stream, reason));
1335 PluginDownloader *pd = (PluginDownloader*) stream->pdata;
1336 if (pd != NULL) {
1337 NPStreamRequest *req = (NPStreamRequest *) pd->getRequest ();
1338 if (req != NULL)
1339 req->StreamDestroyed ();
1342 return NPERR_NO_ERROR;
1346 // Tries to load the XAML file, the parsing might fail because a
1347 // required dependency is not available, so we need to queue the
1348 // request to fetch the data.
1350 bool
1351 PluginInstance::LoadXAML ()
1353 int error = 0;
1356 // Only try to load if there's no missing files.
1358 Surface *our_surface = surface;
1359 AddCleanupPointer (&our_surface);
1361 if (!deployment->InitializeManagedDeployment (this, NULL, culture, uiCulture))
1362 return false;
1364 xaml_loader->LoadVM ();
1366 MoonlightScriptControlObject *root = GetRootObject ();
1367 if (!loading_splash) {
1368 register_event (instance, "onLoad", onLoad, root);
1369 //register_event (instance, "onError", onError, root);
1370 register_event (instance, "onResize", onResize, root->content);
1371 is_splash = false;
1372 loading_splash = false;
1373 } else {
1374 register_event (instance, "onLoad", (char*)"", root);
1375 //register_event (instance, "onError", "", root);
1376 register_event (instance, "onResize", (char*)"", root->content);
1377 is_splash = true;
1378 loading_splash = false;
1381 xaml_loader->TryLoad (&error);
1383 if (!our_surface)
1384 return false;
1386 RemoveCleanupPointer (&our_surface);
1388 return true;
1391 #if PLUGIN_SL_2_0
1393 // Loads a XAP file
1395 bool
1396 PluginInstance::LoadXAP (const char *url, const char *fname)
1398 g_free (source_location);
1400 source_location = g_strdup (url);
1402 MoonlightScriptControlObject *root = GetRootObject ();
1404 register_event (instance, "onLoad", onLoad, root);
1405 //register_event (instance, "onError", onError, root);
1406 register_event (instance, "onResize", onResize, root->content);
1407 loading_splash = false;
1408 is_splash = false;
1410 Deployment::GetCurrent ()->Reinitialize ();
1411 GetDeployment()->SetXapLocation (url);
1412 return GetDeployment ()->InitializeManagedDeployment (this, fname, culture, uiCulture);
1415 void
1416 PluginInstance::DestroyApplication ()
1418 GetDeployment ()->DestroyManagedApplication (this);
1420 #endif
1423 * Prepares a string to be passed to Javascript, escapes the " and '
1424 * characters and maps the newline to \n sequence and cr to \r sequence
1426 static char*
1427 string_to_js (char *s)
1429 char *res;
1430 GString *result;
1432 if (strchr (s, '\'') == NULL && strchr (s, '\n') == NULL)
1433 return g_strdup (s);
1435 result = g_string_new ("");
1437 for (char *p = s; *p != 0; *p++){
1438 if (*p == '"' || *p == '\''){
1439 g_string_append_c (result, '\\');
1440 g_string_append_c (result, *p);
1441 } else if (*p == '\n'){
1442 g_string_append_c (result, '\\');
1443 g_string_append_c (result, 'n');
1444 } else if (*p == '\r'){
1445 g_string_append_c (result, '\\');
1446 g_string_append_c (result, 'r');
1447 } else
1448 g_string_append_c (result, *p);
1451 res = result->str;
1452 g_string_free (result, FALSE);
1454 return res;
1457 void
1458 PluginInstance::ReportException (char *msg, char *details, char **stack_trace, int num_frames)
1460 NPObject *object;
1461 NPVariant result;
1462 char *script, *row_js, *msg_escaped, *details_escaped;
1463 char **stack_trace_escaped;
1464 NPString str;
1465 int i;
1466 bool res;
1468 // Get a reference to our element
1469 object = GetHost();
1470 if (!object)
1471 return;
1473 // FIXME:
1474 // - make sure the variables do not become global
1476 // Remove ' from embedded strings
1477 msg_escaped = string_to_js (msg);
1478 details_escaped = string_to_js (details);
1479 stack_trace_escaped = g_new0 (char*, num_frames);
1480 for (i = 0; i < num_frames; ++i)
1481 stack_trace_escaped [i] = string_to_js (stack_trace [i]);
1483 // JS code to create our elements
1484 row_js = g_strdup (" ");
1485 for (i = 0; i < num_frames; ++i) {
1486 char *s;
1488 s = g_strdup_printf ("%s%s%s", row_js, (i == 0) ? "" : "\\n ", stack_trace_escaped [i]);
1489 g_free (row_js);
1490 row_js = s;
1493 script = g_strdup_printf ("text1 = document.createTextNode ('%s'); text2 = document.createTextNode ('Exception Details: '); text3 = document.createTextNode ('%s'); text4 = document.createTextNode ('Stack Trace:'); parent = this.parentNode; a = document.createElement ('div'); a.appendChild (document.createElement ('hr')); msg = document.createElement ('font'); a.appendChild (msg); h2 = document.createElement ('h2'); i = document.createElement ('i'); b = document.createElement ('b'); msg.appendChild (h2); msg.appendChild (b); msg.appendChild (text3); msg.appendChild (document.createElement ('br')); msg.appendChild (document.createElement ('br')); b2 = document.createElement ('b'); b2.appendChild (text4); msg.appendChild (b2); b.appendChild (text2); h2.appendChild (i); i.appendChild (text1); msg.appendChild (document.createElement ('br')); msg.appendChild (document.createElement ('br')); a.appendChild (document.createElement ('hr')); table = document.createElement ('table'); msg.appendChild (table); table.width = '100%%'; table.bgColor = '#ffffcc'; tbody = document.createElement ('tbody'); table.appendChild (tbody); tr = document.createElement ('tr'); tbody.appendChild (tr); td = document.createElement ('td'); tr.appendChild (td); pre = document.createElement ('pre'); td.appendChild (pre); text = document.createTextNode ('%s'); pre.appendChild (text); previous = parent.firstChild; if (parent.firstChild.tagName == 'DIV') parent.removeChild (parent.firstChild); parent.insertBefore (a, this)", msg_escaped, details_escaped, row_js);
1495 g_free (msg_escaped);
1496 g_free (details_escaped);
1497 for (i = 0; i < num_frames; ++i)
1498 g_free (stack_trace_escaped [i]);
1499 g_free (stack_trace_escaped);
1500 g_free (row_js);
1502 str.utf8characters = script;
1503 str.utf8length = strlen (script);
1505 res = NPN_Evaluate (instance, object, &str, &result);
1506 if (res)
1507 NPN_ReleaseVariantValue (&result);
1508 NPN_ReleaseObject (object);
1509 g_free (script);
1512 void *
1513 PluginInstance::Evaluate (const char *code)
1515 NPObject *object = GetHost ();
1516 NPString string;
1517 NPVariant npresult;
1519 if (object == NULL)
1520 return NULL;
1523 string.utf8characters = code;
1524 string.utf8length = strlen (code);
1526 bool ret = NPN_Evaluate (instance, object, &string, &npresult);
1528 Value *res = NULL;
1529 bool keep_ref = false;
1530 if (ret) {
1531 if (!NPVARIANT_IS_VOID (npresult) && !NPVARIANT_IS_NULL (npresult)) {
1532 variant_to_value (&npresult, &res);
1533 if (npresult.type == NPVariantType_Object)
1534 keep_ref = true;
1538 if (!keep_ref)
1539 NPN_ReleaseVariantValue (&npresult);
1541 return (void*)res;
1544 void
1545 PluginInstance::CrossDomainApplicationCheck (const char *source)
1547 char* page_url = GetPageLocation ();
1548 // note: source might not be an absolute URL at this stage - but that only indicates that it's relative to the page url
1549 cross_domain_app = !same_site_of_origin (page_url, source);
1550 if (!cross_domain_app) {
1551 // we need also to consider a web page having cross-domain XAP that redirects to a XAP on the SOO
1552 // this will still be considered a cross-domain
1553 cross_domain_app = !same_site_of_origin (page_url, source_original);
1555 g_free (page_url);
1557 // if the application did not specify 'enablehtmlaccess' then we use its default value
1558 // which is TRUE for same-site applications and FALSE for cross-domain applications
1559 if (default_enable_html_access)
1560 enable_html_access = !cross_domain_app;
1563 static bool
1564 is_xap (const char *fname)
1566 // Check for the ZIP magic header
1568 int fd;
1569 int nread;
1570 char buf[4];
1572 if ((fd = open (fname, O_RDONLY)) == -1)
1573 return false;
1575 nread = read (fd, buf, 4);
1576 if (nread != 4) {
1577 close (fd);
1578 return false;
1581 if (buf [0] != 0x50 || buf [1] != 0x4B || buf [2] != 0x03 || buf [3] != 0x04) {
1582 close (fd);
1583 return false;
1586 close (fd);
1587 return true;
1590 void
1591 PluginInstance::StreamAsFile (NPStream *stream, const char *fname)
1593 nps (printf ("PluginInstance::StreamAsFile (%p, %s)\n", stream, fname));
1595 Deployment::SetCurrent (deployment);
1596 #if DEBUG
1597 AddSource (stream->url, fname);
1598 #endif
1599 if (IS_NOTIFY_SPLASHSOURCE (stream->notifyData)) {
1600 xaml_loader = PluginXamlLoader::FromFilename (stream->url, fname, this, surface);
1601 loading_splash = true;
1602 LoadXAML ();
1603 FlushSplash ();
1605 CrossDomainApplicationCheck (source);
1607 if (IS_NOTIFY_SOURCE (stream->notifyData)) {
1608 delete xaml_loader;
1609 xaml_loader = NULL;
1611 CrossDomainApplicationCheck (stream->url);
1613 Uri *uri = new Uri ();
1616 if (uri->Parse (stream->url, false) && is_xap (fname)) {
1617 LoadXAP (stream->url, fname);
1618 } else {
1619 xaml_loader = PluginXamlLoader::FromFilename (stream->url, fname, this, surface);
1620 LoadXAML ();
1623 GetSurface ()->EmitSourceDownloadProgressChanged (new DownloadProgressEventArgs (1.0));
1624 GetSurface ()->EmitSourceDownloadComplete ();
1626 delete uri;
1627 } else if (IS_NOTIFY_DOWNLOADER (stream->notifyData)){
1628 Downloader *dl = (Downloader *) ((StreamNotify *)stream->notifyData)->pdata;
1630 dl->SetFilename (fname);
1634 gint32
1635 PluginInstance::WriteReady (NPStream *stream)
1637 nps (printf ("PluginInstance::WriteReady (%p)\n", stream));
1639 Deployment::SetCurrent (deployment);
1641 StreamNotify *notify = STREAM_NOTIFY (stream->notifyData);
1643 if (notify && notify->pdata) {
1644 if (IS_NOTIFY_DOWNLOADER (notify)) {
1645 Downloader *dl = (Downloader *) notify->pdata;
1647 dl->NotifySize (stream->end);
1649 return MAX_STREAM_SIZE;
1651 if (IS_NOTIFY_SOURCE (notify)) {
1652 source_size = stream->end;
1654 return MAX_STREAM_SIZE;
1658 NPN_DestroyStream (instance, stream, NPRES_DONE);
1660 return -1;
1663 gint32
1664 PluginInstance::Write (NPStream *stream, gint32 offset, gint32 len, void *buffer)
1666 nps (printf ("PluginInstance::Write (%p, %i, %i, %p)\n", stream, offset, len, buffer));
1668 Deployment::SetCurrent (deployment);
1670 StreamNotify *notify = STREAM_NOTIFY (stream->notifyData);
1672 if (notify && notify->pdata) {
1673 if (IS_NOTIFY_DOWNLOADER (notify)) {
1674 Downloader *dl = (Downloader *) notify->pdata;
1676 dl->Write (buffer, offset, len);
1678 if (IS_NOTIFY_SOURCE (notify)) {
1679 if (source_size > 0) {
1680 float progress = (offset+len)/(float)source_size;
1681 if (GetSurface ()->GetToplevel () != NULL) {
1682 GetSurface ()->EmitSourceDownloadProgressChanged (new DownloadProgressEventArgs (progress));
1688 return len;
1691 class PluginClosure : public EventObject {
1692 public:
1693 PluginClosure (PluginInstance *plugin)
1694 : plugin (plugin)
1698 virtual ~PluginClosure ()
1702 PluginInstance *plugin;
1705 void
1706 PluginInstance::network_error_tickcall (EventObject *data)
1708 PluginClosure *closure = (PluginClosure*)data;
1709 Surface *s = closure->plugin->GetSurface();
1711 s->EmitError (new ErrorEventArgs (RuntimeError,
1712 MoonError (MoonError::EXCEPTION, 2104, "Failed to download silverlight application.")));
1715 void
1716 PluginInstance::splashscreen_error_tickcall (EventObject *data)
1718 PluginClosure *closure = (PluginClosure*)data;
1719 Surface *s = closure->plugin->GetSurface();
1721 s->EmitError (new ErrorEventArgs (RuntimeError,
1722 MoonError (MoonError::EXCEPTION, 2108, "Failed to download the splash screen")));
1723 closure->plugin->is_splash = false;
1725 // we need this check beccause the plugin might have been
1726 // dtor'ed (and the surface zombified) in the amove EmitError.
1727 if (!s->IsZombie())
1728 closure->plugin->UpdateSource ();
1730 closure->unref();
1733 void
1734 PluginInstance::UrlNotify (const char *url, NPReason reason, void *notifyData)
1736 nps (printf ("PluginInstance::UrlNotify (%s, %i, %p)\n", url, reason, notifyData));
1738 StreamNotify *notify = STREAM_NOTIFY (notifyData);
1740 Deployment::SetCurrent (deployment);
1742 if (reason == NPRES_DONE) {
1743 d(printf ("URL %s downloaded successfully.\n", url));
1744 } else {
1745 d(printf ("Download of URL %s failed: %i (%s)\n", url, reason,
1746 reason == NPRES_USER_BREAK ? "user break" :
1747 (reason == NPRES_NETWORK_ERR ? "network error" : "other error")));
1748 if (IS_NOTIFY_SOURCE (notify))
1749 GetSurface()->GetTimeManager()->AddTickCall (network_error_tickcall,
1750 new PluginClosure (this));
1753 if (notify && notify->pdata && IS_NOTIFY_DOWNLOADER (notify)) {
1754 Downloader *dl = (Downloader *) notify->pdata;
1756 if (reason != NPRES_DONE) {
1758 switch (reason) {
1759 case NPRES_USER_BREAK:
1760 dl->NotifyFailed ("user break");
1761 break;
1762 case NPRES_NETWORK_ERR:
1763 dl->NotifyFailed ("network error");
1764 break;
1765 default:
1766 dl->NotifyFailed ("unknown error");
1767 break;
1769 } else {
1770 dl->NotifyFinished (url);
1774 if (notify && notify->pdata && IS_NOTIFY_SPLASHSOURCE (notify)) {
1775 if (reason == NPRES_NETWORK_ERR)
1776 GetSurface()->GetTimeManager()->AddTickCall (splashscreen_error_tickcall,
1777 new PluginClosure (this));
1778 else
1779 UpdateSource ();
1782 if (notify)
1783 delete notify;
1786 bool
1787 PluginInstance::LoadSplash ()
1789 if (splashscreensource != NULL) {
1790 char *pos = strchr (splashscreensource, '#');
1791 if (pos) {
1792 char *original = splashscreensource;
1793 splashscreensource = g_strdup (pos + 1);
1794 g_free (original);
1795 loading_splash = true;
1796 UpdateSourceByReference (splashscreensource);
1797 FlushSplash ();
1798 // this CrossDomainApplicationCheck comes
1799 // after FlushSplash because in cases where a
1800 // XDomain XAP uses a local (to the page)
1801 // splash xaml, the loaded event is fired but
1802 // not the progress events (App7.xap from drt
1803 // #283)
1804 CrossDomainApplicationCheck (source);
1805 UpdateSource ();
1806 } else {
1807 bool cross_domain_splash = false;
1809 Uri *splash_uri = new Uri ();
1810 Uri *page_uri = new Uri ();
1811 Uri *source_uri = new Uri ();
1812 char *page_location = GetPageLocation ();
1814 if (page_uri->Parse (page_location) &&
1815 source_uri->Parse (source) &&
1816 splash_uri->Parse (splashscreensource)) {
1818 if (source_uri->isAbsolute && !splash_uri->isAbsolute) {
1819 // in the case where the xap is at an
1820 // absolute xdomain url and the splash
1821 // xaml file is relative (to the page
1822 // url). We can't do a straight
1823 // SameSiteOfOrigin check because in
1824 // SL no error (no events at all) is
1825 // raised during the splash (App6.xap
1826 // and App8.xap from drt #283)
1827 CrossDomainApplicationCheck (source);
1829 else {
1830 // resolve both xap and splash urls so
1831 // we can see if they're from the same
1832 // site.
1834 // (see App4.xap, App9.xap from drt #283 for this bit)
1836 if (!source_uri->isAbsolute) {
1837 Uri *temp = new Uri();
1838 Uri::Copy (page_uri, temp);
1839 temp->Combine (source_uri);
1840 delete source_uri;
1841 source_uri = temp;
1843 if (!splash_uri->isAbsolute) {
1844 Uri *temp = new Uri();
1845 Uri::Copy (page_uri, temp);
1846 temp->Combine (splash_uri);
1847 delete splash_uri;
1848 splash_uri = temp;
1851 if (source_uri->isAbsolute || splash_uri->isAbsolute)
1852 cross_domain_splash = !Uri::SameSiteOfOrigin (source_uri, splash_uri);
1856 g_free (page_location);
1857 delete page_uri;
1858 delete source_uri;
1859 delete splash_uri;
1861 if (cross_domain_splash) {
1862 surface->EmitError (new ErrorEventArgs (RuntimeError,
1863 MoonError (MoonError::EXCEPTION, 2107, "Splash screens only available on same site as xap")));
1864 UpdateSource ();
1865 return false;
1867 else {
1868 StreamNotify *notify = new StreamNotify (StreamNotify::SPLASHSOURCE, splashscreensource);
1870 // FIXME: check for errors
1871 NPN_GetURLNotify (instance, splashscreensource, NULL, notify);
1874 } else {
1875 // this check is for both local and xdomain xaps which
1876 // have a null splash, in the local case we get no
1877 // splash load event, but progress events, and in the
1878 // xdomain case we get no events at all. (App0.xap and
1879 // App5.xap from drt #283)
1880 CrossDomainApplicationCheck (source);
1881 xaml_loader = PluginXamlLoader::FromStr (NULL, PLUGIN_SPINNER, this, surface);
1882 loading_splash = true;
1883 if (!LoadXAML ())
1884 return false;
1885 FlushSplash ();
1886 UpdateSource ();
1889 return true;
1892 void
1893 PluginInstance::FlushSplash ()
1895 // FIXME we may want to flush all events here but since this is written to the
1896 // tests I'm not sure.
1898 UIElement *toplevel = GetSurface ()->GetToplevel ();
1899 if (toplevel != NULL) {
1900 toplevel->WalkTreeForLoadedHandlers (NULL, false, false);
1901 deployment->EmitLoaded ();
1903 loading_splash = false;
1906 void
1907 PluginInstance::Print (NPPrint *platformPrint)
1909 // nothing to do.
1912 int16_t
1913 PluginInstance::EventHandle (void *event)
1915 if (!surface) {
1916 g_warning ("EventHandle called before SetWindow, discarding event.");
1917 return 0;
1920 if (!windowless) {
1921 g_warning ("EventHandle called for windowed plugin, discarding event.");
1922 return 0;
1926 return ((MoonWindowless*)moon_window)->HandleEvent ((XEvent*)event);
1929 void
1930 PluginInstance::AddWrappedObject (EventObject *obj, NPObject *wrapper)
1932 g_hash_table_insert (wrapped_objects, obj, wrapper);
1935 void
1936 PluginInstance::RemoveWrappedObject (EventObject *obj)
1938 if (wrapped_objects == NULL)
1939 return;
1940 g_hash_table_remove (wrapped_objects, obj);
1943 NPObject*
1944 PluginInstance::LookupWrappedObject (EventObject *obj)
1946 return (NPObject*)g_hash_table_lookup (wrapped_objects, obj);
1949 void
1950 PluginInstance::AddCleanupPointer (gpointer p)
1952 cleanup_pointers = g_slist_prepend (cleanup_pointers, p);
1955 void
1956 PluginInstance::RemoveCleanupPointer (gpointer p)
1958 cleanup_pointers = g_slist_remove (cleanup_pointers, p);
1961 /*** Getters and Setters ******************************************************/
1963 void
1964 PluginInstance::SetSource (const char *value)
1966 bool changed = false;
1967 if (source) {
1968 changed = true;
1969 g_free (source);
1970 source = NULL;
1973 if (changed) {
1974 Recreate (value);
1975 return;
1978 source = g_strdup (value);
1979 // we may not have an original set at this point (e.g. when source is set via scripting)
1980 if (!source_original)
1981 source_original = g_strdup (value);
1983 UpdateSource ();
1986 char *
1987 PluginInstance::GetBackground ()
1989 return background;
1992 bool
1993 PluginInstance::SetBackground (const char *value)
1995 g_free (background);
1996 background = g_strdup (value);
1998 if (surface) {
1999 Color *c = color_from_str (background);
2001 if (c == NULL)
2002 return false;
2004 surface->SetBackgroundColor (c);
2005 delete c;
2008 return true;
2011 bool
2012 PluginInstance::GetEnableFramerateCounter ()
2014 return enable_framerate_counter;
2017 void
2018 PluginInstance::SetEnableFramerateCounter (bool value)
2020 enable_framerate_counter = value;
2023 bool
2024 PluginInstance::GetEnableRedrawRegions ()
2026 return moonlight_flags & RUNTIME_INIT_SHOW_EXPOSE;
2029 void
2030 PluginInstance::SetEnableRedrawRegions (bool value)
2032 if (value)
2033 moonlight_flags |= RUNTIME_INIT_SHOW_EXPOSE;
2034 else
2035 moonlight_flags &= ~RUNTIME_INIT_SHOW_EXPOSE;
2038 bool
2039 PluginInstance::GetEnableHtmlAccess ()
2041 return enable_html_access;
2044 bool
2045 PluginInstance::GetAllowHtmlPopupWindow ()
2047 return allow_html_popup_window;
2050 bool
2051 PluginInstance::GetWindowless ()
2053 return windowless;
2057 PluginInstance::GetMaxFrameRate ()
2059 return maxFrameRate;
2062 Deployment*
2063 PluginInstance::GetDeployment ()
2065 return deployment;
2068 void
2069 PluginInstance::SetMaxFrameRate (int value)
2071 maxFrameRate = value;
2073 surface->GetTimeManager()->SetMaximumRefreshRate (MAX (value, 64));
2076 gint32
2077 PluginInstance::GetActualHeight ()
2079 return surface && surface->GetWindow () ? surface->GetWindow()->GetHeight() : 0;
2082 gint32
2083 PluginInstance::GetActualWidth ()
2085 return surface && surface->GetWindow () ? surface->GetWindow()->GetWidth() : 0;
2088 MoonlightScriptControlObject *
2089 PluginInstance::GetRootObject ()
2091 if (rootobject == NULL)
2092 rootobject = (MoonlightScriptControlObject *) NPN_CreateObject (instance, MoonlightScriptControlClass);
2094 NPN_RetainObject (rootobject);
2095 return rootobject;
2099 PluginInstance::GetInstance ()
2101 return instance;
2104 NPWindow*
2105 PluginInstance::GetWindow ()
2107 return window;
2110 char*
2111 plugin_instance_get_id (PluginInstance *instance)
2113 return instance->GetId ();
2116 void
2117 plugin_instance_get_browser_runtime_settings (bool *debug, bool *html_access,
2118 bool *httpnet_access, bool *script_access)
2120 *debug = *html_access = *httpnet_access = *script_access = false;
2124 XamlLoader
2127 bool
2128 PluginXamlLoader::LoadVM ()
2130 #if PLUGIN_SL_2_0
2131 return InitializeLoader ();
2132 #endif
2133 return false;
2136 bool
2137 PluginXamlLoader::InitializeLoader ()
2139 if (initialized)
2140 return true;
2142 #if PLUGIN_SL_2_0
2143 if (managed_loader)
2144 return true;
2146 if (GetFilename ()) {
2147 managed_loader = plugin->ManagedCreateXamlLoaderForFile (this, GetResourceBase(), GetFilename ());
2148 } else if (GetString ()) {
2149 managed_loader = plugin->ManagedCreateXamlLoaderForString (this, GetResourceBase(), GetString ());
2150 } else {
2151 return false;
2154 initialized = managed_loader != NULL;
2155 #else
2156 initialized = true;
2157 #endif
2158 return initialized;
2162 // On error it sets the @error ref to 1
2163 // Returns the filename that we are missing
2165 void
2166 PluginXamlLoader::TryLoad (int *error)
2168 DependencyObject *element;
2169 Type::Kind element_type;
2171 *error = 0;
2173 //d(printf ("PluginXamlLoader::TryLoad, filename: %s, str: %s\n", GetFilename (), GetString ()));
2175 GetSurface ()->Attach (NULL);
2177 if (GetFilename ()) {
2178 element = CreateDependencyObjectFromFile (GetFilename (), true, &element_type);
2179 } else if (GetString ()) {
2180 element = CreateDependencyObjectFromString (GetString (), true, &element_type);
2181 } else {
2182 *error = 1;
2183 return;
2186 if (!element) {
2187 if (error_args && error_args->GetErrorCode() != -1) {
2188 d(printf ("PluginXamlLoader::TryLoad: Could not load xaml %s: %s (error: %s attr=%s)\n",
2189 GetFilename () ? "file" : "string", GetFilename () ? GetFilename () : GetString (),
2190 error_args->xml_element, error_args->xml_attribute));
2191 error_args->ref ();
2192 GetSurface ()->EmitError (error_args);
2193 return;
2194 } else {
2195 return;
2199 Type *t = Type::Find(element_type);
2200 if (!t) {
2201 d(printf ("PluginXamlLoader::TryLoad: Return value does not subclass Canvas, it is an unregistered type\n"));
2202 element->unref ();
2203 GetSurface ()->EmitError (new ErrorEventArgs (RuntimeError,
2204 MoonError (MoonError::EXCEPTION, 2101, "Failed to initialize the application's root visual")));
2205 return;
2208 if (!t->IsSubclassOf(Type::PANEL)) {
2209 d(printf ("PluginXamlLoader::TryLoad: Return value does not subclass of Panel, it is a %s\n",
2210 element->GetTypeName ()));
2211 element->unref ();
2212 GetSurface ()->EmitError (new ErrorEventArgs (RuntimeError,
2213 MoonError (MoonError::EXCEPTION, 2101, "Failed to initialize the application's root visual")));
2214 return;
2217 //d(printf ("PluginXamlLoader::TryLoad () succeeded.\n"));
2219 GetSurface ()->Attach ((Panel*) element);
2221 // xaml_create_from_* passed us a ref which we don't need to
2222 // keep.
2223 element->unref ();
2225 return;
2228 bool
2229 PluginXamlLoader::SetProperty (void *parser, Value *top_level, const char *xmlns, Value* target, void* target_data, Value *target_parent, const char *prop_xmlns, const char *name, Value* value, void* value_data, int flags)
2231 if (XamlLoader::SetProperty (parser, top_level, xmlns, target, target_data, target_parent, prop_xmlns, name, value, value_data))
2232 return true;
2234 if (value->GetKind () != Type::STRING)
2235 return false;
2237 if (!xaml_is_valid_event_name (target->GetKind(), name, false))
2238 return false;
2240 const char* function_name = value->AsString ();
2242 if (!strncmp (function_name, "javascript:", strlen ("javascript:")))
2243 return false;
2245 event_object_add_xaml_listener ((EventObject *) target->AsDependencyObject (), plugin, name, function_name);
2247 return true;
2250 PluginXamlLoader::PluginXamlLoader (const char *resourceBase, const char *filename, const char *str, PluginInstance *plugin, Surface *surface)
2251 : XamlLoader (resourceBase, filename, str, surface)
2253 this->plugin = plugin;
2254 xaml_is_managed = false;
2255 initialized = false;
2256 error_args = NULL;
2258 #if PLUGIN_SL_2_0
2259 xap = NULL;
2261 managed_loader = NULL;
2262 #endif
2265 PluginXamlLoader::~PluginXamlLoader ()
2267 #if PLUGIN_SL_2_0
2268 if (xap)
2269 delete xap;
2271 if (managed_loader)
2272 plugin->GetDeployment ()->DestroyManagedXamlLoader (managed_loader);
2273 #endif
2276 PluginXamlLoader *
2277 plugin_xaml_loader_from_str (const char *resourceBase, const char *str, PluginInstance *plugin, Surface *surface)
2279 return PluginXamlLoader::FromStr (resourceBase, str, plugin, surface);
2282 bool
2283 PluginInstance::CreatePluginDeployment ()
2285 deployment = new Deployment ();
2286 Deployment::SetCurrent (deployment);
2289 * Give a ref to the deployment, this is required since managed code has
2290 * pointers to this PluginInstance instance. This way we ensure that the
2291 * PluginInstance isn't deleted before managed code has shutdown.
2292 * We unref just after the appdomain is unloaded (in the event handler).
2294 ref ();
2295 deployment->AddHandler (Deployment::AppDomainUnloadedEvent, AppDomainUnloadedEventCallback, this);
2297 if (!deployment->InitializeAppDomain ()) {
2298 g_warning ("Moonlight: Couldn't initialize the AppDomain");
2299 return false;
2302 return true;
2305 void
2306 PluginInstance::AppDomainUnloadedEventHandler (Deployment *deployment, EventArgs *args)
2308 unref (); /* See comment in CreatePluginDeployment */
2311 gpointer
2312 PluginInstance::ManagedCreateXamlLoaderForFile (XamlLoader *native_loader, const char *resourceBase, const char *file)
2314 return GetDeployment ()->CreateManagedXamlLoader (this, native_loader, resourceBase, file, NULL);
2317 gpointer
2318 PluginInstance::ManagedCreateXamlLoaderForString (XamlLoader* native_loader, const char *resourceBase, const char *str)
2320 return GetDeployment ()->CreateManagedXamlLoader (this, native_loader, resourceBase, NULL, str);
2323 gint32
2324 PluginInstance::GetPluginCount ()
2326 return g_slist_length (plugin_instances);