* MoonlightTypeConverter.cs: Convert CacheMode's from strings.
[moon.git] / plugin / plugin.cpp
blob7f23a2a91772e159859677582494a90d1a6d5c05
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 (), true);
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);
1136 // we're changing the page url as well as the xaml
1137 // location, so we need to call SetPageUrl.
1138 // SetPageUrl calls SetSourceLocation on the surface,
1139 // so we don't need to include the call here.
1140 SetPageURL ();
1141 } else {
1142 // we're setting the source location but not changing
1143 // the page location, so we need to call
1144 // SetSourceLocation here.
1145 Uri *page_uri = new Uri ();
1146 Uri *source_uri = new Uri ();
1148 char *page_location = GetPageLocation ();
1150 if (page_uri->Parse (page_location, true) &&
1151 source_uri->Parse (source, true)) {
1153 // apparently we only do this with a xap? ugh...
1155 if (source_uri->path
1156 && strlen (source_uri->path) > 4
1157 && !strncmp (source_uri->path + strlen (source_uri->path) - 4, ".xap", 4)) {
1159 if (!source_uri->isAbsolute) {
1160 Uri *temp = new Uri();
1161 Uri::Copy (page_uri, temp);
1162 temp->Combine (source_uri);
1163 delete source_uri;
1164 source_uri = temp;
1167 char* source_string = source_uri->ToString();
1169 surface->SetSourceLocation (source_string);
1171 g_free (source_string);
1175 g_free (page_location);
1176 delete page_uri;
1177 delete source_uri;
1179 StreamNotify *notify = new StreamNotify (StreamNotify::SOURCE, source);
1181 // FIXME: check for errors
1182 NPN_GetURLNotify (instance, source, NULL, notify);
1186 gboolean
1187 PluginInstance::IdleUpdateSourceByReference (gpointer data)
1189 PluginInstance *instance = (PluginInstance*)data;
1190 char *pos = NULL;
1192 instance->source_idle = 0;
1194 if (instance->source)
1195 pos = strchr (instance->source, '#');
1197 if (pos && strlen (pos+1) > 0)
1198 instance->UpdateSourceByReference (pos+1);
1200 instance->GetSurface ()->EmitSourceDownloadProgressChanged (new DownloadProgressEventArgs (1.0));
1201 instance->GetSurface ()->EmitSourceDownloadComplete ();
1202 return FALSE;
1205 void
1206 PluginInstance::UpdateSourceByReference (const char *value)
1208 // basically do the equivalent of document.getElementById('@value').textContent
1209 // all using NPAPI.
1211 NPVariant _document;
1212 NPVariant _element;
1213 NPVariant _elementName;
1214 NPVariant _textContent;
1216 Deployment::SetCurrent (deployment);
1218 NPIdentifier id_ownerDocument = NPN_GetStringIdentifier ("ownerDocument");
1219 NPIdentifier id_getElementById = NPN_GetStringIdentifier ("getElementById");
1220 NPIdentifier id_textContent = NPN_GetStringIdentifier ("textContent");
1222 NPObject *host = GetHost();
1223 if (!host) {
1224 // printf ("no host\n");
1225 return;
1228 // get host.ownerDocument
1229 bool nperr;
1230 if (!(nperr = NPN_GetProperty (instance, host, id_ownerDocument, &_document))
1231 || !NPVARIANT_IS_OBJECT (_document)) {
1232 // printf ("no document (type == %d, nperr = %d)\n", _document.type, nperr);
1233 return;
1236 // _element = document.getElementById ('@value')
1237 string_to_npvariant (value, &_elementName);
1238 if (!(nperr = NPN_Invoke (instance, NPVARIANT_TO_OBJECT (_document), id_getElementById,
1239 &_elementName, 1, &_element))
1240 || !NPVARIANT_IS_OBJECT (_element)) {
1241 // printf ("no valid element named #%s (type = %d, nperr = %d)\n", value, _element.type, nperr);
1242 NPN_ReleaseVariantValue (&_document);
1245 // _textContent = _element.textContent
1246 if (!(nperr = NPN_GetProperty (instance, NPVARIANT_TO_OBJECT (_element), id_textContent, &_textContent))
1247 || !NPVARIANT_IS_STRING (_textContent)) {
1248 // printf ("no text content for element named #%s (type = %d, nperr = %d)\n", value, _textContent.type, nperr);
1249 NPN_ReleaseVariantValue (&_document);
1250 NPN_ReleaseVariantValue (&_element);
1251 return;
1254 char *xaml = g_strndup ((char *) NPVARIANT_TO_STRING (_textContent).utf8characters, NPVARIANT_TO_STRING (_textContent).utf8length);
1256 // printf ("yay, xaml = %s\n", xaml);
1258 if (xaml_loader)
1259 delete xaml_loader;
1261 xaml_loader = PluginXamlLoader::FromStr (NULL/*FIXME*/, xaml, this, surface);
1262 LoadXAML ();
1264 g_free (xaml);
1266 NPN_ReleaseVariantValue (&_document);
1267 NPN_ReleaseVariantValue (&_element);
1268 NPN_ReleaseVariantValue (&_textContent);
1272 Downloader *
1273 PluginInstance::CreateDownloader (PluginInstance *instance)
1275 if (instance) {
1276 return instance->surface->CreateDownloader ();
1277 } else {
1278 printf ("PluginInstance::CreateDownloader (%p): Unable to create contextual downloader.\n", instance);
1279 return new Downloader ();
1283 void
1284 PluginInstance::SetInitParams (const char *value)
1286 g_free (initParams);
1287 initParams = g_strdup (value);
1290 char*
1291 PluginInstance::GetPageLocation ()
1293 char *location = NULL;
1294 NPIdentifier str_location = NPN_GetStringIdentifier ("location");
1295 NPIdentifier str_href = NPN_GetStringIdentifier ("href");
1296 NPVariant location_property;
1297 NPVariant location_object;
1298 NPObject *window;
1300 if (NPERR_NO_ERROR == NPN_GetValue (instance, NPNVWindowNPObject, &window)) {
1301 // Get the location property from the window object (which is another object).
1302 if (NPN_GetProperty (instance, window, str_location, &location_property)) {
1303 // Get the location property from the location object.
1304 if (NPN_GetProperty (instance, location_property.value.objectValue, str_href, &location_object )) {
1305 location = g_strndup (NPVARIANT_TO_STRING (location_object).utf8characters, NPVARIANT_TO_STRING (location_object).utf8length);
1306 NPN_ReleaseVariantValue (&location_object);
1308 NPN_ReleaseVariantValue (&location_property);
1311 NPN_ReleaseObject (window);
1312 return location;
1315 void
1316 PluginInstance::SetPageURL ()
1318 if (source_location != NULL)
1319 return;
1321 char* location = GetPageLocation ();
1322 if (location && surface) {
1323 this->source_location = location;
1324 surface->SetSourceLocation (this->source_location);
1329 NPError
1330 PluginInstance::NewStream (NPMIMEType type, NPStream *stream, NPBool seekable, guint16 *stype)
1332 Deployment::SetCurrent (deployment);
1334 nps (printf ("PluginInstance::NewStream (%p, %p, %i, %p)\n", type, stream, seekable, stype));
1336 if (IS_NOTIFY_SPLASHSOURCE (stream->notifyData)) {
1337 SetPageURL ();
1339 *stype = NP_ASFILEONLY;
1340 return NPERR_NO_ERROR;
1342 if (IS_NOTIFY_SOURCE (stream->notifyData)) {
1343 // See http://developer.mozilla.org/En/Getting_the_page_URL_in_NPAPI_plugin
1345 // but don't call GetProperty inside SetWindow because it breaks opera by
1346 // causing it to reenter
1348 // this->source_location = g_strdup (stream->url);
1349 SetPageURL ();
1351 *stype = NP_ASFILE;
1352 return NPERR_NO_ERROR;
1355 if (IS_NOTIFY_DOWNLOADER (stream->notifyData)) {
1356 StreamNotify *notify = (StreamNotify *) stream->notifyData;
1357 Downloader *dl = (Downloader *) notify->pdata;
1358 // check if (a) it's a redirection and (b) if it is allowed for the current downloader policy
1359 if (!dl->CheckRedirectionPolicy (stream->url))
1360 return NPERR_INVALID_URL;
1362 npstream_request_set_stream_data (dl, instance, stream);
1363 *stype = NP_ASFILE;
1364 return NPERR_NO_ERROR;
1367 *stype = NP_NORMAL;
1369 return NPERR_NO_ERROR;
1372 NPError
1373 PluginInstance::DestroyStream (NPStream *stream, NPError reason)
1375 nps (printf ("PluginInstance::DestroyStream (%p, %i)\n", stream, reason));
1377 PluginDownloader *pd = (PluginDownloader*) stream->pdata;
1378 if (pd != NULL) {
1379 NPStreamRequest *req = (NPStreamRequest *) pd->getRequest ();
1380 if (req != NULL)
1381 req->StreamDestroyed ();
1384 return NPERR_NO_ERROR;
1388 // Tries to load the XAML file, the parsing might fail because a
1389 // required dependency is not available, so we need to queue the
1390 // request to fetch the data.
1392 bool
1393 PluginInstance::LoadXAML ()
1395 int error = 0;
1398 // Only try to load if there's no missing files.
1400 Surface *our_surface = surface;
1401 AddCleanupPointer (&our_surface);
1403 if (!deployment->InitializeManagedDeployment (this, NULL, culture, uiCulture))
1404 return false;
1406 xaml_loader->LoadVM ();
1408 MoonlightScriptControlObject *root = GetRootObject ();
1409 if (!loading_splash) {
1410 register_event (instance, "onLoad", onLoad, root);
1411 //register_event (instance, "onError", onError, root);
1412 register_event (instance, "onResize", onResize, root->content);
1413 is_splash = false;
1414 loading_splash = false;
1415 } else {
1416 register_event (instance, "onLoad", (char*)"", root);
1417 //register_event (instance, "onError", "", root);
1418 register_event (instance, "onResize", (char*)"", root->content);
1419 is_splash = true;
1420 loading_splash = false;
1423 xaml_loader->TryLoad (&error);
1425 if (!our_surface)
1426 return false;
1428 RemoveCleanupPointer (&our_surface);
1430 return true;
1433 #if PLUGIN_SL_2_0
1435 // Loads a XAP file
1437 bool
1438 PluginInstance::LoadXAP (const char *url, const char *fname)
1440 g_free (source_location);
1442 source_location = g_strdup (url);
1444 MoonlightScriptControlObject *root = GetRootObject ();
1446 register_event (instance, "onLoad", onLoad, root);
1447 //register_event (instance, "onError", onError, root);
1448 register_event (instance, "onResize", onResize, root->content);
1449 loading_splash = false;
1450 is_splash = false;
1452 Deployment::GetCurrent ()->Reinitialize ();
1453 GetDeployment()->SetXapLocation (url);
1454 return GetDeployment ()->InitializeManagedDeployment (this, fname, culture, uiCulture);
1457 void
1458 PluginInstance::DestroyApplication ()
1460 GetDeployment ()->DestroyManagedApplication (this);
1462 #endif
1465 * Prepares a string to be passed to Javascript, escapes the " and '
1466 * characters and maps the newline to \n sequence and cr to \r sequence
1468 static char*
1469 string_to_js (char *s)
1471 char *res;
1472 GString *result;
1474 if (strchr (s, '\'') == NULL && strchr (s, '\n') == NULL)
1475 return g_strdup (s);
1477 result = g_string_new ("");
1479 for (char *p = s; *p != 0; *p++){
1480 if (*p == '"' || *p == '\''){
1481 g_string_append_c (result, '\\');
1482 g_string_append_c (result, *p);
1483 } else if (*p == '\n'){
1484 g_string_append_c (result, '\\');
1485 g_string_append_c (result, 'n');
1486 } else if (*p == '\r'){
1487 g_string_append_c (result, '\\');
1488 g_string_append_c (result, 'r');
1489 } else
1490 g_string_append_c (result, *p);
1493 res = result->str;
1494 g_string_free (result, FALSE);
1496 return res;
1499 void
1500 PluginInstance::ReportException (char *msg, char *details, char **stack_trace, int num_frames)
1502 NPObject *object;
1503 NPVariant result;
1504 char *script, *row_js, *msg_escaped, *details_escaped;
1505 char **stack_trace_escaped;
1506 NPString str;
1507 int i;
1508 bool res;
1510 // Get a reference to our element
1511 object = GetHost();
1512 if (!object)
1513 return;
1515 // FIXME:
1516 // - make sure the variables do not become global
1518 // Remove ' from embedded strings
1519 msg_escaped = string_to_js (msg);
1520 details_escaped = string_to_js (details);
1521 stack_trace_escaped = g_new0 (char*, num_frames);
1522 for (i = 0; i < num_frames; ++i)
1523 stack_trace_escaped [i] = string_to_js (stack_trace [i]);
1525 // JS code to create our elements
1526 row_js = g_strdup (" ");
1527 for (i = 0; i < num_frames; ++i) {
1528 char *s;
1530 s = g_strdup_printf ("%s%s%s", row_js, (i == 0) ? "" : "\\n ", stack_trace_escaped [i]);
1531 g_free (row_js);
1532 row_js = s;
1535 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);
1537 g_free (msg_escaped);
1538 g_free (details_escaped);
1539 for (i = 0; i < num_frames; ++i)
1540 g_free (stack_trace_escaped [i]);
1541 g_free (stack_trace_escaped);
1542 g_free (row_js);
1544 str.utf8characters = script;
1545 str.utf8length = strlen (script);
1547 res = NPN_Evaluate (instance, object, &str, &result);
1548 if (res)
1549 NPN_ReleaseVariantValue (&result);
1550 NPN_ReleaseObject (object);
1551 g_free (script);
1554 void *
1555 PluginInstance::Evaluate (const char *code)
1557 NPObject *object = GetHost ();
1558 NPString string;
1559 NPVariant npresult;
1561 if (object == NULL)
1562 return NULL;
1565 string.utf8characters = code;
1566 string.utf8length = strlen (code);
1568 bool ret = NPN_Evaluate (instance, object, &string, &npresult);
1570 Value *res = NULL;
1571 bool keep_ref = false;
1572 if (ret) {
1573 if (!NPVARIANT_IS_VOID (npresult) && !NPVARIANT_IS_NULL (npresult)) {
1574 variant_to_value (&npresult, &res);
1575 if (npresult.type == NPVariantType_Object)
1576 keep_ref = true;
1580 if (!keep_ref)
1581 NPN_ReleaseVariantValue (&npresult);
1583 return (void*)res;
1586 void
1587 PluginInstance::CrossDomainApplicationCheck (const char *source)
1589 char* page_url = GetPageLocation ();
1590 // note: source might not be an absolute URL at this stage - but that only indicates that it's relative to the page url
1591 cross_domain_app = !same_site_of_origin (page_url, source);
1592 if (!cross_domain_app) {
1593 // we need also to consider a web page having cross-domain XAP that redirects to a XAP on the SOO
1594 // this will still be considered a cross-domain
1595 cross_domain_app = !same_site_of_origin (page_url, source_original);
1597 g_free (page_url);
1599 // if the application did not specify 'enablehtmlaccess' then we use its default value
1600 // which is TRUE for same-site applications and FALSE for cross-domain applications
1601 if (default_enable_html_access)
1602 enable_html_access = !cross_domain_app;
1605 static bool
1606 is_xap (const char *fname)
1608 // Check for the ZIP magic header
1610 int fd;
1611 int nread;
1612 char buf[4];
1614 if ((fd = open (fname, O_RDONLY)) == -1)
1615 return false;
1617 nread = read (fd, buf, 4);
1618 if (nread != 4) {
1619 close (fd);
1620 return false;
1623 if (buf [0] != 0x50 || buf [1] != 0x4B || buf [2] != 0x03 || buf [3] != 0x04) {
1624 close (fd);
1625 return false;
1628 close (fd);
1629 return true;
1632 void
1633 PluginInstance::StreamAsFile (NPStream *stream, const char *fname)
1635 nps (printf ("PluginInstance::StreamAsFile (%p, %s)\n", stream, fname));
1637 Deployment::SetCurrent (deployment);
1638 #if DEBUG
1639 AddSource (stream->url, fname);
1640 #endif
1641 if (IS_NOTIFY_SPLASHSOURCE (stream->notifyData)) {
1642 xaml_loader = PluginXamlLoader::FromFilename (stream->url, fname, this, surface);
1643 loading_splash = true;
1644 surface->SetSourceLocation (stream->url);
1645 LoadXAML ();
1646 FlushSplash ();
1648 CrossDomainApplicationCheck (source);
1649 SetPageURL ();
1651 if (IS_NOTIFY_SOURCE (stream->notifyData)) {
1652 delete xaml_loader;
1653 xaml_loader = NULL;
1655 CrossDomainApplicationCheck (stream->url);
1657 Uri *uri = new Uri ();
1660 if (uri->Parse (stream->url, false) && is_xap (fname)) {
1661 LoadXAP (stream->url, fname);
1662 } else {
1663 xaml_loader = PluginXamlLoader::FromFilename (stream->url, fname, this, surface);
1664 LoadXAML ();
1667 GetSurface ()->EmitSourceDownloadProgressChanged (new DownloadProgressEventArgs (1.0));
1668 GetSurface ()->EmitSourceDownloadComplete ();
1670 delete uri;
1671 } else if (IS_NOTIFY_DOWNLOADER (stream->notifyData)){
1672 Downloader *dl = (Downloader *) ((StreamNotify *)stream->notifyData)->pdata;
1674 dl->SetFilename (fname);
1678 gint32
1679 PluginInstance::WriteReady (NPStream *stream)
1681 nps (printf ("PluginInstance::WriteReady (%p)\n", stream));
1683 Deployment::SetCurrent (deployment);
1685 StreamNotify *notify = STREAM_NOTIFY (stream->notifyData);
1687 if (notify && notify->pdata) {
1688 if (IS_NOTIFY_DOWNLOADER (notify)) {
1689 Downloader *dl = (Downloader *) notify->pdata;
1691 dl->NotifySize (stream->end);
1693 return MAX_STREAM_SIZE;
1695 if (IS_NOTIFY_SOURCE (notify)) {
1696 source_size = stream->end;
1698 return MAX_STREAM_SIZE;
1702 NPN_DestroyStream (instance, stream, NPRES_DONE);
1704 return -1;
1707 gint32
1708 PluginInstance::Write (NPStream *stream, gint32 offset, gint32 len, void *buffer)
1710 nps (printf ("PluginInstance::Write (%p, %i, %i, %p)\n", stream, offset, len, buffer));
1712 Deployment::SetCurrent (deployment);
1714 StreamNotify *notify = STREAM_NOTIFY (stream->notifyData);
1716 if (notify && notify->pdata) {
1717 if (IS_NOTIFY_DOWNLOADER (notify)) {
1718 Downloader *dl = (Downloader *) notify->pdata;
1720 dl->Write (buffer, offset, len);
1722 if (IS_NOTIFY_SOURCE (notify)) {
1723 if (source_size > 0) {
1724 float progress = (offset+len)/(float)source_size;
1725 if (GetSurface ()->GetToplevel () != NULL) {
1726 GetSurface ()->EmitSourceDownloadProgressChanged (new DownloadProgressEventArgs (progress));
1732 return len;
1735 class PluginClosure : public EventObject {
1736 public:
1737 PluginClosure (PluginInstance *plugin)
1738 : plugin (plugin)
1742 virtual ~PluginClosure ()
1746 PluginInstance *plugin;
1749 void
1750 PluginInstance::network_error_tickcall (EventObject *data)
1752 PluginClosure *closure = (PluginClosure*)data;
1753 Surface *s = closure->plugin->GetSurface();
1755 s->EmitError (new ErrorEventArgs (RuntimeError,
1756 MoonError (MoonError::EXCEPTION, 2104, "Failed to download silverlight application.")));
1759 void
1760 PluginInstance::splashscreen_error_tickcall (EventObject *data)
1762 PluginClosure *closure = (PluginClosure*)data;
1763 Surface *s = closure->plugin->GetSurface();
1765 s->EmitError (new ErrorEventArgs (RuntimeError,
1766 MoonError (MoonError::EXCEPTION, 2108, "Failed to download the splash screen")));
1767 closure->plugin->is_splash = false;
1769 // we need this check beccause the plugin might have been
1770 // dtor'ed (and the surface zombified) in the amove EmitError.
1771 if (!s->IsZombie())
1772 closure->plugin->UpdateSource ();
1774 closure->unref();
1777 void
1778 PluginInstance::UrlNotify (const char *url, NPReason reason, void *notifyData)
1780 nps (printf ("PluginInstance::UrlNotify (%s, %i, %p)\n", url, reason, notifyData));
1782 StreamNotify *notify = STREAM_NOTIFY (notifyData);
1784 Deployment::SetCurrent (deployment);
1786 if (reason != NPRES_DONE) {
1787 d(printf ("Download of URL %s failed: %i (%s)\n", url, reason,
1788 reason == NPRES_USER_BREAK ? "user break" :
1789 (reason == NPRES_NETWORK_ERR ? "network error" : "other error")));
1790 if (IS_NOTIFY_SOURCE (notify))
1791 GetSurface()->GetTimeManager()->AddTickCall (network_error_tickcall,
1792 new PluginClosure (this));
1795 if (notify && notify->pdata && IS_NOTIFY_DOWNLOADER (notify)) {
1796 Downloader *dl = (Downloader *) notify->pdata;
1798 if (reason != NPRES_DONE) {
1800 switch (reason) {
1801 case NPRES_USER_BREAK:
1802 dl->NotifyFailed ("user break");
1803 break;
1804 case NPRES_NETWORK_ERR:
1805 dl->NotifyFailed ("network error");
1806 break;
1807 default:
1808 dl->NotifyFailed ("unknown error");
1809 break;
1811 } else {
1812 dl->NotifyFinished (url);
1816 if (notify && notify->pdata && IS_NOTIFY_SPLASHSOURCE (notify)) {
1817 if (reason == NPRES_NETWORK_ERR)
1818 GetSurface()->GetTimeManager()->AddTickCall (splashscreen_error_tickcall,
1819 new PluginClosure (this));
1820 else
1821 UpdateSource ();
1824 if (notify)
1825 delete notify;
1828 bool
1829 PluginInstance::LoadSplash ()
1831 if (splashscreensource != NULL) {
1832 char *pos = strchr (splashscreensource, '#');
1833 if (pos) {
1834 char *original = splashscreensource;
1835 splashscreensource = g_strdup (pos + 1);
1836 g_free (original);
1837 loading_splash = true;
1838 UpdateSourceByReference (splashscreensource);
1839 FlushSplash ();
1840 // this CrossDomainApplicationCheck comes
1841 // after FlushSplash because in cases where a
1842 // XDomain XAP uses a local (to the page)
1843 // splash xaml, the loaded event is fired but
1844 // not the progress events (App7.xap from drt
1845 // #283)
1846 CrossDomainApplicationCheck (source);
1847 UpdateSource ();
1848 } else {
1849 bool cross_domain_splash = false;
1851 Uri *splash_uri = new Uri ();
1852 Uri *page_uri = new Uri ();
1853 Uri *source_uri = new Uri ();
1854 char *page_location = GetPageLocation ();
1856 if (page_uri->Parse (page_location, true) &&
1857 source_uri->Parse (source, true) &&
1858 splash_uri->Parse (splashscreensource, true)) {
1860 if (source_uri->isAbsolute && !splash_uri->isAbsolute) {
1861 // in the case where the xap is at an
1862 // absolute xdomain url and the splash
1863 // xaml file is relative (to the page
1864 // url). We can't do a straight
1865 // SameSiteOfOrigin check because in
1866 // SL no error (no events at all) is
1867 // raised during the splash (App6.xap
1868 // and App8.xap from drt #283)
1869 CrossDomainApplicationCheck (source);
1871 else {
1872 // resolve both xap and splash urls so
1873 // we can see if they're from the same
1874 // site.
1876 // (see App4.xap, App9.xap from drt #283 for this bit)
1878 if (!source_uri->isAbsolute) {
1879 Uri *temp = new Uri();
1880 Uri::Copy (page_uri, temp);
1881 temp->Combine (source_uri);
1882 delete source_uri;
1883 source_uri = temp;
1885 if (!splash_uri->isAbsolute) {
1886 Uri *temp = new Uri();
1887 Uri::Copy (page_uri, temp);
1888 temp->Combine (splash_uri);
1889 delete splash_uri;
1890 splash_uri = temp;
1893 if (source_uri->isAbsolute || splash_uri->isAbsolute)
1894 cross_domain_splash = !Uri::SameSiteOfOrigin (source_uri, splash_uri);
1898 g_free (page_location);
1899 delete page_uri;
1900 delete source_uri;
1901 delete splash_uri;
1903 if (cross_domain_splash) {
1904 surface->EmitError (new ErrorEventArgs (RuntimeError,
1905 MoonError (MoonError::EXCEPTION, 2107, "Splash screens only available on same site as xap")));
1906 UpdateSource ();
1907 return false;
1909 else {
1910 StreamNotify *notify = new StreamNotify (StreamNotify::SPLASHSOURCE, splashscreensource);
1912 // FIXME: check for errors
1913 NPN_GetURLNotify (instance, splashscreensource, NULL, notify);
1916 } else {
1917 // this check is for both local and xdomain xaps which
1918 // have a null splash, in the local case we get no
1919 // splash load event, but progress events, and in the
1920 // xdomain case we get no events at all. (App0.xap and
1921 // App5.xap from drt #283)
1922 CrossDomainApplicationCheck (source);
1923 xaml_loader = PluginXamlLoader::FromStr (NULL, PLUGIN_SPINNER, this, surface);
1924 loading_splash = true;
1925 if (!LoadXAML ())
1926 return false;
1927 FlushSplash ();
1928 UpdateSource ();
1931 return true;
1934 void
1935 PluginInstance::FlushSplash ()
1937 // FIXME we may want to flush all events here but since this is written to the
1938 // tests I'm not sure.
1940 UIElement *toplevel = GetSurface ()->GetToplevel ();
1941 if (toplevel != NULL) {
1942 toplevel->WalkTreeForLoadedHandlers (NULL, false, false);
1943 deployment->EmitLoaded ();
1945 loading_splash = false;
1948 void
1949 PluginInstance::Print (NPPrint *platformPrint)
1951 // nothing to do.
1954 int16_t
1955 PluginInstance::EventHandle (void *event)
1957 if (!surface) {
1958 g_warning ("EventHandle called before SetWindow, discarding event.");
1959 return 0;
1962 if (!windowless) {
1963 g_warning ("EventHandle called for windowed plugin, discarding event.");
1964 return 0;
1968 return ((MoonWindowless*)moon_window)->HandleEvent ((XEvent*)event);
1971 void
1972 PluginInstance::AddWrappedObject (EventObject *obj, NPObject *wrapper)
1974 g_hash_table_insert (wrapped_objects, obj, wrapper);
1977 void
1978 PluginInstance::RemoveWrappedObject (EventObject *obj)
1980 if (wrapped_objects == NULL)
1981 return;
1982 g_hash_table_remove (wrapped_objects, obj);
1985 NPObject*
1986 PluginInstance::LookupWrappedObject (EventObject *obj)
1988 return (NPObject*)g_hash_table_lookup (wrapped_objects, obj);
1991 void
1992 PluginInstance::AddCleanupPointer (gpointer p)
1994 cleanup_pointers = g_slist_prepend (cleanup_pointers, p);
1997 void
1998 PluginInstance::RemoveCleanupPointer (gpointer p)
2000 cleanup_pointers = g_slist_remove (cleanup_pointers, p);
2003 /*** Getters and Setters ******************************************************/
2005 void
2006 PluginInstance::SetSource (const char *value)
2008 bool changed = false;
2009 if (source) {
2010 changed = true;
2011 g_free (source);
2012 source = NULL;
2015 if (changed) {
2016 Recreate (value);
2017 return;
2020 source = g_strdup (value);
2021 // we may not have an original set at this point (e.g. when source is set via scripting)
2022 if (!source_original)
2023 source_original = g_strdup (value);
2025 UpdateSource ();
2028 char *
2029 PluginInstance::GetBackground ()
2031 return background;
2034 bool
2035 PluginInstance::SetBackground (const char *value)
2037 g_free (background);
2038 background = g_strdup (value);
2040 if (surface) {
2041 Color *c = color_from_str (background);
2043 if (c == NULL)
2044 return false;
2046 surface->SetBackgroundColor (c);
2047 delete c;
2050 return true;
2053 bool
2054 PluginInstance::GetEnableFramerateCounter ()
2056 return enable_framerate_counter;
2059 void
2060 PluginInstance::SetEnableFramerateCounter (bool value)
2062 enable_framerate_counter = value;
2065 bool
2066 PluginInstance::GetEnableRedrawRegions ()
2068 return moonlight_flags & RUNTIME_INIT_SHOW_EXPOSE;
2071 void
2072 PluginInstance::SetEnableRedrawRegions (bool value)
2074 if (value)
2075 moonlight_flags |= RUNTIME_INIT_SHOW_EXPOSE;
2076 else
2077 moonlight_flags &= ~RUNTIME_INIT_SHOW_EXPOSE;
2080 bool
2081 PluginInstance::GetEnableHtmlAccess ()
2083 return enable_html_access;
2086 bool
2087 PluginInstance::GetAllowHtmlPopupWindow ()
2089 return allow_html_popup_window;
2092 bool
2093 PluginInstance::GetWindowless ()
2095 return windowless;
2099 PluginInstance::GetMaxFrameRate ()
2101 return maxFrameRate;
2104 Deployment*
2105 PluginInstance::GetDeployment ()
2107 return deployment;
2110 void
2111 PluginInstance::SetMaxFrameRate (int value)
2113 maxFrameRate = value;
2115 surface->GetTimeManager()->SetMaximumRefreshRate (MAX (value, 64));
2118 gint32
2119 PluginInstance::GetActualHeight ()
2121 return surface && surface->GetWindow () ? surface->GetWindow()->GetHeight() : 0;
2124 gint32
2125 PluginInstance::GetActualWidth ()
2127 return surface && surface->GetWindow () ? surface->GetWindow()->GetWidth() : 0;
2130 MoonlightScriptControlObject *
2131 PluginInstance::GetRootObject ()
2133 if (rootobject == NULL)
2134 rootobject = (MoonlightScriptControlObject *) NPN_CreateObject (instance, MoonlightScriptControlClass);
2136 NPN_RetainObject (rootobject);
2137 return rootobject;
2141 PluginInstance::GetInstance ()
2143 return instance;
2146 NPWindow*
2147 PluginInstance::GetWindow ()
2149 return window;
2152 char*
2153 plugin_instance_get_id (PluginInstance *instance)
2155 return instance->GetId ();
2158 void
2159 plugin_instance_get_browser_runtime_settings (bool *debug, bool *html_access,
2160 bool *httpnet_access, bool *script_access)
2162 *debug = *html_access = *httpnet_access = *script_access = false;
2166 XamlLoader
2169 bool
2170 PluginXamlLoader::LoadVM ()
2172 #if PLUGIN_SL_2_0
2173 return InitializeLoader ();
2174 #endif
2175 return false;
2178 bool
2179 PluginXamlLoader::InitializeLoader ()
2181 if (initialized)
2182 return true;
2184 #if PLUGIN_SL_2_0
2185 if (managed_loader)
2186 return true;
2188 if (GetFilename ()) {
2189 managed_loader = plugin->ManagedCreateXamlLoaderForFile (this, GetResourceBase(), GetFilename ());
2190 } else if (GetString ()) {
2191 managed_loader = plugin->ManagedCreateXamlLoaderForString (this, GetResourceBase(), GetString ());
2192 } else {
2193 return false;
2196 initialized = managed_loader != NULL;
2197 #else
2198 initialized = true;
2199 #endif
2200 return initialized;
2204 // On error it sets the @error ref to 1
2205 // Returns the filename that we are missing
2207 void
2208 PluginXamlLoader::TryLoad (int *error)
2210 DependencyObject *element;
2211 Type::Kind element_type;
2213 *error = 0;
2215 //d(printf ("PluginXamlLoader::TryLoad, filename: %s, str: %s\n", GetFilename (), GetString ()));
2217 GetSurface ()->Attach (NULL);
2219 if (GetFilename ()) {
2220 element = CreateDependencyObjectFromFile (GetFilename (), true, &element_type);
2221 } else if (GetString ()) {
2222 element = CreateDependencyObjectFromString (GetString (), true, &element_type);
2223 } else {
2224 *error = 1;
2225 return;
2228 if (!element) {
2229 if (error_args && error_args->GetErrorCode() != -1) {
2230 d(printf ("PluginXamlLoader::TryLoad: Could not load xaml %s: %s (error: %s attr=%s)\n",
2231 GetFilename () ? "file" : "string", GetFilename () ? GetFilename () : GetString (),
2232 error_args->xml_element, error_args->xml_attribute));
2233 error_args->ref ();
2234 GetSurface ()->EmitError (error_args);
2235 return;
2236 } else {
2237 return;
2241 Type *t = Type::Find(element->GetDeployment (), element_type);
2242 if (!t) {
2243 d(printf ("PluginXamlLoader::TryLoad: Return value does not subclass Canvas, it is an unregistered type\n"));
2244 element->unref ();
2245 GetSurface ()->EmitError (new ErrorEventArgs (RuntimeError,
2246 MoonError (MoonError::EXCEPTION, 2101, "Failed to initialize the application's root visual")));
2247 return;
2250 if (!t->IsSubclassOf(Type::PANEL)) {
2251 d(printf ("PluginXamlLoader::TryLoad: Return value does not subclass of Panel, it is a %s\n",
2252 element->GetTypeName ()));
2253 element->unref ();
2254 GetSurface ()->EmitError (new ErrorEventArgs (RuntimeError,
2255 MoonError (MoonError::EXCEPTION, 2101, "Failed to initialize the application's root visual")));
2256 return;
2259 //d(printf ("PluginXamlLoader::TryLoad () succeeded.\n"));
2261 GetSurface ()->Attach ((Panel*) element);
2263 // xaml_create_from_* passed us a ref which we don't need to
2264 // keep.
2265 element->unref ();
2267 return;
2270 bool
2271 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)
2273 if (XamlLoader::SetProperty (parser, top_level, xmlns, target, target_data, target_parent, prop_xmlns, name, value, value_data))
2274 return true;
2276 if (value->GetKind () != Type::STRING)
2277 return false;
2279 if (!xaml_is_valid_event_name (plugin->GetDeployment (), target->GetKind(), name, false))
2280 return false;
2282 const char* function_name = value->AsString ();
2284 if (!strncmp (function_name, "javascript:", strlen ("javascript:")))
2285 return false;
2287 event_object_add_xaml_listener (target->AsDependencyObject (), plugin, name, function_name);
2289 return true;
2292 PluginXamlLoader::PluginXamlLoader (const char *resourceBase, const char *filename, const char *str, PluginInstance *plugin, Surface *surface)
2293 : XamlLoader (resourceBase, filename, str, surface)
2295 this->plugin = plugin;
2296 xaml_is_managed = false;
2297 initialized = false;
2298 error_args = NULL;
2300 #if PLUGIN_SL_2_0
2301 xap = NULL;
2303 managed_loader = NULL;
2304 #endif
2307 PluginXamlLoader::~PluginXamlLoader ()
2309 #if PLUGIN_SL_2_0
2310 if (xap)
2311 delete xap;
2313 if (managed_loader)
2314 plugin->GetDeployment ()->DestroyManagedXamlLoader (managed_loader);
2315 #endif
2318 PluginXamlLoader *
2319 plugin_xaml_loader_from_str (const char *resourceBase, const char *str, PluginInstance *plugin, Surface *surface)
2321 return PluginXamlLoader::FromStr (resourceBase, str, plugin, surface);
2324 bool
2325 PluginInstance::CreatePluginDeployment ()
2327 deployment = new Deployment ();
2328 Deployment::SetCurrent (deployment);
2331 * Give a ref to the deployment, this is required since managed code has
2332 * pointers to this PluginInstance instance. This way we ensure that the
2333 * PluginInstance isn't deleted before managed code has shutdown.
2334 * We unref just after the appdomain is unloaded (in the event handler).
2336 ref ();
2337 deployment->AddHandler (Deployment::AppDomainUnloadedEvent, AppDomainUnloadedEventCallback, this);
2339 if (!deployment->InitializeAppDomain ()) {
2340 g_warning ("Moonlight: Couldn't initialize the AppDomain");
2341 return false;
2344 return true;
2347 void
2348 PluginInstance::AppDomainUnloadedEventHandler (Deployment *deployment, EventArgs *args)
2350 unref (); /* See comment in CreatePluginDeployment */
2353 gpointer
2354 PluginInstance::ManagedCreateXamlLoaderForFile (XamlLoader *native_loader, const char *resourceBase, const char *file)
2356 return GetDeployment ()->CreateManagedXamlLoader (this, native_loader, resourceBase, file, NULL);
2359 gpointer
2360 PluginInstance::ManagedCreateXamlLoaderForString (XamlLoader* native_loader, const char *resourceBase, const char *str)
2362 return GetDeployment ()->CreateManagedXamlLoader (this, native_loader, resourceBase, NULL, str);
2365 gint32
2366 PluginInstance::GetPluginCount ()
2368 return g_slist_length (plugin_instances);