1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * moon-plugin.cpp: MoonLight browser plugin.
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.
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"
31 #include "windowless.h"
32 #include "window-gtk.h"
34 #include "deployment.h"
36 #include "timemanager.h"
38 #define Visual _XxVisual
39 #define Region _XxRegion
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>",
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/)");
100 gtk_about_dialog_set_website (about
, "http://moonlight-project.com/");
102 gtk_about_dialog_set_website (about
, "http://moonlight-project.com/Beta");
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
,
112 G_CALLBACK (gtk_widget_destroy
),
115 gtk_dialog_run (GTK_DIALOG (about
));
119 plugin_media_pack (PluginInstance
*plugin
)
121 CodecDownloader::ShowUI (plugin
->GetSurface ());
125 plugin_properties (PluginInstance
*plugin
)
127 plugin
->Properties ();
131 plugin_show_menu (PluginInstance
*plugin
)
134 GtkWidget
*menu_item
;
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
);
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");
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
);
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
);
168 gtk_widget_show_all (menu
);
169 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
, 0, gtk_get_current_event_time());
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
);
186 NPN_strdup (const char *tocopy
)
188 int len
= strlen(tocopy
);
189 char *ptr
= (char *)NPN_MemAlloc (len
+1);
191 strcpy (ptr
, tocopy
);
192 // WebKit should calloc so we dont have to do this
199 /*** PluginInstance:: *********************************************************/
201 GSList
*plugin_instances
= NULL
;
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);
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
);
226 expose_regions (GtkToggleButton
*checkbox
, gpointer user_data
)
228 if (gtk_toggle_button_get_active (checkbox
))
229 moonlight_flags
|= RUNTIME_INIT_SHOW_EXPOSE
;
231 moonlight_flags
&= ~RUNTIME_INIT_SHOW_EXPOSE
;
235 clipping_regions (GtkToggleButton
*checkbox
, gpointer user_data
)
237 if (gtk_toggle_button_get_active (checkbox
))
238 moonlight_flags
|= RUNTIME_INIT_SHOW_CLIPPING
;
240 moonlight_flags
&= ~RUNTIME_INIT_SHOW_CLIPPING
;
244 bounding_boxes (GtkToggleButton
*checkbox
, gpointer user_data
)
246 if (gtk_toggle_button_get_active (checkbox
))
247 moonlight_flags
|= RUNTIME_INIT_SHOW_BOUNDING_BOXES
;
249 moonlight_flags
&= ~RUNTIME_INIT_SHOW_BOUNDING_BOXES
;
253 textboxes (GtkToggleButton
*checkbox
, gpointer user_data
)
255 if (gtk_toggle_button_get_active (checkbox
))
256 moonlight_flags
|= RUNTIME_INIT_SHOW_TEXTBOXES
;
258 moonlight_flags
&= ~RUNTIME_INIT_SHOW_TEXTBOXES
;
262 show_fps (GtkToggleButton
*checkbox
, gpointer user_data
)
264 if (gtk_toggle_button_get_active (checkbox
))
265 moonlight_flags
|= RUNTIME_INIT_SHOW_FPS
;
267 moonlight_flags
&= ~RUNTIME_INIT_SHOW_FPS
;
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
);
279 PluginInstance::Properties ()
281 GtkWidget
*dialog
, *table
, *checkbox
;
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
++);
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
++);
324 table_add (table
, "(Unknown)", 1, row
++);
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
++);
333 table_add (table
, Media::IsMSCodecsInstalled () ? "ms-codecs" : "ffmpeg", 1, row
++);
335 table_add (table
, Media::IsMSCodecsInstalled () ? "ms-codecs" : "none", 1, 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);
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
)
384 this->instance
= instance
;
387 connected_to_container
= false;
389 properties_fps_label
= NULL
;
390 properties_cache_label
= NULL
;
399 source_location
= NULL
;
400 source_location_original
= NULL
;
403 source_original
= NULL
;
408 onSourceDownloadProgressChanged
= NULL
;
409 onSourceDownloadComplete
= NULL
;
410 splashscreensource
= NULL
;
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;
424 is_shutting_down
= false;
425 has_shutdown
= false;
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.
433 enable_framerate_counter
= false;
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;
453 PluginInstance::Recreate (const char *source
)
455 //printf ("PluginInstance::Recreate (%s) this: %p, instance->pdata: %p\n", source, this, instance->pdata);
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
;
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 ());
493 result
->moon_window
= surface
->DetachWindow (); /* we reuse the same MoonWindow */
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
);
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 ();
525 PluginInstance::ref ()
527 g_assert (refcount
> 0);
528 g_atomic_int_inc (&refcount
);
532 PluginInstance::unref ()
534 g_assert (refcount
> 0);
535 int v
= g_atomic_int_exchange_and_add (&refcount
, -1) - 1;
541 PluginInstance::IsShuttingDown ()
544 return is_shutting_down
;
548 PluginInstance::HasShutdown ()
555 PluginInstance::Shutdown ()
560 g_return_if_fail (!is_shutting_down
);
561 g_return_if_fail (!has_shutdown
);
563 is_shutting_down
= true;
565 Deployment::SetCurrent (deployment
);
568 // Destroy the XAP application
569 DestroyApplication ();
572 for (p
= timers
; p
!= NULL
; p
= p
->next
){
573 guint32 source_id
= GPOINTER_TO_INT (p
->data
);
575 g_source_remove (source_id
);
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
;
590 g_slist_free (cleanup_pointers
);
591 cleanup_pointers
= NULL
;
594 NPN_ReleaseObject ((NPObject
*)rootobject
);
602 g_free (onSourceDownloadProgressChanged
);
603 onSourceDownloadProgressChanged
= NULL
;
604 g_free (onSourceDownloadComplete
);
605 onSourceDownloadComplete
= NULL
;
606 g_free (splashscreensource
);
607 splashscreensource
= 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
;
627 g_source_remove (source_idle
);
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 ();
640 surface
->unref_delayed();
641 //gdk_error_trap_pop ();
650 deployment
->Shutdown ();
656 is_shutting_down
= false;
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
);
673 PluginInstance::GetSources ()
681 same_site_of_origin (const char *url1
, const char *url2
)
686 if (url1
== NULL
|| url2
== NULL
)
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
))
699 result
= Uri::SameSiteOfOrigin (uri1
, uri2
);
708 parse_bool_arg (const char *arg
)
711 return xaml_bool_from_str (arg
, &b
) && b
;
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);
722 else if (!g_ascii_strcasecmp (argn
[i
], "initParams")) {
723 initParams
= g_strdup (argv
[i
]);
725 else if (!g_ascii_strcasecmp (argn
[i
], "onLoad")) {
728 else if (!g_ascii_strcasecmp (argn
[i
], "onError")) {
731 else if (!g_ascii_strcasecmp (argn
[i
], "onResize")) {
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
]);
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));
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;
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
;
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");
826 if (supportsWindowless
) {
827 NPN_SetValue (instance
, NPPVpluginWindowBool
, (void *) FALSE
);
828 NPN_SetValue (instance
, NPPVpluginTransparentBool
, (void *) TRUE
);
829 d(printf ("windowless mode\n"));
831 d(printf ("browser doesn't support windowless mode.\n"));
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")) {
842 TryLoadBridge ("opera");
844 else if (strstr (useragent
, "AppleWebKit")) {
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");
863 g_warning ("probing for browser type failed, user agent = `%s'",
867 if (!CreatePluginDeployment ()) {
868 g_warning ("Couldn't initialize Mono or create the plugin Deployment");
872 typedef BrowserBridge
* (*create_bridge_func
)();
875 get_plugin_dir (void)
877 static char *plugin_dir
= NULL
;
881 if (dladdr((void *) &plugin_show_menu
, &dlinfo
) == 0) {
882 fprintf (stderr
, "Unable to find the location of libmoonplugin.so: %s\n", dlerror ());
885 plugin_dir
= g_path_get_dirname (dlinfo
.dli_fname
);
891 PluginInstance::TryLoadBridge (const char *prefix
)
893 char *bridge_name
= g_strdup_printf ("libmoonplugin-%sbridge.so", prefix
);
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());
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());
914 bridge
= bridge_ctor ();
918 PluginInstance::GetValue (NPPVariable variable
, void *result
)
920 NPError err
= NPERR_NO_ERROR
;
923 case NPPVpluginNeedsXEmbed
:
924 *((NPBool
*)result
) = !windowless
;
926 case NPPVpluginScriptableNPObject
:
927 *((NPObject
**) result
) = GetRootObject ();
930 err
= NPERR_INVALID_PARAM
;
938 PluginInstance::SetValue (NPNVariable variable
, void *value
)
940 return NPERR_NO_ERROR
;
944 PluginInstance::SetWindow (NPWindow
*window
)
946 Deployment::SetCurrent (deployment
);
949 // XXX opera Window lifetime hack needs this
950 this->window
= window
;
953 return NPERR_GENERIC_ERROR
;
955 moon_window
->Resize (window
->width
, window
->height
);
956 return NPERR_NO_ERROR
;
959 this->window
= window
;
962 return NPERR_NO_ERROR
;
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"));
976 PluginInstance::ReportFPS (Surface
*surface
, int nframes
, float nsecs
, void *user_data
)
978 PluginInstance
*plugin
= (PluginInstance
*) user_data
;
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
);
993 PluginInstance::ReportCache (Surface
*surface
, long bytes
, void *user_data
)
995 PluginInstance
*plugin
= (PluginInstance
*) user_data
;
999 msg
= g_strdup_printf ("Cache size is ~%d KB", (int) (bytes
/ 1024));
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
);
1012 register_event (NPP instance
, const char *event_name
, char *script_name
, NPObject
*npobj
)
1017 char *retval
= NPN_strdup (script_name
);
1020 STRINGZ_TO_NPVARIANT (retval
, npvalue
);
1021 NPIdentifier identifier
= NPN_GetStringIdentifier (event_name
);
1022 NPN_SetProperty (instance
, npobj
, identifier
, &npvalue
);
1023 NPN_MemFree (retval
);
1027 PluginInstance::IsLoaded ()
1029 if (!GetSurface () || is_splash
)
1032 return GetSurface()->IsLoaded();
1036 PluginInstance::CreateWindow ()
1038 bool created
= false;
1039 bool success
= true;
1041 if (moon_window
== NULL
) {
1043 moon_window
= new MoonWindowless (window
->width
, window
->height
, this);
1044 moon_window
->SetTransparent (true);
1047 moon_window
= new MoonWindowGtk (false, window
->width
, window
->height
);
1054 surface
= new Surface (moon_window
);
1055 deployment
->SetSurface (surface
);
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.
1068 success
= LoadSplash ();
1070 surface
->SetFPSReportFunc (ReportFPS
, this);
1071 surface
->SetCacheReportFunc (ReportCache
, this);
1072 surface
->SetDownloaderContext (this);
1074 surface
->GetTimeManager()->SetMaximumRefreshRate (maxFrameRate
);
1077 Color
*c
= color_from_str (background
);
1080 d(printf ("error setting background color\n"));
1081 c
= new Color (0x00FFFFFF);
1084 surface
->SetBackgroundColor (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
|
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;
1118 PluginInstance::UpdateSource ()
1121 g_source_remove (source_idle
);
1125 if (surface
!= NULL
)
1126 surface
->DetachDownloaders ();
1128 if (!source
|| strlen (source
) == 0)
1131 char *pos
= strchr (source
, '#');
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);
1137 StreamNotify
*notify
= new StreamNotify (StreamNotify::SOURCE
, source
);
1139 // FIXME: check for errors
1140 NPN_GetURLNotify (instance
, source
, NULL
, notify
);
1145 PluginInstance::IdleUpdateSourceByReference (gpointer data
)
1147 PluginInstance
*instance
= (PluginInstance
*)data
;
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 ();
1164 PluginInstance::UpdateSourceByReference (const char *value
)
1166 // basically do the equivalent of document.getElementById('@value').textContent
1169 NPVariant _document
;
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();
1182 // printf ("no host\n");
1186 // get host.ownerDocument
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);
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
);
1212 char *xaml
= g_strndup ((char *) NPVARIANT_TO_STRING (_textContent
).utf8characters
, NPVARIANT_TO_STRING (_textContent
).utf8length
);
1214 // printf ("yay, xaml = %s\n", xaml);
1219 xaml_loader
= PluginXamlLoader::FromStr (NULL
/*FIXME*/, xaml
, this, surface
);
1224 NPN_ReleaseVariantValue (&_document
);
1225 NPN_ReleaseVariantValue (&_element
);
1226 NPN_ReleaseVariantValue (&_textContent
);
1231 PluginInstance::CreateDownloader (PluginInstance
*instance
)
1234 return instance
->surface
->CreateDownloader ();
1236 printf ("PluginInstance::CreateDownloader (%p): Unable to create contextual downloader.\n", instance
);
1237 return new Downloader ();
1242 PluginInstance::SetInitParams (const char *value
)
1244 g_free (initParams
);
1245 initParams
= g_strdup (value
);
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
;
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
);
1274 PluginInstance::SetPageURL ()
1276 if (source_location
!= NULL
)
1279 char* location
= GetPageLocation ();
1280 if (location
&& surface
) {
1281 this->source_location
= location
;
1282 surface
->SetSourceLocation (this->source_location
);
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
)) {
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);
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
);
1322 return NPERR_NO_ERROR
;
1327 return NPERR_NO_ERROR
;
1331 PluginInstance::DestroyStream (NPStream
*stream
, NPError reason
)
1333 nps (printf ("PluginInstance::DestroyStream (%p, %i)\n", stream
, reason
));
1335 PluginDownloader
*pd
= (PluginDownloader
*) stream
->pdata
;
1337 NPStreamRequest
*req
= (NPStreamRequest
*) pd
->getRequest ();
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.
1351 PluginInstance::LoadXAML ()
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
))
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
);
1372 loading_splash
= false;
1374 register_event (instance
, "onLoad", (char*)"", root
);
1375 //register_event (instance, "onError", "", root);
1376 register_event (instance
, "onResize", (char*)"", root
->content
);
1378 loading_splash
= false;
1381 xaml_loader
->TryLoad (&error
);
1386 RemoveCleanupPointer (&our_surface
);
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;
1410 Deployment::GetCurrent ()->Reinitialize ();
1411 GetDeployment()->SetXapLocation (url
);
1412 return GetDeployment ()->InitializeManagedDeployment (this, fname
, culture
, uiCulture
);
1416 PluginInstance::DestroyApplication ()
1418 GetDeployment ()->DestroyManagedApplication (this);
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
1427 string_to_js (char *s
)
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');
1448 g_string_append_c (result
, *p
);
1452 g_string_free (result
, FALSE
);
1458 PluginInstance::ReportException (char *msg
, char *details
, char **stack_trace
, int num_frames
)
1462 char *script
, *row_js
, *msg_escaped
, *details_escaped
;
1463 char **stack_trace_escaped
;
1468 // Get a reference to our element
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
) {
1488 s
= g_strdup_printf ("%s%s%s", row_js
, (i
== 0) ? "" : "\\n ", stack_trace_escaped
[i
]);
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
);
1502 str
.utf8characters
= script
;
1503 str
.utf8length
= strlen (script
);
1505 res
= NPN_Evaluate (instance
, object
, &str
, &result
);
1507 NPN_ReleaseVariantValue (&result
);
1508 NPN_ReleaseObject (object
);
1513 PluginInstance::Evaluate (const char *code
)
1515 NPObject
*object
= GetHost ();
1523 string
.utf8characters
= code
;
1524 string
.utf8length
= strlen (code
);
1526 bool ret
= NPN_Evaluate (instance
, object
, &string
, &npresult
);
1529 bool keep_ref
= false;
1531 if (!NPVARIANT_IS_VOID (npresult
) && !NPVARIANT_IS_NULL (npresult
)) {
1532 variant_to_value (&npresult
, &res
);
1533 if (npresult
.type
== NPVariantType_Object
)
1539 NPN_ReleaseVariantValue (&npresult
);
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
);
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
;
1564 is_xap (const char *fname
)
1566 // Check for the ZIP magic header
1572 if ((fd
= open (fname
, O_RDONLY
)) == -1)
1575 nread
= read (fd
, buf
, 4);
1581 if (buf
[0] != 0x50 || buf
[1] != 0x4B || buf
[2] != 0x03 || buf
[3] != 0x04) {
1591 PluginInstance::StreamAsFile (NPStream
*stream
, const char *fname
)
1593 nps (printf ("PluginInstance::StreamAsFile (%p, %s)\n", stream
, fname
));
1595 Deployment::SetCurrent (deployment
);
1597 AddSource (stream
->url
, fname
);
1599 if (IS_NOTIFY_SPLASHSOURCE (stream
->notifyData
)) {
1600 xaml_loader
= PluginXamlLoader::FromFilename (stream
->url
, fname
, this, surface
);
1601 loading_splash
= true;
1605 CrossDomainApplicationCheck (source
);
1607 if (IS_NOTIFY_SOURCE (stream
->notifyData
)) {
1611 CrossDomainApplicationCheck (stream
->url
);
1613 Uri
*uri
= new Uri ();
1616 if (uri
->Parse (stream
->url
, false) && is_xap (fname
)) {
1617 LoadXAP (stream
->url
, fname
);
1619 xaml_loader
= PluginXamlLoader::FromFilename (stream
->url
, fname
, this, surface
);
1623 GetSurface ()->EmitSourceDownloadProgressChanged (new DownloadProgressEventArgs (1.0));
1624 GetSurface ()->EmitSourceDownloadComplete ();
1627 } else if (IS_NOTIFY_DOWNLOADER (stream
->notifyData
)){
1628 Downloader
*dl
= (Downloader
*) ((StreamNotify
*)stream
->notifyData
)->pdata
;
1630 dl
->SetFilename (fname
);
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
);
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
));
1691 class PluginClosure
: public EventObject
{
1693 PluginClosure (PluginInstance
*plugin
)
1698 virtual ~PluginClosure ()
1702 PluginInstance
*plugin
;
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.")));
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.
1728 closure
->plugin
->UpdateSource ();
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
));
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
) {
1759 case NPRES_USER_BREAK
:
1760 dl
->NotifyFailed ("user break");
1762 case NPRES_NETWORK_ERR
:
1763 dl
->NotifyFailed ("network error");
1766 dl
->NotifyFailed ("unknown error");
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));
1787 PluginInstance::LoadSplash ()
1789 if (splashscreensource
!= NULL
) {
1790 char *pos
= strchr (splashscreensource
, '#');
1792 char *original
= splashscreensource
;
1793 splashscreensource
= g_strdup (pos
+ 1);
1795 loading_splash
= true;
1796 UpdateSourceByReference (splashscreensource
);
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
1804 CrossDomainApplicationCheck (source
);
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
);
1830 // resolve both xap and splash urls so
1831 // we can see if they're from the same
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
);
1843 if (!splash_uri
->isAbsolute
) {
1844 Uri
*temp
= new Uri();
1845 Uri::Copy (page_uri
, temp
);
1846 temp
->Combine (splash_uri
);
1851 if (source_uri
->isAbsolute
|| splash_uri
->isAbsolute
)
1852 cross_domain_splash
= !Uri::SameSiteOfOrigin (source_uri
, splash_uri
);
1856 g_free (page_location
);
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")));
1868 StreamNotify
*notify
= new StreamNotify (StreamNotify::SPLASHSOURCE
, splashscreensource
);
1870 // FIXME: check for errors
1871 NPN_GetURLNotify (instance
, splashscreensource
, NULL
, notify
);
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;
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;
1907 PluginInstance::Print (NPPrint
*platformPrint
)
1913 PluginInstance::EventHandle (void *event
)
1916 g_warning ("EventHandle called before SetWindow, discarding event.");
1921 g_warning ("EventHandle called for windowed plugin, discarding event.");
1926 return ((MoonWindowless
*)moon_window
)->HandleEvent ((XEvent
*)event
);
1930 PluginInstance::AddWrappedObject (EventObject
*obj
, NPObject
*wrapper
)
1932 g_hash_table_insert (wrapped_objects
, obj
, wrapper
);
1936 PluginInstance::RemoveWrappedObject (EventObject
*obj
)
1938 if (wrapped_objects
== NULL
)
1940 g_hash_table_remove (wrapped_objects
, obj
);
1944 PluginInstance::LookupWrappedObject (EventObject
*obj
)
1946 return (NPObject
*)g_hash_table_lookup (wrapped_objects
, obj
);
1950 PluginInstance::AddCleanupPointer (gpointer p
)
1952 cleanup_pointers
= g_slist_prepend (cleanup_pointers
, p
);
1956 PluginInstance::RemoveCleanupPointer (gpointer p
)
1958 cleanup_pointers
= g_slist_remove (cleanup_pointers
, p
);
1961 /*** Getters and Setters ******************************************************/
1964 PluginInstance::SetSource (const char *value
)
1966 bool changed
= false;
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
);
1987 PluginInstance::GetBackground ()
1993 PluginInstance::SetBackground (const char *value
)
1995 g_free (background
);
1996 background
= g_strdup (value
);
1999 Color
*c
= color_from_str (background
);
2004 surface
->SetBackgroundColor (c
);
2012 PluginInstance::GetEnableFramerateCounter ()
2014 return enable_framerate_counter
;
2018 PluginInstance::SetEnableFramerateCounter (bool value
)
2020 enable_framerate_counter
= value
;
2024 PluginInstance::GetEnableRedrawRegions ()
2026 return moonlight_flags
& RUNTIME_INIT_SHOW_EXPOSE
;
2030 PluginInstance::SetEnableRedrawRegions (bool value
)
2033 moonlight_flags
|= RUNTIME_INIT_SHOW_EXPOSE
;
2035 moonlight_flags
&= ~RUNTIME_INIT_SHOW_EXPOSE
;
2039 PluginInstance::GetEnableHtmlAccess ()
2041 return enable_html_access
;
2045 PluginInstance::GetAllowHtmlPopupWindow ()
2047 return allow_html_popup_window
;
2051 PluginInstance::GetWindowless ()
2057 PluginInstance::GetMaxFrameRate ()
2059 return maxFrameRate
;
2063 PluginInstance::GetDeployment ()
2069 PluginInstance::SetMaxFrameRate (int value
)
2071 maxFrameRate
= value
;
2073 surface
->GetTimeManager()->SetMaximumRefreshRate (MAX (value
, 64));
2077 PluginInstance::GetActualHeight ()
2079 return surface
&& surface
->GetWindow () ? surface
->GetWindow()->GetHeight() : 0;
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
);
2099 PluginInstance::GetInstance ()
2105 PluginInstance::GetWindow ()
2111 plugin_instance_get_id (PluginInstance
*instance
)
2113 return instance
->GetId ();
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;
2128 PluginXamlLoader::LoadVM ()
2131 return InitializeLoader ();
2137 PluginXamlLoader::InitializeLoader ()
2146 if (GetFilename ()) {
2147 managed_loader
= plugin
->ManagedCreateXamlLoaderForFile (this, GetResourceBase(), GetFilename ());
2148 } else if (GetString ()) {
2149 managed_loader
= plugin
->ManagedCreateXamlLoaderForString (this, GetResourceBase(), GetString ());
2154 initialized
= managed_loader
!= NULL
;
2162 // On error it sets the @error ref to 1
2163 // Returns the filename that we are missing
2166 PluginXamlLoader::TryLoad (int *error
)
2168 DependencyObject
*element
;
2169 Type::Kind element_type
;
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
);
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
));
2192 GetSurface ()->EmitError (error_args
);
2199 Type
*t
= Type::Find(element_type
);
2201 d(printf ("PluginXamlLoader::TryLoad: Return value does not subclass Canvas, it is an unregistered type\n"));
2203 GetSurface ()->EmitError (new ErrorEventArgs (RuntimeError
,
2204 MoonError (MoonError::EXCEPTION
, 2101, "Failed to initialize the application's root visual")));
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 ()));
2212 GetSurface ()->EmitError (new ErrorEventArgs (RuntimeError
,
2213 MoonError (MoonError::EXCEPTION
, 2101, "Failed to initialize the application's root visual")));
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
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
))
2234 if (value
->GetKind () != Type::STRING
)
2237 if (!xaml_is_valid_event_name (target
->GetKind(), name
, false))
2240 const char* function_name
= value
->AsString ();
2242 if (!strncmp (function_name
, "javascript:", strlen ("javascript:")))
2245 event_object_add_xaml_listener ((EventObject
*) target
->AsDependencyObject (), plugin
, name
, function_name
);
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;
2261 managed_loader
= NULL
;
2265 PluginXamlLoader::~PluginXamlLoader ()
2272 plugin
->GetDeployment ()->DestroyManagedXamlLoader (managed_loader
);
2277 plugin_xaml_loader_from_str (const char *resourceBase
, const char *str
, PluginInstance
*plugin
, Surface
*surface
)
2279 return PluginXamlLoader::FromStr (resourceBase
, str
, plugin
, surface
);
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).
2295 deployment
->AddHandler (Deployment::AppDomainUnloadedEvent
, AppDomainUnloadedEventCallback
, this);
2297 if (!deployment
->InitializeAppDomain ()) {
2298 g_warning ("Moonlight: Couldn't initialize the AppDomain");
2306 PluginInstance::AppDomainUnloadedEventHandler (Deployment
*deployment
, EventArgs
*args
)
2308 unref (); /* See comment in CreatePluginDeployment */
2312 PluginInstance::ManagedCreateXamlLoaderForFile (XamlLoader
*native_loader
, const char *resourceBase
, const char *file
)
2314 return GetDeployment ()->CreateManagedXamlLoader (this, native_loader
, resourceBase
, file
, NULL
);
2318 PluginInstance::ManagedCreateXamlLoaderForString (XamlLoader
* native_loader
, const char *resourceBase
, const char *str
)
2320 return GetDeployment ()->CreateManagedXamlLoader (this, native_loader
, resourceBase
, NULL
, str
);
2324 PluginInstance::GetPluginCount ()
2326 return g_slist_length (plugin_instances
);