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");
152 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menu_item
);
153 g_signal_connect_swapped (G_OBJECT(menu_item
), "activate", G_CALLBACK (plugin_media_pack
), plugin
);
157 menu_item
= gtk_menu_item_new_with_label ("Show XAML Hierarchy");
158 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menu_item
);
159 g_signal_connect_swapped (G_OBJECT(menu_item
), "activate", G_CALLBACK (plugin_debug
), plugin
);
161 menu_item
= gtk_menu_item_new_with_label ("Sources");
162 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menu_item
);
163 g_signal_connect_swapped (G_OBJECT(menu_item
), "activate", G_CALLBACK (plugin_sources
), plugin
);
166 gtk_widget_show_all (menu
);
167 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
, 0, gtk_get_current_event_time());
171 PluginInstance::plugin_button_press_callback (GtkWidget
*widget
, GdkEventButton
*event
, gpointer user_data
)
173 PluginInstance
*plugin
= (PluginInstance
*) user_data
;
175 if (event
->button
== 3) {
176 plugin_show_menu (plugin
);
184 NPN_strdup (const char *tocopy
)
186 int len
= strlen(tocopy
);
187 char *ptr
= (char *)NPN_MemAlloc (len
+1);
189 strcpy (ptr
, tocopy
);
190 // WebKit should calloc so we dont have to do this
197 /*** PluginInstance:: *********************************************************/
199 GSList
*plugin_instances
= NULL
;
202 table_add (GtkWidget
*table
, const char *txt
, int col
, int row
)
204 GtkWidget
*l
= gtk_label_new (txt
);
206 gtk_misc_set_alignment (GTK_MISC (l
), 0.0, 0.5);
207 gtk_table_attach (GTK_TABLE(table
), l
, col
, col
+1, row
, row
+1, (GtkAttachOptions
) (GTK_FILL
), (GtkAttachOptions
) 0, 4, 0);
211 title (const char *txt
)
213 char *fmt
= g_strdup_printf ("<b>%s</b>", txt
);
214 GtkWidget
*label
= gtk_label_new (NULL
);
216 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
217 gtk_label_set_markup (GTK_LABEL (label
), fmt
);
224 expose_regions (GtkToggleButton
*checkbox
, gpointer user_data
)
226 if (gtk_toggle_button_get_active (checkbox
))
227 moonlight_flags
|= RUNTIME_INIT_SHOW_EXPOSE
;
229 moonlight_flags
&= ~RUNTIME_INIT_SHOW_EXPOSE
;
233 clipping_regions (GtkToggleButton
*checkbox
, gpointer user_data
)
235 if (gtk_toggle_button_get_active (checkbox
))
236 moonlight_flags
|= RUNTIME_INIT_SHOW_CLIPPING
;
238 moonlight_flags
&= ~RUNTIME_INIT_SHOW_CLIPPING
;
242 bounding_boxes (GtkToggleButton
*checkbox
, gpointer user_data
)
244 if (gtk_toggle_button_get_active (checkbox
))
245 moonlight_flags
|= RUNTIME_INIT_SHOW_BOUNDING_BOXES
;
247 moonlight_flags
&= ~RUNTIME_INIT_SHOW_BOUNDING_BOXES
;
251 textboxes (GtkToggleButton
*checkbox
, gpointer user_data
)
253 if (gtk_toggle_button_get_active (checkbox
))
254 moonlight_flags
|= RUNTIME_INIT_SHOW_TEXTBOXES
;
256 moonlight_flags
&= ~RUNTIME_INIT_SHOW_TEXTBOXES
;
260 show_fps (GtkToggleButton
*checkbox
, gpointer user_data
)
262 if (gtk_toggle_button_get_active (checkbox
))
263 moonlight_flags
|= RUNTIME_INIT_SHOW_FPS
;
265 moonlight_flags
&= ~RUNTIME_INIT_SHOW_FPS
;
269 PluginInstance::properties_dialog_response (GtkWidget
*dialog
, int response
, PluginInstance
*plugin
)
271 plugin
->properties_fps_label
= NULL
;
272 plugin
->properties_cache_label
= NULL
;
273 gtk_widget_destroy (dialog
);
277 PluginInstance::Properties ()
279 GtkWidget
*dialog
, *table
, *checkbox
;
284 dialog
= gtk_dialog_new_with_buttons ("Object Properties", NULL
, (GtkDialogFlags
)
285 GTK_DIALOG_NO_SEPARATOR
,
286 GTK_STOCK_CLOSE
, GTK_RESPONSE_NONE
, NULL
);
287 gtk_container_set_border_width (GTK_CONTAINER (dialog
), 8);
289 vbox
= GTK_BOX (GTK_DIALOG (dialog
)->vbox
);
291 // Silverlight Application properties
292 gtk_box_pack_start (vbox
, title ("Properties"), FALSE
, FALSE
, 0);
293 gtk_box_pack_start (vbox
, gtk_hseparator_new (), FALSE
, FALSE
, 8);
295 table
= gtk_table_new (11, 2, FALSE
);
296 gtk_box_pack_start (vbox
, table
, TRUE
, TRUE
, 0);
298 table_add (table
, "Source:", 0, row
++);
299 table_add (table
, "Width:", 0, row
++);
300 table_add (table
, "Height:", 0, row
++);
301 table_add (table
, "Background:", 0, row
++);
302 table_add (table
, "Kind:", 0, row
++);
303 table_add (table
, "Windowless:", 0, row
++);
304 table_add (table
, "MaxFrameRate:", 0, row
++);
305 table_add (table
, "Codecs:", 0, row
++);
308 table_add (table
, source
, 1, row
++);
309 snprintf (buffer
, sizeof (buffer
), "%dpx", GetActualWidth ());
310 table_add (table
, buffer
, 1, row
++);
311 snprintf (buffer
, sizeof (buffer
), "%dpx", GetActualHeight ());
312 table_add (table
, buffer
, 1, row
++);
313 table_add (table
, background
, 1, row
++);
314 table_add (table
, xaml_loader
== NULL
? "(Unknown)" : (xaml_loader
->IsManaged () ? "1.1 (XAML + Managed Code)" : "1.0 (Pure XAML)"), 1, row
++);
315 table_add (table
, windowless
? "yes" : "no", 1, row
++);
316 snprintf (buffer
, sizeof (buffer
), "%i", maxFrameRate
);
317 table_add (table
, buffer
, 1, row
++);
319 table_add (table
, Media::IsMSCodecsInstalled () ? "ms-codecs" : "ffmpeg", 1, row
++);
321 table_add (table
, Media::IsMSCodecsInstalled () ? "ms-codecs" : "none", 1, row
++);
325 properties_fps_label
= gtk_label_new ("");
326 gtk_misc_set_alignment (GTK_MISC (properties_fps_label
), 0.0, 0.5);
327 gtk_table_attach (GTK_TABLE(table
), properties_fps_label
, 0, 2, row
, row
+1, (GtkAttachOptions
) (GTK_FILL
), (GtkAttachOptions
) 0, 4, 0);
330 properties_cache_label
= gtk_label_new ("");
331 gtk_misc_set_alignment (GTK_MISC (properties_cache_label
), 0.0, 0.5);
332 gtk_table_attach (GTK_TABLE(table
), properties_cache_label
, 0, 2, row
, row
+1, (GtkAttachOptions
) (GTK_FILL
), (GtkAttachOptions
) 0, 4, 0);
334 // Runtime debug options
335 gtk_box_pack_start (vbox
, title ("Runtime Debug Options"), FALSE
, FALSE
, 0);
336 gtk_box_pack_start (vbox
, gtk_hseparator_new (), FALSE
, FALSE
, 8);
338 checkbox
= gtk_check_button_new_with_label ("Show exposed regions");
339 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox
), moonlight_flags
& RUNTIME_INIT_SHOW_EXPOSE
);
340 g_signal_connect (checkbox
, "toggled", G_CALLBACK (expose_regions
), NULL
);
341 gtk_box_pack_start (vbox
, checkbox
, FALSE
, FALSE
, 0);
343 checkbox
= gtk_check_button_new_with_label ("Show clipping regions");
344 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox
), moonlight_flags
& RUNTIME_INIT_SHOW_CLIPPING
);
345 g_signal_connect (checkbox
, "toggled", G_CALLBACK (clipping_regions
), NULL
);
346 gtk_box_pack_start (vbox
, checkbox
, FALSE
, FALSE
, 0);
348 checkbox
= gtk_check_button_new_with_label ("Show bounding boxes");
349 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox
), moonlight_flags
& RUNTIME_INIT_SHOW_BOUNDING_BOXES
);
350 g_signal_connect (checkbox
, "toggled", G_CALLBACK (bounding_boxes
), NULL
);
351 gtk_box_pack_start (vbox
, checkbox
, FALSE
, FALSE
, 0);
353 checkbox
= gtk_check_button_new_with_label ("Show text boxes");
354 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox
), moonlight_flags
& RUNTIME_INIT_SHOW_TEXTBOXES
);
355 g_signal_connect (checkbox
, "toggled", G_CALLBACK (textboxes
), NULL
);
356 gtk_box_pack_start (vbox
, checkbox
, FALSE
, FALSE
, 0);
358 checkbox
= gtk_check_button_new_with_label ("Show Frames Per Second");
359 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox
), moonlight_flags
& RUNTIME_INIT_SHOW_FPS
);
360 g_signal_connect (checkbox
, "toggled", G_CALLBACK (show_fps
), NULL
);
361 gtk_box_pack_start (vbox
, checkbox
, FALSE
, FALSE
, 0);
363 g_signal_connect (dialog
, "response", G_CALLBACK (properties_dialog_response
), this);
364 gtk_widget_show_all (dialog
);
367 PluginInstance::PluginInstance (NPMIMEType pluginType
, NPP instance
, guint16 mode
)
369 this->instance
= instance
;
373 properties_fps_label
= NULL
;
374 properties_cache_label
= NULL
;
383 source_location
= NULL
;
384 source_location_original
= NULL
;
387 source_original
= NULL
;
392 onSourceDownloadProgressChanged
= NULL
;
393 onSourceDownloadComplete
= NULL
;
394 splashscreensource
= NULL
;
399 cross_domain_app
= false; // false, since embedded xaml (in html) won't load anything (to change this value)
400 default_enable_html_access
= true; // should we use the default value (wrt the HTML script supplied value)
401 enable_html_access
= true; // an empty plugin must return TRUE before loading anything else (e.g. scripting)
402 allow_html_popup_window
= false;
403 xembed_supported
= FALSE
;
404 loading_splash
= false;
409 // MSDN says the default is 24: http://msdn2.microsoft.com/en-us/library/bb979688.aspx
410 // 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
411 // testing seems to confirm that the default is 60.
413 enable_framerate_counter
= false;
415 vm_missing_file
= NULL
;
418 system_windows_assembly
= NULL
;
421 moon_initialize_deployment_xap
=
422 moon_initialize_deployment_xaml
=
423 moon_destroy_application
= NULL
;
428 wrapped_objects
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
430 cleanup_pointers
= NULL
;
432 plugin_instances
= g_slist_append (plugin_instances
, instance
);
434 /* back pointer to us */
435 instance
->pdata
= this;
442 PluginInstance::~PluginInstance ()
447 Deployment::SetCurrent (deployment
);
449 for (p
= timers
; p
!= NULL
; p
= p
->next
){
450 guint32 source_id
= GPOINTER_TO_INT (p
->data
);
452 g_source_remove (source_id
);
456 g_hash_table_destroy (wrapped_objects
);
458 // Remove us from the list.
459 plugin_instances
= g_slist_remove (plugin_instances
, instance
);
461 for (GSList
*l
= cleanup_pointers
; l
; l
= l
->next
) {
462 gpointer
* p
= (gpointer
*)l
->data
;
465 g_slist_free (cleanup_pointers
);
468 NPN_ReleaseObject ((NPObject
*)rootobject
);
476 // Destroy the XAP application
477 DestroyApplication ();
481 g_free (source_original
);
482 g_free (source_location
);
483 g_free (source_location_original
);
486 g_source_remove (source_idle
);
489 // The code below was an attempt at fixing this, but we are still getting spurious errors
490 // we might have another source of problems
492 //fprintf (stderr, "Destroying the surface: %p, plugin: %p\n", surface, this);
493 if (surface
!= NULL
) {
494 //gdk_error_trap_push ();
497 surface
->unref_delayed();
498 //gdk_display_sync (display);
499 //gdk_error_trap_pop ();
506 deployment
->Dispose ();
507 deployment
->unref_delayed();
515 PluginInstance::AddSource (const char *uri
, const char *filename
)
517 moon_source
*src
= new moon_source ();
518 src
->uri
= g_strdup (uri
);
519 src
->filename
= g_strdup (filename
);
520 if (moon_sources
== NULL
)
521 moon_sources
= new List ();
522 moon_sources
->Append (src
);
526 PluginInstance::GetSources ()
534 same_site_of_origin (const char *url1
, const char *url2
)
539 if (url1
== NULL
|| url2
== NULL
)
543 if (uri1
->Parse (url1
)) {
544 Uri
*uri2
= new Uri ();
546 if (uri2
->Parse (url2
)) {
547 // if only one of the two URI is absolute then the second one is relative to the first,
548 // which makes it part of the same site of origin
549 if ((uri1
->isAbsolute
&& !uri2
->isAbsolute
) || (!uri1
->isAbsolute
&& uri2
->isAbsolute
))
552 result
= Uri::SameSiteOfOrigin (uri1
, uri2
);
561 parse_bool_arg (const char *arg
)
564 return xaml_bool_from_str (arg
, &b
) && b
;
568 PluginInstance::Initialize (int argc
, char* const argn
[], char* const argv
[])
570 for (int i
= 0; i
< argc
; i
++) {
571 if (argn
[i
] == NULL
) {
572 //g_warning ("PluginInstance::Initialize, arg %d == NULL", i);
575 else if (!g_ascii_strcasecmp (argn
[i
], "initParams")) {
576 initParams
= g_strdup (argv
[i
]);
578 else if (!g_ascii_strcasecmp (argn
[i
], "onLoad")) {
581 else if (!g_ascii_strcasecmp (argn
[i
], "onError")) {
584 else if (!g_ascii_strcasecmp (argn
[i
], "onResize")) {
587 else if (!g_ascii_strcasecmp (argn
[i
], "src") || !g_ascii_strcasecmp (argn
[i
], "source")) {
588 /* There is a new design pattern that creates a silverlight object with data="data:application/x-silverlight,"
589 * firefox is passing this to us as the src element. We need to ensure we dont set source to this value
590 * as this design pattern sets a xap up after the fact, but checks to ensure Source hasn't been set yet.
592 * eg: http://theamazingalbumcoveratlas.org/
594 * TODO: Find a site that has data:application/x-silverlight,SOMEDATA and figure out what we do with it
596 if (g_ascii_strncasecmp (argv
[i
], "data:application/x-silverlight", 30) != 0 && argv
[i
][strlen(argv
[i
])-1] != ',') {
597 source
= g_strdup (argv
[i
]);
598 // we must be able to retrieve the original source, e.g. after a redirect
599 source_original
= g_strdup (source
);
602 else if (!g_ascii_strcasecmp (argn
[i
], "background")) {
603 background
= g_strdup (argv
[i
]);
605 else if (!g_ascii_strcasecmp (argn
[i
], "windowless")) {
606 windowless
= parse_bool_arg (argv
[i
]);
608 else if (!g_ascii_strcasecmp (argn
[i
], "maxFramerate")) {
609 maxFrameRate
= atoi (argv
[i
]);
611 else if (!g_ascii_strcasecmp (argn
[i
], "id")) {
612 id
= g_strdup (argv
[i
]);
614 else if (!g_ascii_strcasecmp (argn
[i
], "enablehtmlaccess")) {
615 default_enable_html_access
= false; // we're using the application value, not the default one
616 enable_html_access
= parse_bool_arg (argv
[i
]);
618 else if (!g_ascii_strcasecmp (argn
[i
], "allowhtmlpopupwindow")) {
619 allow_html_popup_window
= parse_bool_arg (argv
[i
]);
621 else if (!g_ascii_strcasecmp (argn
[i
], "splashscreensource")) {
622 splashscreensource
= g_strdup (argv
[i
]);
624 else if (!g_ascii_strcasecmp (argn
[i
], "onSourceDownloadProgressChanged")) {
625 onSourceDownloadProgressChanged
= g_strdup (argv
[i
]);
627 else if (!g_ascii_strcasecmp (argn
[i
], "onSourceDownloadComplete")) {
628 onSourceDownloadComplete
= g_strdup (argv
[i
]);
631 //fprintf (stderr, "unhandled attribute %s='%s' in PluginInstance::Initialize\n", argn[i], argv[i]);
635 // like 'source' the original location can also be required later (for cross-domain checks after redirections)
636 source_location_original
= GetPageLocation ();
638 guint32 supportsWindowless
= FALSE
; // NPBool + padding
640 int plugin_major
, plugin_minor
;
641 int netscape_major
, netscape_minor
;
642 bool try_opera_quirks
= FALSE
;
644 /* Find the version numbers. */
645 NPN_Version(&plugin_major
, &plugin_minor
,
646 &netscape_major
, &netscape_minor
);
648 //d(printf ("Plugin NPAPI version = %d.%d\n", plugin_major, netscape_minor));
649 //d(printf ("Browser NPAPI version = %d.%d\n", netscape_major, netscape_minor));
652 error
= NPN_GetValue (instance
, NPNVSupportsXEmbedBool
, &xembed_supported
);
653 if (error
|| !xembed_supported
) {
654 // This should be an error but we'll use it to detect
655 // that we are running in opera
656 //return NPERR_INCOMPATIBLE_VERSION_ERROR;
658 d(printf ("*** XEmbed not supported\n"));
660 try_opera_quirks
= true;
663 error
= NPN_GetValue (instance
, NPNVSupportsWindowless
, &supportsWindowless
);
664 supportsWindowless
= (error
== NPERR_NO_ERROR
) && supportsWindowless
;
667 if ((moonlight_flags
& RUNTIME_INIT_ALLOW_WINDOWLESS
) == 0) {
668 printf ("plugin wants to be windowless, but we're not going to let it\n");
673 if (supportsWindowless
) {
674 NPN_SetValue (instance
, NPPVpluginWindowBool
, (void *) FALSE
);
675 NPN_SetValue (instance
, NPPVpluginTransparentBool
, (void *) TRUE
);
676 d(printf ("windowless mode\n"));
678 d(printf ("browser doesn't support windowless mode.\n"));
683 // grovel around in the useragent and try to figure out which
684 // browser bridge we should use.
685 const char *useragent
= NPN_UserAgent (instance
);
687 if (strstr (useragent
, "Opera")) {
689 TryLoadBridge ("opera");
691 else if (strstr (useragent
, "AppleWebKit")) {
693 TryLoadBridge ("webkit");
695 else if (strstr (useragent
, "Gecko")) {
696 // gecko based, let's look for 'rv:1.8' vs 'rv:1.9'
697 if (strstr (useragent
, "rv:1.8")) {
698 TryLoadBridge ("ff2");
700 else if (strstr (useragent
, "rv:1.9")) {
701 TryLoadBridge ("ff3");
705 // XXX Opera currently claims to be mozilla when we query it
706 if (!bridge
&& try_opera_quirks
)
707 TryLoadBridge ("opera");
710 g_warning ("probing for browser type failed, user agent = `%s'",
714 if (!CreatePluginDeployment ()) {
715 g_warning ("Couldn't initialize Mono or create the plugin Deployment");
719 typedef BrowserBridge
* (*create_bridge_func
)();
722 get_plugin_dir (void)
724 static char *plugin_dir
= NULL
;
728 if (dladdr((void *) &plugin_show_menu
, &dlinfo
) == 0) {
729 fprintf (stderr
, "Unable to find the location of libmoonplugin.so: %s\n", dlerror ());
732 plugin_dir
= g_path_get_dirname (dlinfo
.dli_fname
);
738 PluginInstance::TryLoadBridge (const char *prefix
)
740 char *bridge_name
= g_strdup_printf ("libmoonplugin-%sbridge.so", prefix
);
743 bridge_path
= g_build_filename (get_plugin_dir (), bridge_name
, NULL
);
745 void* bridge_handle
= dlopen (bridge_path
, RTLD_LAZY
);
747 g_free (bridge_name
);
748 g_free (bridge_path
);
750 if (bridge_handle
== NULL
) {
751 g_warning ("failed to load browser bridge: %s", dlerror());
755 create_bridge_func bridge_ctor
= (create_bridge_func
)dlsym (bridge_handle
, "CreateBrowserBridge");
756 if (bridge_ctor
== NULL
) {
757 g_warning ("failed to locate CreateBrowserBridge symbol: %s", dlerror());
761 bridge
= bridge_ctor ();
765 PluginInstance::Finalize ()
770 PluginInstance::GetValue (NPPVariable variable
, void *result
)
772 NPError err
= NPERR_NO_ERROR
;
775 case NPPVpluginNeedsXEmbed
:
776 *((NPBool
*)result
) = !windowless
;
778 case NPPVpluginScriptableNPObject
:
779 *((NPObject
**) result
) = GetRootObject ();
782 err
= NPERR_INVALID_PARAM
;
790 PluginInstance::SetValue (NPNVariable variable
, void *value
)
792 return NPERR_NO_ERROR
;
796 PluginInstance::SetWindow (NPWindow
*window
)
798 Deployment::SetCurrent (deployment
);
801 // XXX opera Window lifetime hack needs this
802 this->window
= window
;
805 return NPERR_GENERIC_ERROR
;
807 moon_window
->Resize (window
->width
, window
->height
);
808 return NPERR_NO_ERROR
;
811 this->window
= window
;
814 return NPERR_NO_ERROR
;
818 PluginInstance::GetHost()
820 NPObject
*object
= NULL
;
821 if (NPERR_NO_ERROR
!= NPN_GetValue(instance
, NPNVPluginElementNPObject
, &object
)) {
822 d(printf ("Failed to get plugin host object\n"));
828 PluginInstance::ReportFPS (Surface
*surface
, int nframes
, float nsecs
, void *user_data
)
830 PluginInstance
*plugin
= (PluginInstance
*) user_data
;
833 msg
= g_strdup_printf ("Rendered %d frames in %.3fs = %.3f FPS",
834 nframes
, nsecs
, nframes
/ nsecs
);
836 NPN_Status (plugin
->instance
, msg
);
838 if (plugin
->properties_fps_label
)
839 gtk_label_set_text (GTK_LABEL (plugin
->properties_fps_label
), msg
);
845 PluginInstance::ReportCache (Surface
*surface
, long bytes
, void *user_data
)
847 PluginInstance
*plugin
= (PluginInstance
*) user_data
;
851 msg
= g_strdup_printf ("Cache size is ~%d KB", (int) (bytes
/ 1024));
853 msg
= g_strdup_printf ("Cache size is ~%.2f MB", bytes
/ 1048576.0);
855 NPN_Status (plugin
->instance
, msg
);
857 if (plugin
->properties_cache_label
)
858 gtk_label_set_text (GTK_LABEL (plugin
->properties_cache_label
), msg
);
864 register_event (NPP instance
, const char *event_name
, char *script_name
, NPObject
*npobj
)
869 char *retval
= NPN_strdup (script_name
);
872 STRINGZ_TO_NPVARIANT (retval
, npvalue
);
873 NPIdentifier identifier
= NPN_GetStringIdentifier (event_name
);
874 NPN_SetProperty (instance
, npobj
, identifier
, &npvalue
);
875 NPN_MemFree (retval
);
879 PluginInstance::IsLoaded ()
881 if (!GetSurface () || is_splash
)
884 return GetSurface()->IsLoaded();
888 PluginInstance::CreateWindow ()
891 moon_window
= new MoonWindowless (window
->width
, window
->height
, this);
892 moon_window
->SetTransparent (true);
895 moon_window
= new MoonWindowGtk (false, window
->width
, window
->height
);
898 surface
= new Surface (moon_window
);
899 deployment
->SetSurface (surface
);
901 MoonlightScriptControlObject
*root
= GetRootObject ();
902 register_event (instance
, "onSourceDownloadProgressChanged", onSourceDownloadProgressChanged
, root
);
903 register_event (instance
, "onSourceDownloadComplete", onSourceDownloadComplete
, root
);
904 register_event (instance
, "onError", onError
, root
);
905 // register_event (instance, "onResize", onResize, rootx->content);
907 // NOTE: last testing showed this call causes opera to reenter but moving it is trouble and
908 // the bug is on opera's side.
912 surface
->SetFPSReportFunc (ReportFPS
, this);
913 surface
->SetCacheReportFunc (ReportCache
, this);
914 surface
->SetDownloaderContext (this);
916 surface
->GetTimeManager()->SetMaximumRefreshRate (maxFrameRate
);
919 Color
*c
= color_from_str (background
);
922 d(printf ("error setting background color\n"));
923 c
= new Color (0x00FFFFFF);
926 surface
->SetBackgroundColor (c
);
931 // GtkPlug container and surface inside
932 container
= gtk_plug_new ((GdkNativeWindow
) window
->window
);
934 // Connect signals to container
935 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (container
), GTK_CAN_FOCUS
);
937 gtk_widget_add_events (container
,
938 GDK_BUTTON_PRESS_MASK
|
939 GDK_BUTTON_RELEASE_MASK
|
941 GDK_KEY_RELEASE_MASK
|
942 GDK_POINTER_MOTION_MASK
|
945 GDK_VISIBILITY_NOTIFY_MASK
|
946 GDK_ENTER_NOTIFY_MASK
|
947 GDK_LEAVE_NOTIFY_MASK
|
948 GDK_FOCUS_CHANGE_MASK
951 g_signal_connect (G_OBJECT(container
), "button-press-event", G_CALLBACK (PluginInstance::plugin_button_press_callback
), this);
953 gtk_container_add (GTK_CONTAINER (container
), ((MoonWindowGtk
*)moon_window
)->GetWidget());
954 //display = gdk_drawable_get_display (surface->GetWidget()->window);
955 gtk_widget_show_all (container
);
960 PluginInstance::UpdateSource ()
963 g_source_remove (source_idle
);
968 surface
->DetachDownloaders ();
970 if (!source
|| strlen (source
) == 0)
973 char *pos
= strchr (source
, '#');
975 // FIXME: this will crash if this object has been deleted by the time IdleUpdateSourceByReference is called.
976 source_idle
= g_idle_add (IdleUpdateSourceByReference
, this);
979 StreamNotify
*notify
= new StreamNotify (StreamNotify::SOURCE
, source
);
981 // FIXME: check for errors
982 NPN_GetURLNotify (instance
, source
, NULL
, notify
);
987 PluginInstance::IdleUpdateSourceByReference (gpointer data
)
989 PluginInstance
*instance
= (PluginInstance
*)data
;
992 instance
->source_idle
= 0;
994 if (instance
->source
)
995 pos
= strchr (instance
->source
, '#');
997 if (pos
&& strlen (pos
+1) > 0)
998 instance
->UpdateSourceByReference (pos
+1);
1000 instance
->GetSurface ()->EmitSourceDownloadProgressChanged (new DownloadProgressEventArgs (1.0));
1001 instance
->GetSurface ()->EmitSourceDownloadComplete ();
1006 PluginInstance::UpdateSourceByReference (const char *value
)
1008 // basically do the equivalent of document.getElementById('@value').textContent
1011 NPVariant _document
;
1013 NPVariant _elementName
;
1014 NPVariant _textContent
;
1016 Deployment::SetCurrent (deployment
);
1018 NPIdentifier id_ownerDocument
= NPN_GetStringIdentifier ("ownerDocument");
1019 NPIdentifier id_getElementById
= NPN_GetStringIdentifier ("getElementById");
1020 NPIdentifier id_textContent
= NPN_GetStringIdentifier ("textContent");
1022 NPObject
*host
= GetHost();
1024 // printf ("no host\n");
1028 // get host.ownerDocument
1030 if (!(nperr
= NPN_GetProperty (instance
, host
, id_ownerDocument
, &_document
))
1031 || !NPVARIANT_IS_OBJECT (_document
)) {
1032 // printf ("no document (type == %d, nperr = %d)\n", _document.type, nperr);
1036 // _element = document.getElementById ('@value')
1037 string_to_npvariant (value
, &_elementName
);
1038 if (!(nperr
= NPN_Invoke (instance
, NPVARIANT_TO_OBJECT (_document
), id_getElementById
,
1039 &_elementName
, 1, &_element
))
1040 || !NPVARIANT_IS_OBJECT (_element
)) {
1041 // printf ("no valid element named #%s (type = %d, nperr = %d)\n", value, _element.type, nperr);
1042 NPN_ReleaseVariantValue (&_document
);
1045 // _textContent = _element.textContent
1046 if (!(nperr
= NPN_GetProperty (instance
, NPVARIANT_TO_OBJECT (_element
), id_textContent
, &_textContent
))
1047 || !NPVARIANT_IS_STRING (_textContent
)) {
1048 // printf ("no text content for element named #%s (type = %d, nperr = %d)\n", value, _textContent.type, nperr);
1049 NPN_ReleaseVariantValue (&_document
);
1050 NPN_ReleaseVariantValue (&_element
);
1054 char *xaml
= g_strndup ((char *) NPVARIANT_TO_STRING (_textContent
).utf8characters
, NPVARIANT_TO_STRING (_textContent
).utf8length
);
1056 // printf ("yay, xaml = %s\n", xaml);
1061 xaml_loader
= PluginXamlLoader::FromStr (NULL
/*FIXME*/, xaml
, this, surface
);
1066 NPN_ReleaseVariantValue (&_document
);
1067 NPN_ReleaseVariantValue (&_element
);
1068 NPN_ReleaseVariantValue (&_textContent
);
1073 PluginInstance::CreateDownloader (PluginInstance
*instance
)
1076 return instance
->surface
->CreateDownloader ();
1078 printf ("PluginInstance::CreateDownloader (%p): Unable to create contextual downloader.\n", instance
);
1079 return new Downloader ();
1084 PluginInstance::SetInitParams (const char *value
)
1086 g_free (initParams
);
1087 initParams
= g_strdup (value
);
1091 PluginInstance::GetPageLocation ()
1093 char *location
= NULL
;
1094 NPIdentifier str_location
= NPN_GetStringIdentifier ("location");
1095 NPIdentifier str_href
= NPN_GetStringIdentifier ("href");
1096 NPVariant location_property
;
1097 NPVariant location_object
;
1100 if (NPERR_NO_ERROR
== NPN_GetValue (instance
, NPNVWindowNPObject
, &window
)) {
1101 // Get the location property from the window object (which is another object).
1102 if (NPN_GetProperty (instance
, window
, str_location
, &location_property
)) {
1103 // Get the location property from the location object.
1104 if (NPN_GetProperty (instance
, location_property
.value
.objectValue
, str_href
, &location_object
)) {
1105 location
= g_strndup (NPVARIANT_TO_STRING (location_object
).utf8characters
, NPVARIANT_TO_STRING (location_object
).utf8length
);
1106 NPN_ReleaseVariantValue (&location_object
);
1108 NPN_ReleaseVariantValue (&location_property
);
1111 NPN_ReleaseObject (window
);
1116 PluginInstance::SetPageURL ()
1118 if (source_location
!= NULL
)
1121 char* location
= GetPageLocation ();
1122 if (location
&& surface
) {
1123 this->source_location
= location
;
1124 surface
->SetSourceLocation (this->source_location
);
1130 PluginInstance::NewStream (NPMIMEType type
, NPStream
*stream
, NPBool seekable
, guint16
*stype
)
1132 nps (printf ("PluginInstance::NewStream (%p, %p, %i, %p)\n", type
, stream
, seekable
, stype
));
1134 if (IS_NOTIFY_SPLASHSOURCE (stream
->notifyData
)) {
1137 *stype
= NP_ASFILEONLY
;
1138 return NPERR_NO_ERROR
;
1140 if (IS_NOTIFY_SOURCE (stream
->notifyData
)) {
1141 // See http://developer.mozilla.org/En/Getting_the_page_URL_in_NPAPI_plugin
1143 // but don't call GetProperty inside SetWindow because it breaks opera by
1144 // causing it to reenter
1146 // this->source_location = g_strdup (stream->url);
1150 return NPERR_NO_ERROR
;
1153 if (IS_NOTIFY_DOWNLOADER (stream
->notifyData
)) {
1154 StreamNotify
*notify
= (StreamNotify
*) stream
->notifyData
;
1155 Downloader
*dl
= (Downloader
*) notify
->pdata
;
1156 // check if (a) it's a redirection and (b) if it is allowed for the current downloader policy
1157 if (!dl
->CheckRedirectionPolicy (stream
->url
))
1158 return NPERR_INVALID_URL
;
1160 npstream_request_set_stream_data (dl
, instance
, stream
);
1162 return NPERR_NO_ERROR
;
1165 if (IS_NOTIFY_REQUEST (stream
->notifyData
)) {
1166 *stype
= NP_ASFILEONLY
;
1167 return NPERR_NO_ERROR
;
1172 return NPERR_NO_ERROR
;
1176 PluginInstance::DestroyStream (NPStream
*stream
, NPError reason
)
1178 nps (printf ("PluginInstance::DestroyStream (%p, %i)\n", stream
, reason
));
1180 PluginDownloader
*pd
= (PluginDownloader
*) stream
->pdata
;
1182 NPStreamRequest
*req
= (NPStreamRequest
*) pd
->getRequest ();
1184 req
->StreamDestroyed ();
1187 return NPERR_NO_ERROR
;
1191 // Tries to load the XAML file, the parsing might fail because a
1192 // required dependency is not available, so we need to queue the
1193 // request to fetch the data.
1196 PluginInstance::LoadXAML ()
1200 if (!InitializePluginAppDomain ()) {
1201 g_warning ("Couldn't initialize the plugin AppDomain");
1206 // Only try to load if there's no missing files.
1208 Surface
*our_surface
= surface
;
1209 AddCleanupPointer (&our_surface
);
1211 ManagedInitializeDeployment (NULL
);
1212 xaml_loader
->LoadVM ();
1214 MoonlightScriptControlObject
*root
= GetRootObject ();
1215 if (!loading_splash
) {
1216 register_event (instance
, "onLoad", onLoad
, root
);
1217 //register_event (instance, "onError", onError, root);
1218 register_event (instance
, "onResize", onResize
, root
->content
);
1220 loading_splash
= false;
1222 register_event (instance
, "onLoad", (char*)"", root
);
1223 //register_event (instance, "onError", "", root);
1224 register_event (instance
, "onResize", (char*)"", root
->content
);
1226 loading_splash
= false;
1229 const char *missing
= xaml_loader
->TryLoad (&error
);
1234 RemoveCleanupPointer (&our_surface
);
1236 if (vm_missing_file
== NULL
)
1237 vm_missing_file
= g_strdup (missing
);
1239 if (vm_missing_file
!= NULL
) {
1240 StreamNotify
*notify
= new StreamNotify (StreamNotify::REQUEST
, vm_missing_file
);
1242 // FIXME: check for errors
1243 NPN_GetURLNotify (instance
, vm_missing_file
, NULL
, notify
);
1253 PluginInstance::LoadXAP (const char *url
, const char *fname
)
1255 if (!InitializePluginAppDomain ()) {
1256 g_warning ("Couldn't initialize the plugin AppDomain");
1260 g_free (source_location
);
1262 source_location
= g_strdup (url
);
1264 MoonlightScriptControlObject
*root
= GetRootObject ();
1266 register_event (instance
, "onLoad", onLoad
, root
);
1267 //register_event (instance, "onError", onError, root);
1268 register_event (instance
, "onResize", onResize
, root
->content
);
1269 loading_splash
= false;
1272 Deployment::GetCurrent ()->Reinitialize ();
1273 GetDeployment()->SetXapLocation (url
);
1274 ManagedInitializeDeployment (fname
);
1278 PluginInstance::DestroyApplication ()
1280 ManagedDestroyApplication ();
1285 * Prepares a string to be passed to Javascript, escapes the " and '
1286 * characters and maps the newline to \n sequence and cr to \r sequence
1289 string_to_js (char *s
)
1294 if (strchr (s
, '\'') == NULL
&& strchr (s
, '\n') == NULL
)
1295 return g_strdup (s
);
1297 result
= g_string_new ("");
1299 for (char *p
= s
; *p
!= 0; *p
++){
1300 if (*p
== '"' || *p
== '\''){
1301 g_string_append_c (result
, '\\');
1302 g_string_append_c (result
, *p
);
1303 } else if (*p
== '\n'){
1304 g_string_append_c (result
, '\\');
1305 g_string_append_c (result
, 'n');
1306 } else if (*p
== '\r'){
1307 g_string_append_c (result
, '\\');
1308 g_string_append_c (result
, 'r');
1310 g_string_append_c (result
, *p
);
1314 g_string_free (result
, FALSE
);
1320 PluginInstance::ReportException (char *msg
, char *details
, char **stack_trace
, int num_frames
)
1324 char *script
, *row_js
, *msg_escaped
, *details_escaped
;
1325 char **stack_trace_escaped
;
1330 // Get a reference to our element
1336 // - make sure the variables do not become global
1338 // Remove ' from embedded strings
1339 msg_escaped
= string_to_js (msg
);
1340 details_escaped
= string_to_js (details
);
1341 stack_trace_escaped
= g_new0 (char*, num_frames
);
1342 for (i
= 0; i
< num_frames
; ++i
)
1343 stack_trace_escaped
[i
] = string_to_js (stack_trace
[i
]);
1345 // JS code to create our elements
1346 row_js
= g_strdup (" ");
1347 for (i
= 0; i
< num_frames
; ++i
) {
1350 s
= g_strdup_printf ("%s%s%s", row_js
, (i
== 0) ? "" : "\\n ", stack_trace_escaped
[i
]);
1355 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
);
1357 g_free (msg_escaped
);
1358 g_free (details_escaped
);
1359 for (i
= 0; i
< num_frames
; ++i
)
1360 g_free (stack_trace_escaped
[i
]);
1361 g_free (stack_trace_escaped
);
1364 str
.utf8characters
= script
;
1365 str
.utf8length
= strlen (script
);
1367 res
= NPN_Evaluate (instance
, object
, &str
, &result
);
1369 NPN_ReleaseVariantValue (&result
);
1370 NPN_ReleaseObject (object
);
1375 PluginInstance::Evaluate (const char *code
)
1377 NPObject
*object
= GetHost ();
1385 string
.utf8characters
= code
;
1386 string
.utf8length
= strlen (code
);
1388 bool ret
= NPN_Evaluate (instance
, object
, &string
, &npresult
);
1391 bool keep_ref
= false;
1393 if (!NPVARIANT_IS_VOID (npresult
) && !NPVARIANT_IS_NULL (npresult
)) {
1394 variant_to_value (&npresult
, &res
);
1395 if (npresult
.type
== NPVariantType_Object
)
1401 NPN_ReleaseVariantValue (&npresult
);
1407 PluginInstance::CrossDomainApplicationCheck (const char *source
)
1409 char* page_url
= GetPageLocation ();
1410 // note: source might not be an absolute URL at this stage - but that only indicates that it's relative to the page url
1411 cross_domain_app
= !same_site_of_origin (page_url
, source
);
1412 if (!cross_domain_app
) {
1413 // we need also to consider a web page having cross-domain XAP that redirects to a XAP on the SOO
1414 // this will still be considered a cross-domain
1415 cross_domain_app
= !same_site_of_origin (page_url
, source_original
);
1419 // if the application did not specify 'enablehtmlaccess' then we use its default value
1420 // which is TRUE for same-site applications and FALSE for cross-domain applications
1421 if (default_enable_html_access
)
1422 enable_html_access
= !cross_domain_app
;
1426 is_xap (const char *fname
)
1428 // Check for the ZIP magic header
1434 if ((fd
= open (fname
, O_RDONLY
)) == -1)
1437 nread
= read (fd
, buf
, 4);
1443 if (buf
[0] != 0x50 || buf
[1] != 0x4B || buf
[2] != 0x03 || buf
[3] != 0x04) {
1453 PluginInstance::StreamAsFile (NPStream
*stream
, const char *fname
)
1455 nps (printf ("PluginInstance::StreamAsFile (%p, %s)\n", stream
, fname
));
1457 Deployment::SetCurrent (deployment
);
1459 AddSource (stream
->url
, fname
);
1461 if (IS_NOTIFY_SPLASHSOURCE (stream
->notifyData
)) {
1462 xaml_loader
= PluginXamlLoader::FromFilename (stream
->url
, fname
, this, surface
);
1463 loading_splash
= true;
1467 if (IS_NOTIFY_SOURCE (stream
->notifyData
)) {
1468 bool splash
= (xaml_loader
!= NULL
);
1472 CrossDomainApplicationCheck (stream
->url
);
1474 Uri
*uri
= new Uri ();
1477 if (uri
->Parse (stream
->url
, false) && is_xap (fname
)) {
1478 LoadXAP (stream
->url
, fname
);
1480 xaml_loader
= PluginXamlLoader::FromFilename (stream
->url
, fname
, this, surface
);
1484 GetSurface ()->EmitSourceDownloadProgressChanged (new DownloadProgressEventArgs (1.0));
1485 GetSurface ()->EmitSourceDownloadComplete ();
1488 } else if (IS_NOTIFY_DOWNLOADER (stream
->notifyData
)){
1489 Downloader
*dl
= (Downloader
*) ((StreamNotify
*)stream
->notifyData
)->pdata
;
1491 dl
->SetFilename (fname
);
1492 } else if (IS_NOTIFY_REQUEST (stream
->notifyData
)) {
1495 /// Commented out for now, I don't think we should need this code at all anymore since we never request assemblies
1496 /// to be downloaded anymore.
1500 if (!vm_missing_file)
1503 if (reload && xaml_loader->GetMapping (vm_missing_file) != NULL)
1506 if (reload && xaml_loader->GetMapping (stream->url) != NULL)
1509 if (vm_missing_file)
1510 xaml_loader->RemoveMissing (vm_missing_file);
1512 char *missing = vm_missing_file;
1513 vm_missing_file = NULL;
1516 // There may be more missing files.
1517 vm_missing_file = g_strdup (xaml_loader->GetMissing ());
1519 xaml_loader->InsertMapping (missing, fname);
1520 xaml_loader->InsertMapping (stream->url, fname);
1532 PluginInstance::WriteReady (NPStream
*stream
)
1534 nps (printf ("PluginInstance::WriteReady (%p)\n", stream
));
1536 Deployment::SetCurrent (deployment
);
1538 StreamNotify
*notify
= STREAM_NOTIFY (stream
->notifyData
);
1540 if (notify
&& notify
->pdata
) {
1541 if (IS_NOTIFY_DOWNLOADER (notify
)) {
1542 Downloader
*dl
= (Downloader
*) notify
->pdata
;
1544 dl
->NotifySize (stream
->end
);
1546 return MAX_STREAM_SIZE
;
1548 if (IS_NOTIFY_SOURCE (notify
)) {
1549 source_size
= stream
->end
;
1551 return MAX_STREAM_SIZE
;
1555 NPN_DestroyStream (instance
, stream
, NPRES_DONE
);
1561 PluginInstance::Write (NPStream
*stream
, gint32 offset
, gint32 len
, void *buffer
)
1563 nps (printf ("PluginInstance::Write (%p, %i, %i, %p)\n", stream
, offset
, len
, buffer
));
1565 Deployment::SetCurrent (deployment
);
1567 StreamNotify
*notify
= STREAM_NOTIFY (stream
->notifyData
);
1569 if (notify
&& notify
->pdata
) {
1570 if (IS_NOTIFY_DOWNLOADER (notify
)) {
1571 Downloader
*dl
= (Downloader
*) notify
->pdata
;
1573 dl
->Write (buffer
, offset
, len
);
1575 if (IS_NOTIFY_SOURCE (notify
)) {
1576 if (source_size
> 0) {
1577 float progress
= (offset
+len
)/(float)source_size
;
1578 if (GetSurface ()->GetToplevel () != NULL
) {
1579 GetSurface ()->EmitSourceDownloadProgressChanged (new DownloadProgressEventArgs (progress
));
1588 class PluginClosure
: public EventObject
{
1590 PluginClosure (PluginInstance
*plugin
)
1595 virtual ~PluginClosure ()
1599 PluginInstance
*plugin
;
1603 PluginInstance::network_error_tickcall (EventObject
*data
)
1605 PluginClosure
*closure
= (PluginClosure
*)data
;
1606 Surface
*s
= closure
->plugin
->GetSurface();
1608 s
->EmitError (new ErrorEventArgs (RuntimeError
, 2104, "Failed to download silverlight application."));
1612 PluginInstance::splashscreen_error_tickcall (EventObject
*data
)
1614 PluginClosure
*closure
= (PluginClosure
*)data
;
1615 Surface
*s
= closure
->plugin
->GetSurface();
1617 s
->EmitError (new ErrorEventArgs (RuntimeError
, 2108, "Failed to download the splash screen"));
1618 closure
->plugin
->is_splash
= false;
1620 // we need this check beccause the plugin might have been
1621 // dtor'ed (and the surface zombified) in the amove EmitError.
1623 closure
->plugin
->UpdateSource ();
1629 PluginInstance::UrlNotify (const char *url
, NPReason reason
, void *notifyData
)
1631 nps (printf ("PluginInstance::UrlNotify (%s, %i, %p)\n", url
, reason
, notifyData
));
1633 StreamNotify
*notify
= STREAM_NOTIFY (notifyData
);
1635 Deployment::SetCurrent (deployment
);
1637 if (reason
== NPRES_DONE
) {
1638 d(printf ("URL %s downloaded successfully.\n", url
));
1640 d(printf ("Download of URL %s failed: %i (%s)\n", url
, reason
,
1641 reason
== NPRES_USER_BREAK
? "user break" :
1642 (reason
== NPRES_NETWORK_ERR
? "network error" : "other error")));
1643 if (IS_NOTIFY_SOURCE (notify
))
1644 GetSurface()->GetTimeManager()->AddTickCall (network_error_tickcall
,
1645 new PluginClosure (this));
1648 if (notify
&& notify
->pdata
&& IS_NOTIFY_DOWNLOADER (notify
)) {
1649 Downloader
*dl
= (Downloader
*) notify
->pdata
;
1651 if (reason
!= NPRES_DONE
) {
1654 case NPRES_USER_BREAK
:
1655 dl
->NotifyFailed ("user break");
1657 case NPRES_NETWORK_ERR
:
1658 dl
->NotifyFailed ("network error");
1661 dl
->NotifyFailed ("unknown error");
1665 dl
->NotifyFinished (url
);
1669 if (notify
&& notify
->pdata
&& IS_NOTIFY_SPLASHSOURCE (notify
)) {
1670 if (reason
== NPRES_NETWORK_ERR
)
1671 GetSurface()->GetTimeManager()->AddTickCall (splashscreen_error_tickcall
,
1672 new PluginClosure (this));
1682 PluginInstance::LoadSplash ()
1684 if (splashscreensource
!= NULL
) {
1685 char *pos
= strchr (splashscreensource
, '#');
1687 splashscreensource
= pos
+1;
1688 loading_splash
= true;
1689 UpdateSourceByReference (splashscreensource
);
1693 StreamNotify
*notify
= new StreamNotify (StreamNotify::SPLASHSOURCE
, splashscreensource
);
1695 // FIXME: check for errors
1696 NPN_GetURLNotify (instance
, splashscreensource
, NULL
, notify
);
1699 xaml_loader
= PluginXamlLoader::FromStr (NULL
, PLUGIN_SPINNER
, this, surface
);
1700 loading_splash
= true;
1708 PluginInstance::FlushSplash ()
1710 // FIXME we may want to flush all events here but since this is written to the
1711 // tests I'm not sure.
1713 UIElement
*toplevel
= GetSurface ()->GetToplevel ();
1714 if (toplevel
!= NULL
) {
1715 List
*list
= toplevel
->WalkTreeForLoaded (NULL
);
1716 toplevel
->EmitSubtreeLoad (list
);
1719 loading_splash
= false;
1723 PluginInstance::Print (NPPrint
*platformPrint
)
1729 PluginInstance::EventHandle (void *event
)
1732 g_warning ("EventHandle called before SetWindow, discarding event.");
1737 g_warning ("EventHandle called for windowed plugin, discarding event.");
1742 return ((MoonWindowless
*)moon_window
)->HandleEvent ((XEvent
*)event
);
1746 PluginInstance::AddWrappedObject (EventObject
*obj
, NPObject
*wrapper
)
1748 g_hash_table_insert (wrapped_objects
, obj
, wrapper
);
1752 PluginInstance::RemoveWrappedObject (EventObject
*obj
)
1754 g_hash_table_remove (wrapped_objects
, obj
);
1758 PluginInstance::LookupWrappedObject (EventObject
*obj
)
1760 return (NPObject
*)g_hash_table_lookup (wrapped_objects
, obj
);
1764 PluginInstance::AddCleanupPointer (gpointer p
)
1766 cleanup_pointers
= g_slist_prepend (cleanup_pointers
, p
);
1770 PluginInstance::RemoveCleanupPointer (gpointer p
)
1772 cleanup_pointers
= g_slist_remove (cleanup_pointers
, p
);
1775 /*** Getters and Setters ******************************************************/
1778 PluginInstance::SetSource (const char *value
)
1785 source
= g_strdup (value
);
1786 // we may not have an original set at this point (e.g. when source is set via scripting)
1787 if (!source_original
)
1788 source_original
= g_strdup (value
);
1794 PluginInstance::GetBackground ()
1800 PluginInstance::SetBackground (const char *value
)
1802 g_free (background
);
1803 background
= g_strdup (value
);
1806 Color
*c
= color_from_str (background
);
1811 surface
->SetBackgroundColor (c
);
1819 PluginInstance::GetEnableFramerateCounter ()
1821 return enable_framerate_counter
;
1825 PluginInstance::SetEnableFramerateCounter (bool value
)
1827 enable_framerate_counter
= value
;
1831 PluginInstance::GetEnableRedrawRegions ()
1833 return moonlight_flags
& RUNTIME_INIT_SHOW_EXPOSE
;
1837 PluginInstance::SetEnableRedrawRegions (bool value
)
1840 moonlight_flags
|= RUNTIME_INIT_SHOW_EXPOSE
;
1842 moonlight_flags
&= ~RUNTIME_INIT_SHOW_EXPOSE
;
1846 PluginInstance::GetEnableHtmlAccess ()
1848 return enable_html_access
;
1852 PluginInstance::GetAllowHtmlPopupWindow ()
1854 return allow_html_popup_window
;
1858 PluginInstance::GetWindowless ()
1864 PluginInstance::GetMaxFrameRate ()
1866 return maxFrameRate
;
1870 PluginInstance::GetDeployment ()
1876 PluginInstance::SetMaxFrameRate (int value
)
1878 maxFrameRate
= value
;
1880 surface
->GetTimeManager()->SetMaximumRefreshRate (MAX (value
, 64));
1884 PluginInstance::GetActualHeight ()
1886 return surface
? surface
->GetWindow()->GetHeight() : 0;
1890 PluginInstance::GetActualWidth ()
1892 return surface
? surface
->GetWindow()->GetWidth() : 0;
1895 MoonlightScriptControlObject
*
1896 PluginInstance::GetRootObject ()
1898 if (rootobject
== NULL
)
1899 rootobject
= NPN_CreateObject (instance
, MoonlightScriptControlClass
);
1901 NPN_RetainObject (rootobject
);
1902 return (MoonlightScriptControlObject
*)rootobject
;
1906 PluginInstance::GetInstance ()
1912 PluginInstance::GetWindow ()
1917 // [Obselete (this is obsolete in SL b2.)]
1919 PluginInstance::TimeoutAdd (gint32 interval
, GSourceFunc callback
, gpointer data
)
1923 #if GLIB_CHECK_VERSION(2,14,0)
1924 if (glib_check_version (2,14,0) && interval
> 1000 && ((interval
% 1000) == 0))
1925 id
= g_timeout_add_seconds (interval
/ 1000, callback
, data
);
1928 id
= g_timeout_add (interval
, callback
, data
);
1930 timers
= g_slist_append (timers
, GINT_TO_POINTER ((int)id
));
1935 // [Obselete (this is obsolete in SL b2.)]
1937 PluginInstance::TimeoutStop (guint32 source_id
)
1939 g_source_remove (source_id
);
1940 timers
= g_slist_remove (timers
, GINT_TO_POINTER (source_id
));
1944 plugin_instance_get_id (PluginInstance
*instance
)
1946 return instance
->GetId ();
1950 plugin_instance_get_browser_runtime_settings (bool *debug
, bool *html_access
,
1951 bool *httpnet_access
, bool *script_access
)
1953 *debug
= *html_access
= *httpnet_access
= *script_access
= false;
1961 PluginXamlLoader::LoadVM ()
1964 return InitializeLoader ();
1970 PluginXamlLoader::InitializeLoader ()
1979 if (GetFilename ()) {
1980 managed_loader
= plugin
->ManagedCreateXamlLoaderForFile (this, GetResourceBase(), GetFilename ());
1981 } else if (GetString ()) {
1982 managed_loader
= plugin
->ManagedCreateXamlLoaderForString (this, GetResourceBase(), GetString ());
1987 initialized
= managed_loader
!= NULL
;
1995 // On error it sets the @error ref to 1
1996 // Returns the filename that we are missing
1999 PluginXamlLoader::TryLoad (int *error
)
2001 DependencyObject
*element
;
2002 Type::Kind element_type
;
2006 //d(printf ("PluginXamlLoader::TryLoad, filename: %s, str: %s\n", GetFilename (), GetString ()));
2008 GetSurface ()->Attach (NULL
);
2010 if (GetFilename ()) {
2011 element
= CreateDependencyObjectFromFile (GetFilename (), true, &element_type
);
2012 } else if (GetString ()) {
2013 element
= CreateDependencyObjectFromString (GetString (), true, &element_type
);
2020 if (error_args
&& error_args
->error_code
!= -1) {
2021 d(printf ("PluginXamlLoader::TryLoad: Could not load xaml %s: %s (error: %s attr=%s)\n",
2022 GetFilename () ? "file" : "string", GetFilename () ? GetFilename () : GetString (),
2023 error_args
->xml_element
, error_args
->xml_attribute
));
2025 GetSurface ()->EmitError (error_args
);
2029 d(printf ("PluginXamlLoader::TryLoad: Could not load xaml %s: %s (missing_assembly: %s)\n",
2030 GetFilename () ? "file" : "string", GetFilename () ? GetFilename () : GetString (),
2033 xaml_is_managed = true;
2034 return GetMissing ();
2040 Type
*t
= Type::Find(element_type
);
2042 d(printf ("PluginXamlLoader::TryLoad: Return value does not subclass Canvas, it is an unregistered type\n"));
2044 GetSurface ()->EmitError (new ErrorEventArgs (RuntimeError
, 2101, "Failed to initialize the application's root visual"));
2048 if (!t
->IsSubclassOf(Type::PANEL
)) {
2049 d(printf ("PluginXamlLoader::TryLoad: Return value does not subclass of Panel, it is a %s\n",
2050 element
->GetTypeName ()));
2052 GetSurface ()->EmitError (new ErrorEventArgs (RuntimeError
, 2101, "Failed to initialize the application's root visual"));
2056 //d(printf ("PluginXamlLoader::TryLoad () succeeded.\n"));
2058 GetSurface ()->Attach ((Panel
*) element
);
2060 // xaml_create_from_* passed us a ref which we don't need to
2068 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
)
2070 if (XamlLoader::SetProperty (parser
, top_level
, xmlns
, target
, target_data
, target_parent
, prop_xmlns
, name
, value
, value_data
))
2073 if (value
->GetKind () != Type::STRING
)
2076 if (!xaml_is_valid_event_name (target
->GetKind(), name
, false))
2079 const char* function_name
= value
->AsString ();
2081 if (!strncmp (function_name
, "javascript:", strlen ("javascript:")))
2084 event_object_add_xaml_listener ((EventObject
*) target
->AsDependencyObject (), plugin
, name
, function_name
);
2089 PluginXamlLoader::PluginXamlLoader (const char *resourceBase
, const char *filename
, const char *str
, PluginInstance
*plugin
, Surface
*surface
, bool import_default_xmlns
)
2090 : XamlLoader (resourceBase
, filename
, str
, surface
)
2092 this->plugin
= plugin
;
2093 xaml_is_managed
= false;
2094 initialized
= false;
2097 SetImportDefaultXmlns (import_default_xmlns
);
2102 managed_loader
= NULL
;
2106 PluginXamlLoader::~PluginXamlLoader ()
2113 plugin
->ManagedLoaderDestroy (managed_loader
);
2118 plugin_xaml_loader_from_str (const char *resourceBase
, const char *str
, PluginInstance
*plugin
, Surface
*surface
)
2120 return PluginXamlLoader::FromStr (resourceBase
, str
, plugin
, surface
);
2124 // Our Mono embedding bits are here. By storing the mono_domain in
2125 // the PluginInstance instead of in a global variable, we don't need
2126 // the code in moonlight.cs for managing app domains.
2129 PluginInstance::MonoGetMethodFromName (MonoClass
*klass
, const char *name
, int narg
)
2132 method
= mono_class_get_method_from_name (klass
, name
, narg
);
2135 printf ("Warning could not find method %s\n", name
);
2141 PluginInstance::MonoGetPropertyFromName (MonoClass
*klass
, const char *name
)
2143 MonoProperty
*property
;
2144 property
= mono_class_get_property_from_name (klass
, name
);
2147 printf ("Warning could not find property %s\n", name
);
2152 bool PluginInstance::mono_is_loaded
= false;
2155 PluginInstance::MonoIsLoaded ()
2157 return mono_is_loaded
;
2161 extern gboolean
mono_jit_set_trace_options (const char *options
);
2165 PluginInstance::DeploymentInit ()
2167 return mono_is_loaded
= true; // We load mono in runtime_init.
2172 PluginInstance::CreatePluginDeployment ()
2174 deployment
= new Deployment ();
2175 Deployment::SetCurrent (deployment
);
2181 PluginInstance::InitializePluginAppDomain ()
2183 bool result
= false;
2185 system_windows_assembly
= mono_assembly_load_with_partial_name ("System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", NULL
);
2187 if (system_windows_assembly
) {
2189 MonoClass
*app_launcher
;
2193 image
= mono_assembly_get_image (system_windows_assembly
);
2195 d (printf ("Assembly: %s\n", mono_image_get_filename (image
)));
2197 app_launcher
= mono_class_from_name (image
, "Mono", "ApplicationLauncher");
2198 if (!app_launcher
) {
2199 g_warning ("could not find ApplicationLauncher type");
2203 moon_exception
= mono_class_from_name (image
, "Mono", "MoonException");
2204 if (!moon_exception
) {
2205 g_warning ("could not find MoonException type");
2209 moon_load_xaml
= MonoGetMethodFromName (app_launcher
, "CreateXamlLoader", -1);
2210 moon_initialize_deployment_xap
= MonoGetMethodFromName (app_launcher
, "InitializeDeployment", 2);
2211 moon_initialize_deployment_xaml
= MonoGetMethodFromName (app_launcher
, "InitializeDeployment", 0);
2212 moon_destroy_application
= MonoGetMethodFromName (app_launcher
, "DestroyApplication", -1);
2214 if (moon_load_xaml
== NULL
|| moon_initialize_deployment_xap
== NULL
|| moon_initialize_deployment_xaml
== NULL
|| moon_destroy_application
== NULL
) {
2215 g_warning ("lookup for ApplicationLauncher methods failed");
2219 moon_exception_message
= MonoGetPropertyFromName (mono_get_exception_class(), "Message");
2220 moon_exception_error_code
= MonoGetPropertyFromName (moon_exception
, "ErrorCode");
2222 if (moon_exception_message
== NULL
|| moon_exception_error_code
== NULL
) {
2223 g_warning ("lookup for MoonException properties failed");
2227 printf ("Plugin AppDomain Creation: could not find System.Windows.dll.\n");
2230 printf ("Plugin AppDomain Creation: %s\n", result
? "OK" : "Failed");
2236 PluginInstance::ManagedExceptionToErrorEventArgs (MonoObject
*exc
)
2239 char* message
= NULL
;
2241 if (mono_object_isinst (exc
, mono_get_exception_class())) {
2242 MonoObject
*ret
= mono_property_get_value (moon_exception_message
, exc
, NULL
, NULL
);
2244 message
= mono_string_to_utf8 ((MonoString
*)ret
);
2246 if (mono_object_isinst (exc
, moon_exception
)) {
2247 MonoObject
*ret
= mono_property_get_value (moon_exception_error_code
, exc
, NULL
, NULL
);
2249 errorCode
= *(int*) mono_object_unbox (ret
);
2252 return new ErrorEventArgs (RuntimeError
, errorCode
, message
);
2256 PluginInstance::ManagedCreateXamlLoader (XamlLoader
* native_loader
, const char *resourceBase
, const char *file
, const char *str
)
2259 MonoObject
*exc
= NULL
;
2260 if (moon_load_xaml
== NULL
)
2263 PluginInstance
*this_obj
= this;
2266 Deployment::SetCurrent (deployment
);
2268 params
[0] = &native_loader
;
2269 params
[1] = &this_obj
;
2270 params
[2] = &surface
;
2271 params
[3] = resourceBase
? mono_string_new (mono_domain_get (), resourceBase
) : NULL
;
2272 params
[4] = file
? mono_string_new (mono_domain_get (), file
) : NULL
;
2273 params
[5] = str
? mono_string_new (mono_domain_get (), str
) : NULL
;
2274 loader
= mono_runtime_invoke (moon_load_xaml
, NULL
, params
, &exc
);
2277 deployment
->GetSurface()->EmitError (ManagedExceptionToErrorEventArgs (exc
));
2281 return GUINT_TO_POINTER (mono_gchandle_new (loader
, false));
2285 PluginInstance::ManagedCreateXamlLoaderForFile (XamlLoader
*native_loader
, const char *resourceBase
, const char *file
)
2287 return ManagedCreateXamlLoader (native_loader
, resourceBase
, file
, NULL
);
2291 PluginInstance::ManagedCreateXamlLoaderForString (XamlLoader
* native_loader
, const char *resourceBase
, const char *str
)
2293 return ManagedCreateXamlLoader (native_loader
, resourceBase
, NULL
, str
);
2297 PluginInstance::ManagedLoaderDestroy (gpointer loader_object
)
2299 guint32 loader
= GPOINTER_TO_UINT (loader_object
);
2301 mono_gchandle_free (loader
);
2305 PluginInstance::ManagedInitializeDeployment (const char *file
)
2307 if (moon_initialize_deployment_xap
== NULL
&& moon_initialize_deployment_xaml
)
2310 PluginInstance
*this_obj
= this;
2313 MonoObject
*exc
= NULL
;
2315 Deployment::SetCurrent (deployment
);
2317 params
[0] = &this_obj
;
2319 params
[1] = mono_string_new (mono_domain_get (), file
);
2320 ret
= mono_runtime_invoke (moon_initialize_deployment_xap
, NULL
, params
, &exc
);
2322 ret
= mono_runtime_invoke (moon_initialize_deployment_xaml
, NULL
, params
, &exc
);
2326 deployment
->GetSurface()->EmitError (ManagedExceptionToErrorEventArgs (exc
));
2330 return (bool) (*(MonoBoolean
*) mono_object_unbox(ret
));
2334 PluginInstance::ManagedDestroyApplication ()
2336 if (moon_destroy_application
== NULL
)
2339 PluginInstance
*this_obj
= this;
2340 MonoObject
*exc
= NULL
;
2342 params
[0] = &this_obj
;
2344 Deployment::SetCurrent (deployment
);
2346 mono_runtime_invoke (moon_destroy_application
, NULL
, params
, &exc
);
2349 deployment
->GetSurface()->EmitError (ManagedExceptionToErrorEventArgs (exc
));