Index: configure.in
===================================================================
RCS file: /cvs/gnome/totem/configure.in,v
retrieving revision 1.310
diff -p -u -B -r1.310 configure.in
--- configure.in	3 Nov 2006 16:20:00 -0000	1.310
+++ configure.in	12 Nov 2006 20:08:24 -0000
@@ -243,25 +243,28 @@ AC_MSG_CHECKING([whether to compile the 
 
 AC_ARG_ENABLE([browser-plugins],
 	AS_HELP_STRING([--enable-mozilla],[compile the mozilla plugin]),
-	[ENABLE_MOZILLA=$enableval],
-	[ENABLE_MOZILLA=autodetect])
+	[],[enable_browser_plugins=autodetect])
 
 # Backward compatibility
 AC_ARG_ENABLE([mozilla],[],[enable_browser_plugins=$enableval],[enable_browser_plugins=autodetect])
 
-AC_MSG_RESULT([$ENABLE_MOZILLA])
+AC_MSG_RESULT([$enable_browser_plugins])
 
-dnl Check which mozilla to use
-if test "x$ENABLE_MOZILLA" != "xno" ; then
+# Check which mozilla to use
+
+if test "$enable_browser_plugins" != "no" ; then
 	AC_MSG_CHECKING([which gecko to use])
 
-	AC_ARG_WITH([mozilla],
-		AS_HELP_STRING([--with-mozilla],[Which gecko engine to use (default: autodetect)]))
+	AC_ARG_WITH([gecko],
+		[AS_HELP_STRING([--with-gecko],[Which gecko engine to use (default: autodetect)])])
+
+	# backward compat
+	AC_ARG_WITH([mozilla],[],[with_gecko=$withval])
 
 	GECKOS="xulrunner firefox mozilla-firefox seamonkey mozilla"
-	gecko=$with_mozilla
+	gecko=$with_gecko
 
-	if test "x$gecko" = "x"; then
+	if test -z "$with_gecko"; then
 		dnl Autodetect gecko
 		for g in $GECKOS; do
 			if $PKG_CONFIG --exists $g-xpcom; then
@@ -273,16 +276,16 @@ if test "x$ENABLE_MOZILLA" != "xno" ; th
 		AC_MSG_ERROR([Gecko "$gecko" not found])
 	fi
 
-	if test "x$gecko" = "x" -a "x$ENABLE_MOZILLA" = "xautodetect"; then
+	if test -z "$gecko" -a "$enable_browser_plugins" = "autodetect"; then
 		dnl No gecko found, disable plugin
 		AC_MSG_WARN([No gecko found, disabling plugin])
-		ENABLE_MOZILLA=no
-	elif test "x$gecko" = "x"; then
+		enable_browser_plugins=no
+	elif test -z "$gecko"; then
 		AC_MSG_ERROR([No gecko found])
 	elif ! ( echo "$GECKOS" | egrep "(^| )$gecko(\$| )" > /dev/null); then
 		AC_MSG_ERROR([Unknown gecko "$gecko" specified])
 	else
-		ENABLE_MOZILLA=yes
+		enable_browser_plugins=yes
 	fi
 
 	AC_MSG_RESULT([$gecko])
@@ -297,31 +300,31 @@ if test "x$ENABLE_MOZILLA" != "xno" ; th
 	MOZILLA=$gecko
 	AC_SUBST([MOZILLA])
 
-	if test "$enable_cxx_warnings" != "no" -a "$GCC" = "yes"; then
-		PLUGIN_EXTRA_WARN_CXXFLAGS="-Wno-ctor-dtor-privacy -Wno-non-virtual-dtor"
+	if test "$enable_cxx_warnings" != "no" -a "$GXX" = "yes"; then
+		PLUGIN_EXTRA_WARN_CXXFLAGS="-Wno-ctor-dtor-privacy -Wno-non-virtual-dtor -Woverloaded-virtual"
 	else
 		PLUGIN_EXTRA_WARN_CXXFLAGS=
 	fi
 	AC_SUBST([PLUGIN_EXTRA_WARN_CXXFLAGS])
 fi
 
-dnl Check for mozilla modules, but keep the CFLAGS and LIBS in
-dnl separate vars
-if test "x$ENABLE_MOZILLA" = "xyes" ; then
+# Check for mozilla modules, but keep the CFLAGS and LIBS in
+# separate vars
+if test "$enable_browser_plugins" = "yes" ; then
 	PKG_CHECK_MODULES([MOZILLA_NOT_LINKED],
 		[$MOZILLA-xpcom >= $MOZILLA_VERSION_MIN \
 		 $MOZILLA-plugin],,
-                [ENABLE_MOZILLA=no])
+                [enable_browser_plugins=no])
 fi
-dnl Check for other required modules, and merge CFLAGS, but not link
-dnl flags to avoid linking against -lxpcom -lplds4 -lplc4 -lnspr4
-if test "x$ENABLE_MOZILLA" = "xyes" ; then
+# Check for other required modules, and merge CFLAGS, but not link
+# flags to avoid linking against -lxpcom -lplds4 -lplc4 -lnspr4
+if test "$enable_browser_plugins" = "yes" ; then
 	PKG_CHECK_MODULES([BROWSER_PLUGIN],
 		[glib-2.0
 		 gnome-vfs-2.0 >= $GNOMEVFS_REQS
 		 gnome-vfs-module-2.0 >= $GNOMEVFS_REQS
 		 gthread-2.0],
-		[],[ENABLE_MOZILLA=no])
+		[],[enable_browser_plugins=no])
 
 	BROWSER_PLUGIN_CFLAGS="$MOZILLA_NOT_LINKED_CFLAGS $BROWSER_PLUGIN_CFLAGS"
 	AC_SUBST([BROWSER_PLUGIN_CFLAGS])
@@ -335,20 +338,20 @@ if test "x$ENABLE_MOZILLA" = "xyes" ; th
 				>= 0.17 to function.])])
 fi
 
-if test "x$ENABLE_MOZILLA" = "xyes" ; then
+if test "$enable_browser_plugins" = "yes" ; then
 	PKG_CHECK_MODULES([DBUS], [dbus-glib-1 >= $DBUS_REQS],,
-			[ENABLE_MOZILLA=no])
+			[enable_browser_plugins=no])
 	DBUSLIBDIR="`$PKG_CONFIG dbus-glib-1 --variable=libdir`"
 	DBUSBINDIR="`echo $DBUSLIBDIR | sed -e s/lib/bin/`"
 	AC_PATH_PROG([DBUS_BIND], [dbus-binding-tool], [no], [$DBUSBINDIR:$PATH])
 	if test "x$DBUS_BIND" = "xno"; then
 		AC_MSG_WARN([dbus-binding-tool not found])
-		ENABLE_MOZILLA=no
+		enable_browser_plugins=no
 	fi
 fi
 
-dnl Sets some variables, and check for xpidl
-if test "x$ENABLE_MOZILLA" = "xyes" ; then
+# Sets some variables, and check for xpidl
+if test "$enable_browser_plugins" = "yes" ; then
 	MOZILLA_PREFIX="`$PKG_CONFIG $MOZILLA-xpcom --variable=prefix`"
 	MOZILLA_LIBDIR="`$PKG_CONFIG $MOZILLA-xpcom --variable=libdir`"
 	MOZILLA_INCLUDE_ROOT="`$PKG_CONFIG --variable=includedir $MOZILLA-xpcom`"
@@ -372,8 +375,8 @@ if test "x$ENABLE_MOZILLA" = "xyes" ; th
 	AC_ARG_VAR([MOZILLA_PLUGINDIR],[Where to install the plugin to])
 fi
 
-dnl Search for the idl include directory
-if test "x$ENABLE_MOZILLA" = "xyes" ; then
+# Search for the idl include directory
+if test "$enable_browser_plugins" = "yes" ; then
 	dnl This only works on gecko 1.8
 	MOZILLA_IDLDIR="`$PKG_CONFIG --variable=idldir $MOZILLA-xpcom`"
 	dnl Fallback for older versions
@@ -388,13 +391,58 @@ if test "x$ENABLE_MOZILLA" = "xyes" ; th
 	AC_SUBST([MOZILLA_IDLDIR])
 fi
 
-if test "$ENABLE_MOZILLA" = "yes" ; then
-	AC_DEFINE([HAVE_MOZILLA],[1],[Define if you build the mozilla plugin])
+if test "$enable_browser_plugins" = "yes" ; then
+	AC_DEFINE([ENABLE_BROWSER_PLUGINS],[1],[Define if you build the mozilla plugin])
+fi
+
+AM_CONDITIONAL([ENABLE_BROWSER_PLUGINS], [test "$enable_browser_plugins" = "yes"])
+
+# check for libxpcomglue_s
+
+LIBXPCOMGLUE_S=
+
+if test "$enable_browser_plugins" = "yes"; then
+	AC_LANG_PUSH([C++])
+	__SAVE_CPPFLAGS=$CPPFLAGS
+	__SAVE_CXXFLAGS=$CXXFLAGS
+	__SAVE_LIBS=$LIBS
+	__SAVE_LDFLAGS=$LDFLAGS
+
+	CPPFLAGS="$CPPFLAGS -I$MOZILLA_INCLUDE_ROOT"
+	CXXFLAGS="$CXXFLAGS $MOZILLA_NOT_LINKED_CFLAGS"
+	LIBS="$LIBS -L$MOZILLA_LIBDIR -lxpcom -lxpcomglue_s"
+	LDFLAGS="$LDFLAGS -Wl,--rpath -Wl,$MOZILLA_LIBDIR"
+
+	AC_MSG_CHECKING([for libxpcomglue_s])
+	AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#include <mozilla-config.h>
+#include <nsStringAPI.h>
+		]],[[
+nsCString s;
+const char *t = s.get ();
+		]])],
+	[have_libxpcomglue_s=yes],
+	[have_libxpcomglue_s=no])
+	AC_MSG_RESULT([$have_libxpcomglue_s])
+
+	CPPFLAGS=$__SAVE_CPPFLAGS
+	CXXFLAGS=$__SAVE_CXXFLAGS
+	LIBS=$__SAVE_LIBS
+	LDFLAGS=$__SAVE_LDFLAGS
+	AC_LANG_POP([C++])
+
+	if test "$have_libxpcomglue_s" = "yes"; then
+		LIBXPCOMGLUE_S="-L$MOZILLA_LIBDIR -lxpcomglue_s"
+	else
+		AC_MSG_WARN([libxpcomglue_s not available; plugins may not be portable])
+	fi
 fi
 
-AM_CONDITIONAL([HAVE_MOZILLA], [test "x$ENABLE_MOZILLA" = "xyes"])
+AC_SUBST([LIBXPCOMGLUE_S])
+
+# check which plugins to enable
 
-if test "$ENABLE_MOZILLA" = "yes"; then
+if test "$enable_browser_plugins" = "yes"; then
 
 AC_MSG_CHECKING([whether to enable the basic browser plugin])
 AC_ARG_ENABLE([basic-plugin],
Index: Makefile.am
===================================================================
RCS file: /cvs/gnome/totem/Makefile.am,v
retrieving revision 1.14
diff -p -u -B -r1.14 Makefile.am
--- Makefile.am	1 Oct 2006 12:21:01 -0000	1.14
+++ Makefile.am	12 Nov 2006 20:08:24 -0000
@@ -1,5 +1,10 @@
-SUBDIRS = po data src browser-plugin help
+SUBDIRS = po data src help
 
+if ENABLE_BROWSER_PLUGINS
+SUBDIRS += browser-plugin
+endif
+
+# FIXME: huh?
 install-data-local:
 	@$(NORMAL_INSTALL)
 
@@ -25,4 +30,4 @@ DISTCLEANFILES = intltool-extract intlto
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = totem-plparser.pc
 
-DISTCHECK_CONFIGURE_FLAGS = --disable-scrollkeeper
\ No newline at end of file
+DISTCHECK_CONFIGURE_FLAGS = --disable-scrollkeeper
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gnome/totem/src/Makefile.am,v
retrieving revision 1.172
diff -p -u -B -r1.172 Makefile.am
--- src/Makefile.am	1 Nov 2006 10:23:08 -0000	1.172
+++ src/Makefile.am	12 Nov 2006 20:08:25 -0000
@@ -19,7 +19,7 @@ bin_PROGRAMS = totem totem-video-thumbna
 libexec_PROGRAMS =
 noinst_PROGRAMS = list_v4l disc-test $(TOTEM_PROPERTIES_PAGE_TEST)
 
-if HAVE_MOZILLA
+if ENABLE_BROWSER_PLUGINS
 libexec_PROGRAMS += totem-mozilla-viewer
 endif
 
@@ -102,7 +102,7 @@ totem_video_thumbnailer_LDADD =			\
 	$(XTEST_LIBS) $(X_LIBS) 
 
 nautilusdir = $(libdir)/nautilus/extensions-1.0/
-libtotem_properties_page_la_CFLAGS = -I$(top_srcdir)
+libtotem_properties_page_la_CFLAGS = -I$(top_srcdir) $(WARN_CFLAGS)
 libtotem_properties_page_la_SOURCES = 	\
 	totem-properties-main.c		\
 	totem-properties-view.c		\
@@ -114,7 +114,7 @@ libtotem_properties_page_la_LIBADD =				
 	$(XTEST_LIBS) $(X_LIBS)
 libtotem_properties_page_la_LDFLAGS = $(modules_flags)
 
-test_properties_page_CFLAGS = -I$(top_srcdir)
+test_properties_page_CFLAGS = -I$(top_srcdir) $(WARN_CFLAGS)
 test_properties_page_SOURCES =		\
 	totem-properties-main.c		\
 	totem-properties-view.c		\
@@ -129,16 +129,30 @@ test_properties_page_LDADD =		\
 
 BUILT_SOURCES = 
 
-if HAVE_MOZILLA
+if ENABLE_BROWSER_PLUGINS
 
 BUILT_SOURCES += \
 	totem-mozilla-interface.h
 
 totem-mozilla-interface.h: totem-mozilla-interface.xml
-	$(DBUS_BIND) --prefix=totem_embedded --mode=glib-server \
-		--output=totem-mozilla-interface.h $<
+	$(DBUS_BIND) --prefix=totem_embedded --mode=glib-server $< > $@
+
+totem-mozilla-viewer-marshal.c: totem-mozilla-viewer-marshal.list
+	$(GLIB_GENMARSHAL) --prefix=totem_embedded_marshal $< --header --body > $@
+
+totem-mozilla-viewer-marshal.h: totem-mozilla-viewer-marshal.list
+	$(GLIB_GENMARSHAL) --prefix=totem_embedded_marshal $< --header > $@
+
+# FIXME until we can depend on a fixed dbus-binding-tool, fixup the binding with sed
+# see https://bugs.freedesktop.org/show_bug.cgi?id=6358
+totem-mozilla-client.h: totem-mozilla-interface.xml
+	$(DBUS_BIND) --prefix=totem_embedded --mode=glib-client $< | \
+	sed -e 's/DBusGAsyncData \*data = user_data/DBusGAsyncData *data = (DBusGAsyncData *) user_data/g' > $@
 
 totem_mozilla_viewer_SOURCES = 		\
+	totem-mozilla-interface.h	\
+	totem-mozilla-viewer-marshal.c	\
+	totem-mozilla-viewer-marshal.h	\
 	totem-mozilla-viewer.c		\
 	totem-mozilla-options.h
 
@@ -147,7 +161,7 @@ totem_mozilla_viewer_LDADD = \
 	libtotem_player.la plparse/libtotem-plparser.la \
 	$(EXTRA_GNOME_LIBS) $(XVIDMODE_LIBS) \
 	$(DBUS_LIBS) $(NVTV_LIBS) \
-	$(XTEST_LIBS) $(X_LIBS) 
+	$(XTEST_LIBS) $(X_LIBS)
 endif
 
 vanity_SOURCES =					\
@@ -182,11 +196,9 @@ INCLUDES = 						\
 	-I$(top_builddir)/src/plparse			\
 	-I$(top_builddir)/src/backend			\
 	$(EXTRA_GNOME_CFLAGS)				\
+	$(WARN_CFLAGS)					\
 	-DDBUS_API_SUBJECT_TO_CHANGE			\
 	$(DBUS_CFLAGS)					\
-	$(MOZILLA_CFLAGS)				\
-	$(MOZILLA_SUBDIR_INCLUDES)			\
-	$(MOZILLA_XPCOM_CFLAGS)				\
 	$(NVTV_CFLAGS)					\
 	$(NAUTILUS_CFLAGS)				\
 	-DGNOMELOCALEDIR=\""$(datadir)/locale"\"	\
Index: src/totem-mozilla-interface.xml
===================================================================
RCS file: /cvs/gnome/totem/src/totem-mozilla-interface.xml,v
retrieving revision 1.3
diff -p -u -B -r1.3 totem-mozilla-interface.xml
--- src/totem-mozilla-interface.xml	9 Aug 2006 16:28:54 -0000	1.3
+++ src/totem-mozilla-interface.xml	12 Nov 2006 20:08:25 -0000
@@ -1,13 +1,44 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 
-<node name="/">
-  <interface name="org.totem.MozillaPluginInterface">
-    <method name="Play"/>
-    <method name="Pause"/>
-    <method name="Stop"/>
+<node name="/org/gnome/totem/PluginViewer">
+  <interface name="org.gnome.totem.PluginViewer">
+    <method name="DoCommand">
+      <arg type="s" name="Command" direction="in" />
+    </method>
+    <method name="SetWindow">
+      <arg type="s" name="Controls" direction="in" />
+      <arg type="u" name="XID" direction="in" />
+      <arg type="i" name="Width" direction="in" />
+      <arg type="i" name="Height" direction="in" />
+    </method>
+    <method name="UnsetWindow">
+      <arg type="u" name="XID" direction="in" />
+    </method>
+    <method name="OpenStream">
+      <arg type="s" name="URI" direction="in" />
+      <arg type="s" name="BaseURI" direction="in" />
+    </method>
+    <method name="CloseStream" />
+    <method name="OpenURI">
+      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="totem_embedded_open_uri" />
+      <arg type="s" name="URI" direction="in" />
+      <arg type="s" name="BaseURI" direction="in" />
+    </method>
+    <method name="SetPlaylist">
+      <arg type="s" name="Path" direction="in" />
+      <arg type="s" name="URI" direction="in" />
+      <arg type="s" name="BaseURI" direction="in" />
+    </method>
     <method name="SetLocalFile">
-      <arg type="s"/>
+      <arg type="s" name="Path" direction="in" />
+      <arg type="s" name="URI" direction="in" />
+      <arg type="s" name="BaseURI" direction="in" />
     </method>
-    <signal name="StopSendingData"/>
+
+    <signal name="ButtonPress">
+      <!-- FIXME: this needs a parameter for the event timestamp -->
+    </signal>
+    <signal name="StartStream" />
+    <signal name="StopStream" />
   </interface>
 </node>
Index: src/totem-mozilla-options.h
===================================================================
RCS file: /cvs/gnome/totem/src/totem-mozilla-options.h,v
retrieving revision 1.9
diff -p -u -B -r1.9 totem-mozilla-options.h
--- src/totem-mozilla-options.h	30 Oct 2006 23:47:55 -0000	1.9
+++ src/totem-mozilla-options.h	12 Nov 2006 20:08:25 -0000
@@ -18,16 +18,29 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#define TOTEM_OPTION_XID		"xid"
-#define TOTEM_OPTION_WIDTH		"width"
-#define TOTEM_OPTION_HEIGHT		"height"
-#define TOTEM_OPTION_URL		"url"
-#define TOTEM_OPTION_CONTROLS_HIDDEN	"nocontrols"
-#define TOTEM_OPTION_STATUSBAR		"statusbar"
-#define TOTEM_OPTION_HREF		"href"
-#define TOTEM_OPTION_TARGET		"target"
+#ifndef __TOTEM_MOZILLA_OPTIONS_H__
+#define __TOTEM_MOZILLA_OPTIONS_H__
+
+#define TOTEM_PLUGIN_VIEWER_INTERFACE_NAME	"org.gnome.totem.PluginViewer"
+#define TOTEM_PLUGIN_VIEWER_NAME_TEMPLATE	"org.gnome.totem.PluginViewer_%d"
+#define TOTEM_PLUGIN_VIEWER_DBUS_PATH		"/org/gnome/totem/PluginViewer"
+
+#define TOTEM_OPTION_BASE_URI		"base-uri"
+#define TOTEM_OPTION_CONTROLS_HIDDEN	"no-controls"
 #define TOTEM_OPTION_HIDDEN		"hidden"
+#define TOTEM_OPTION_HREF		"href"
 #define TOTEM_OPTION_MIMETYPE		"mimetype"
+#define TOTEM_OPTION_NOAUTOSTART	"no-autostart"
 #define TOTEM_OPTION_PLAYLIST		"playlist"
+#define TOTEM_OPTION_PLUGIN_TYPE	"plugin-type"
 #define TOTEM_OPTION_REPEAT		"repeat"
-#define TOTEM_OPTION_NOAUTOSTART	"noautostart"
+#define TOTEM_OPTION_TARGET		"target"
+#define TOTEM_OPTION_URL		"url"
+#define TOTEM_OPTION_USER_AGENT		"user-agent"
+#define TOTEM_OPTION_STATUSBAR		"statusbar"
+
+#define TOTEM_COMMAND_PLAY		"Play"
+#define TOTEM_COMMAND_PAUSE		"Pause"
+#define TOTEM_COMMAND_STOP		"Stop"
+
+#endif /* !__TOTEM_MOZILLA_OPTIONS_H__ */
Index: src/totem-mozilla-viewer.c
===================================================================
RCS file: /cvs/gnome/totem/src/totem-mozilla-viewer.c,v
retrieving revision 1.69
diff -p -u -B -r1.69 totem-mozilla-viewer.c
--- src/totem-mozilla-viewer.c	30 Oct 2006 23:47:55 -0000	1.69
+++ src/totem-mozilla-viewer.c	12 Nov 2006 20:08:25 -0000
@@ -1,7 +1,8 @@
-/* Totem Mozilla Embedded Viewer
+/* Totem Plugin Embedded Viewer
  *
  * Copyright (C) <2004-2006> Bastien Nocera <hadess@hadess.net>
  * Copyright (C) <2002> David A. Schleef <ds@schleef.org>
+ * Copyright © 2006 Christian Persch
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -21,14 +22,18 @@
 
 #include "config.h"
 
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
 
+#include <glib.h>
+#include <glib/gi18n.h>
 #include <gdk/gdkx.h>
+#include <gtk/gtk.h>
 #include <glade/glade.h>
 #include <gconf/gconf-client.h>
+
 #include <totem-pl-parser.h>
 
 #include <dbus/dbus-glib.h>
@@ -32,8 +37,6 @@
 #include <totem-pl-parser.h>
 
 #include <dbus/dbus-glib.h>
-#include <sys/types.h>
-#include <unistd.h>
 
 #include <libgnomevfs/gnome-vfs.h>
 #include <libgnomevfs/gnome-vfs-mime-handlers.h>
@@ -45,11 +48,11 @@
 #include "bacon-volume.h"
 #include "video-utils.h"
 
+#include "totem-mozilla-viewer-marshal.h"
+
 GtkWidget *totem_statusbar_create (void);
 GtkWidget *totem_volume_create (void);
 
-#define IS_FD_STREAM (strcmp(emb->filename, "fd://0") == 0)
-
 #define VOLUME_DOWN_OFFSET -8
 #define VOLUME_UP_OFFSET 8
 
@@ -61,15 +64,27 @@ GtkWidget *totem_volume_create (void);
 typedef enum {
 	STATE_PLAYING,
 	STATE_PAUSED,
-	STATE_STOPPED
+	STATE_STOPPED,
+	LAST_STATE
 } TotemStates;
 
+typedef enum {
+	TOTEM_PLUGIN_TYPE_BASIC,
+	TOTEM_PLUGIN_TYPE_GMP,
+	TOTEM_PLUGIN_TYPE_COMPLEX,
+	TOTEM_PLUGIN_TYPE_NARROWSPACE,
+	TOTEM_PLUGIN_TYPE_MULLY,
+	TOTEM_PLUGIN_TYPE_LAST
+} TotemPluginType;
+
 #define TOTEM_TYPE_EMBEDDED (totem_embedded_get_type ())
-typedef struct {
-	GObjectClass parent_class;
+#define TOTEM_EMBEDDED(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), TOTEM_TYPE_EMBEDDED, TotemEmbedded))
+#define TOTEM_EMBEDDED_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), TOTEM_TYPE_EMBEDDED, TotemEmbeddedClass))
+#define TOTEM_IS_EMBEDDED(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), TOTEM_TYPE_EMBEDDED))
+#define TOTEM_IS_EMBEDDED_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TOTEM_TYPE_EMBEDDED))
+#define TOTEM_EMBEDDED_GET_CLASS(o)(G_TYPE_INSTANCE_GET_CLASS ((o), TOTEM_TYPE_EMBEDDED, TotemEmbeddedClass))
 
-	void (*stop_sending_data) (GObject *emb);
-} TotemEmbeddedClass;
+typedef GObjectClass TotemEmbeddedClass;
 
 typedef struct _TotemEmbedded {
 	GObject parent;
@@ -79,16 +94,19 @@ typedef struct _TotemEmbedded {
 	GtkWidget *about;
 	TotemStatusbar *statusbar;
 	int width, height;
-	const char *orig_filename;
 	const char *mimetype;
-	char *filename, *href, *target;
+	char *base_uri;
+	char *current_uri;
+	char *href;
+	char *target;
+	char *stream_uri;
 	BaconVideoWidget *bvw;
 	TotemStates state;
 	GdkCursor *cursor;
 
 	/* Playlist */
 	GList *playlist, *current;
-	GMainLoop *loop;
+	guint parser_id;
 	int num_items;
 
 	/* Open menu item */
@@ -99,50 +117,117 @@ typedef struct _TotemEmbedded {
 	GtkAdjustment *seekadj;
 	GtkWidget *seek;
 
+	/* Error */
+
+	guint type : 3; /* TotemPluginType */
+
+	guint is_browser_stream : 1;
 	guint is_playlist : 1;
-	guint embedded_done : 1;
 	guint controller_hidden : 1;
+	guint show_statusbar : 1;
 	guint hidden : 1;
 	guint repeat : 1;
 	guint seeking : 1;
 	guint noautostart : 1;
+	guint autostart : 1;
 } TotemEmbedded;
 
 GType totem_embedded_get_type (void);
 
+#define TOTEM_EMBEDDED_ERROR_QUARK (g_quark_from_static_string ("TotemEmbeddedErrorQuark"))
+
+enum
+{
+	TOTEM_EMBEDDED_UNKNOWN_PLUGIN_TYPE,
+	TOTEM_EMBEDDED_SETWINDOW_UNSUPPORTED_CONTROLS,
+	TOTEM_EMBEDDED_SETWINDOW_HAVE_WINDOW,
+	TOTEM_EMBEDDED_SETWINDOW_INVALID_XID,
+	TOTEM_EMBEDDED_NO_URI,
+	TOTEM_EMBEDDED_OPEN_FAILED,
+	TOTEM_EMBEDDED_UNKNOWN_COMMAND
+};
+
 G_DEFINE_TYPE (TotemEmbedded, totem_embedded, G_TYPE_OBJECT);
 static void totem_embedded_init (TotemEmbedded *emb) { }
 
-gboolean totem_embedded_play (TotemEmbedded *emb, GError **err);
-gboolean totem_embedded_pause (TotemEmbedded *emb, GError **err);
-gboolean totem_embedded_stop (TotemEmbedded *emb, GError **err);
-gboolean totem_embedded_set_local_file (TotemEmbedded *emb, const char *url, GError **err);
+static gboolean totem_embedded_do_command (TotemEmbedded *emb, const char *command, GError **err);
+static gboolean totem_embedded_push_parser (gpointer data);
 
-static void totem_embedded_set_menu (TotemEmbedded *emb, gboolean enable);
+static void totem_embedded_set_menu (TotemEmbedded *emb, gboolean enable_copy);
 static void on_open1_activate (GtkButton *button, TotemEmbedded *emb);
 
-#include "totem-mozilla-interface.h"
-
 enum {
-	STOP_SENDING_DATA,
+	BUTTON_PRESS,
+	START_STREAM,
+	STOP_STREAM,
 	LAST_SIGNAL
 };
-static int totem_emb_table_signals[LAST_SIGNAL] = { 0 };
+static int signals[LAST_SIGNAL] = { 0 };
+
+static void
+totem_embedded_finalize (GObject *object)
+{
+	TotemEmbedded *embedded = TOTEM_EMBEDDED (object);
+
+	if (embedded->window)
+		gtk_widget_destroy (embedded->window);
+
+	if (embedded->xml)
+		g_object_unref (embedded->xml);
+	if (embedded->menuxml)
+		g_object_unref (embedded->menuxml);
+
+	/* FIXME etc */
+
+	G_OBJECT_CLASS (totem_embedded_parent_class)->finalize (object);
+}
 
 static void totem_embedded_class_init (TotemEmbeddedClass *klass)
 {
-	GObjectClass *object_class;
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-	object_class = (GObjectClass *) klass;
+	object_class->finalize = totem_embedded_finalize;
 
-	totem_emb_table_signals[STOP_SENDING_DATA] =
-		g_signal_new ("stop-sending-data",
+#if 0
+	GType args[2];
+	args[0] = args[1] = G_TYPE_UINT;
+	signals[BUTTON_PRESS] =
+		g_signal_newv ("button-press",
+				G_TYPE_FROM_CLASS (object_class),
+				G_SIGNAL_RUN_LAST,
+				NULL /* class closure */,
+				NULL /* accu */, NULL /* accu data */,
+				totem_embedded_marshal_VOID__UINT_UINT,
+				G_TYPE_NONE,
+				2, args);
+#endif
+	signals[BUTTON_PRESS] =
+		g_signal_newv ("button-press",
+				G_TYPE_FROM_CLASS (object_class),
+				G_SIGNAL_RUN_LAST,
+				NULL /* class closure */,
+				NULL /* accu */, NULL /* accu data */,
+				g_cclosure_marshal_VOID__VOID,
+				G_TYPE_NONE,
+				0, NULL);
+	signals[START_STREAM] =
+		g_signal_newv ("start-stream",
+				G_TYPE_FROM_CLASS (object_class),
+				G_SIGNAL_RUN_LAST,
+				NULL /* class closure */,
+				NULL /* accu */, NULL /* accu data */,
+				g_cclosure_marshal_VOID__VOID,
+				G_TYPE_NONE,
+				0, NULL);
+	signals[STOP_STREAM] =
+		g_signal_newv ("stop-stream",
 				G_TYPE_FROM_CLASS (object_class),
 				G_SIGNAL_RUN_LAST,
-				G_STRUCT_OFFSET (TotemEmbeddedClass, stop_sending_data),
-				NULL, NULL,
+				NULL /* class closure */,
+				NULL /* accu */, NULL /* accu data */,
 				g_cclosure_marshal_VOID__VOID,
-				G_TYPE_NONE, 0);
+				G_TYPE_NONE,
+				0, NULL);
 }
 
 static void
@@ -155,28 +240,26 @@ totem_embedded_exit (TotemEmbedded *emb)
 static void
 totem_embedded_error_and_exit (char *title, char *reason, TotemEmbedded *emb)
 {
+	/* FIXME send a signal to the plugin with the error message instead! */
 	totem_interface_error_blocking (title, reason,
 			GTK_WINDOW (emb->window));
 	totem_embedded_exit (emb);
 }
 
 static void
-totem_embedded_emit_stop_sending_data (TotemEmbedded *emb)
+totem_embedded_set_error (TotemEmbedded *emb,
+			  char *primary,
+			  char *secondary)
 {
-	g_return_if_fail (emb->filename != NULL);
-	if (IS_FD_STREAM) {
-		g_signal_emit (G_OBJECT (emb),
-				totem_emb_table_signals[STOP_SENDING_DATA],
-				0, NULL);
-	}
+	/* FIXME */
 }
 
 static void
 totem_embedded_set_state (TotemEmbedded *emb, TotemStates state)
 {
-	char *id = NULL;
 	GtkWidget *image;
 	GdkCursor *cursor;
+	char id[32];
 
 	if (state == emb->state)
 		return;
@@ -188,18 +271,19 @@ totem_embedded_set_state (TotemEmbedded 
 	case STATE_STOPPED:
 		if (emb->href != NULL)
 			cursor = emb->cursor;
-		id = g_strdup_printf ("gtk-media-play-%s",
-				gtk_widget_get_direction (image) ? "ltr" : "rtl");
+
+		g_snprintf (id, sizeof (id), "gtk-media-play-%s",
+			    gtk_widget_get_direction (image) ? "ltr" : "rtl");
 		totem_statusbar_set_text (emb->statusbar, _("Stopped"));
 		totem_statusbar_set_time_and_length (emb->statusbar, 0, 0);
 		break;
 	case STATE_PAUSED:
-		id = g_strdup_printf ("gtk-media-play-%s",
+		g_snprintf (id, sizeof (id), "gtk-media-play-%s",
 				gtk_widget_get_direction (image) ? "ltr" : "rtl");
 		totem_statusbar_set_text (emb->statusbar, _("Paused"));
 		break;
 	case STATE_PLAYING:
-		id = g_strdup ("gtk-media-pause");
+		g_snprintf (id, sizeof (id), "gtk-media-pause");
 		totem_statusbar_set_text (emb->statusbar, _("Playing"));
 		break;
 	default:
@@ -207,7 +291,7 @@ totem_embedded_set_state (TotemEmbedded 
 	}
 
 	gtk_image_set_from_icon_name (GTK_IMAGE (image), id, GTK_ICON_SIZE_MENU);
-	g_free (id);
+
 	if (emb->hidden == FALSE && cursor != NULL)
 		gdk_window_set_cursor (GTK_WIDGET (emb->bvw)->window, cursor);
 
@@ -224,34 +308,57 @@ totem_embedded_set_pp_state (TotemEmbedd
 }
 
 static gboolean
-totem_embedded_open (TotemEmbedded *emb)
+totem_embedded_open_internal (TotemEmbedded *emb,
+			      GError **error)
 {
 	GError *err = NULL;
 	gboolean retval;
+	const char *uri;
+
+	/* FIXME: stop previous content, or is that automatic ? */
+
+	if (emb->is_browser_stream)
+		uri = "fd://0";
+	else
+		uri = emb->current_uri;
+
+	if (!uri) {
+		g_set_error (error,
+			     TOTEM_EMBEDDED_ERROR_QUARK,
+			     TOTEM_EMBEDDED_NO_URI,
+			     _("No URI to play"));
+		//FIXME totem_embedded_set_error (emb, error); |error| may be null?
 
-	g_message ("totem_embedded_open '%s'", emb->filename);
+		return FALSE;
+	}
+
+	g_message ("totem_embedded_open_internal '%s' is-browser-stream %d", uri, emb->is_browser_stream);
 
-	retval = bacon_video_widget_open (emb->bvw, emb->filename, &err);
+	/* FIXME: remove |err| and rely on async on_error? */
+	g_message ("BEFORE _open");
+	retval = bacon_video_widget_open (emb->bvw, uri, &err);
+	g_message ("AFTER _open");
 	if (retval == FALSE)
 	{
-		char *msg, *disp;
+		GError *errint;
+		char *primary;
 
-		totem_embedded_emit_stop_sending_data (emb);
+		/* FIXME we haven't even started sending yet! */
+		//g_signal_emit (emb, signals[STOP_STREAM], 0);
 
 		totem_embedded_set_state (emb, STATE_STOPPED);
 
-		//FIXME if emb->filename is fd://0 or stdin:///
-		//we should use a better name than that
-		disp = g_strdup (emb->filename);
-		//disp = gnome_vfs_unescape_string_for_display (totem->mrl);
-		msg = g_strdup_printf(_("Totem could not play '%s'."), disp);
-		g_free (disp);
+		errint = g_error_new (TOTEM_EMBEDDED_ERROR_QUARK,
+				      TOTEM_EMBEDDED_OPEN_FAILED,
+				      _("Totem could not play '%s'"),
+				     emb->current_uri);
+		
+		//FIXME disp = gnome_vfs_unescape_string_for_display (totem->mrl); ?
+		primary = g_strdup_printf(_("Totem could not play '%s'"), emb->current_uri);
+		totem_embedded_set_error (emb, primary, err->message);;
+		g_free (primary);
 
-		g_message ("error: %s", err->message);
-		totem_interface_error_blocking (msg, err->message,
-				GTK_WINDOW (emb->window));
-		g_free (msg);
-		g_error_free (err);
+		g_propagate_error (error, err);
 
 		totem_embedded_set_pp_state (emb, FALSE);
 	} else {
@@ -259,7 +366,8 @@ totem_embedded_open (TotemEmbedded *emb)
 		totem_embedded_set_pp_state (emb, TRUE);
 	}
 
-	if (IS_FD_STREAM && emb->num_items > 1) {
+	/* FIXME? */
+	if (emb->is_browser_stream && emb->num_items > 1) {
 		totem_embedded_set_menu (emb, FALSE);
 	} else {
 		totem_embedded_set_menu (emb, TRUE);
@@ -268,8 +376,9 @@ totem_embedded_open (TotemEmbedded *emb)
 	return retval;
 }
 
-gboolean
-totem_embedded_play (TotemEmbedded *emb, GError **err)
+static gboolean
+totem_embedded_play (TotemEmbedded *emb,
+		     GError **error)
 {
 	if (bacon_video_widget_play (emb->bvw, NULL))
 		totem_embedded_set_state (emb, STATE_PLAYING);
@@ -276,8 +386,9 @@ totem_embedded_play (TotemEmbedded *emb,
 	return TRUE;
 }
 
-gboolean
-totem_embedded_pause (TotemEmbedded *emb, GError **err)
+static gboolean
+totem_embedded_pause (TotemEmbedded *emb,
+		      GError **error)
 {
 	bacon_video_widget_pause (emb->bvw);
 	totem_embedded_set_state (emb, STATE_PAUSED);
@@ -284,8 +396,9 @@ totem_embedded_pause (TotemEmbedded *emb
 	return TRUE;
 }
 
-gboolean
-totem_embedded_stop (TotemEmbedded *emb, GError **err)
+static gboolean
+totem_embedded_stop (TotemEmbedded *emb,
+		     GError **error)
 {
 	bacon_video_widget_stop (emb->bvw);
 	totem_embedded_set_state (emb, STATE_STOPPED);
@@ -292,27 +406,180 @@ totem_embedded_stop (TotemEmbedded *emb,
 	return TRUE;
 }
 
-gboolean
+static gboolean
+totem_embedded_do_command (TotemEmbedded *embedded,
+			   const char *command,
+			   GError **error)
+{
+	g_return_val_if_fail (command != NULL, FALSE);
+
+	if (strcmp (command, TOTEM_COMMAND_PLAY) == 0) {
+		return totem_embedded_play (embedded, error);
+	}
+	if (strcmp (command, TOTEM_COMMAND_PAUSE) == 0) {
+		return totem_embedded_pause (embedded, error);
+	}
+	if (strcmp (command, TOTEM_COMMAND_STOP) == 0) {
+		return totem_embedded_stop (embedded, error);
+	}
+		
+	g_set_error (error,
+		     TOTEM_EMBEDDED_ERROR_QUARK,
+		     TOTEM_EMBEDDED_UNKNOWN_COMMAND,
+		     "Unknown command '%s'", command);
+	return FALSE;
+}
+
+static void
+totem_embedded_set_uri (TotemEmbedded *emb,
+		        const char *uri,
+		        const char *base_uri,
+		        gboolean is_browser_stream)
+{
+	char *old_uri, *old_base;
+
+	old_uri = emb->current_uri;
+	old_base = emb->base_uri;
+
+	emb->current_uri = g_strdup (uri);
+	emb->base_uri = g_strdup (base_uri);
+	emb->is_browser_stream = is_browser_stream != FALSE;
+
+	g_free (old_uri);
+	g_free (old_base);
+}
+
+static gboolean
+totem_embedded_open_uri (TotemEmbedded *emb,
+			 const char *uri,
+			 const char *base_uri,
+			 GError **error)
+{
+	/* FIXME: stop previous content?? */
+	totem_embedded_set_uri (emb, uri, base_uri, FALSE);
+
+	bacon_video_widget_close (emb->bvw);
+	return totem_embedded_open_internal (emb, error);
+}
+
+static gboolean
+totem_embedded_open_stream (TotemEmbedded *emb,
+			    const char *uri,
+			    const char *base_uri,
+			    GError **error)
+{
+	/* FIXME: stop previous content?? */
+	totem_embedded_set_uri (emb, uri, base_uri, TRUE);
+
+	bacon_video_widget_close (emb->bvw);
+	return totem_embedded_open_internal (emb, error);
+}
+
+static gboolean
+totem_embedded_close_stream (TotemEmbedded *emb,
+			     GError *error)
+{
+	if (!emb->is_browser_stream)
+		return TRUE;
+
+	/* FIXME this enough? */
+	bacon_video_widget_close (emb->bvw);
+
+	return TRUE;
+}
+
+static gboolean
+totem_embedded_open_playlist_item (TotemEmbedded *emb,
+				   GList *item)
+{
+	gboolean eop;
+
+	if (!emb->playlist)
+		return FALSE;
+
+	eop = item == NULL;
+
+	/* Start at the head */
+	if (!item)
+		item = emb->playlist;
+
+	/* FIXME: if (emb->current == item) { just start again, depending on repeat/autostart settings } */
+	emb->current = item;
+	g_assert (item != NULL);
+
+	totem_embedded_set_uri (emb,
+				(const char *) item->data,
+			        emb->base_uri /* FIXME? */,
+			        FALSE);
+
+	bacon_video_widget_close (emb->bvw);
+	if (totem_embedded_open_internal (emb, NULL /* FIXME */) &&
+	    ((eop != FALSE && emb->repeat != FALSE)
+		|| (eop == FALSE))) {
+		totem_embedded_play (emb, NULL);
+	}
+
+	return TRUE;
+}
+
+static gboolean
 totem_embedded_set_local_file (TotemEmbedded *emb,
-			       const char *path, GError **err)
+			       const char *path,
+			       const char *uri,
+			       const char *base_uri,
+			       GError **error)
 {
+	char *file_uri;
+
 	g_message ("Setting the current path to %s", path);
 
-	g_free (emb->filename);
-	emb->filename = g_filename_to_uri (path, NULL, NULL);
+	file_uri = g_filename_to_uri (path, NULL, error);
+	if (!file_uri)
+		return FALSE;
+
+	/* FIXME what about |uri| param?!! */
+	totem_embedded_set_uri (emb, file_uri, base_uri, FALSE);
+	g_free (file_uri);
+
+	return totem_embedded_open_internal (emb, error);
+}
+
+static gboolean
+totem_embedded_set_playlist (TotemEmbedded *emb,
+			     const char *path,
+			     const char *uri,
+			     const char *base_uri,
+			     GError **error)
+{
+	char *file_uri;
+
+	g_message ("Setting the current playlist to %s", path);
+
+	file_uri = g_filename_to_uri (path, NULL, error);
+	if (!file_uri)
+		return FALSE;
+
+	totem_embedded_set_uri (emb, file_uri, base_uri, FALSE);
+	g_free (file_uri);
+
+	/* Schedule parsing on idle */
+	if (emb->parser_id == 0)
+		emb->parser_id = g_idle_add (totem_embedded_push_parser,
+					     emb);
 
 	return TRUE;
 }
 
 static void
-totem_embedded_set_menu (TotemEmbedded *emb, gboolean enable)
+totem_embedded_set_menu (TotemEmbedded *emb,
+			 gboolean enable_copy)
 {
 	GtkWidget *menu, *item, *image;
 	GtkWidget *copy;
 	char *label;
 
 	copy = glade_xml_get_widget (emb->menuxml, "copy_location1");
-	gtk_widget_set_sensitive (copy, enable);
+	gtk_widget_set_sensitive (copy, enable_copy);
 
 	if (emb->menu_item != NULL) {
 		gtk_widget_destroy (emb->menu_item);
@@ -323,15 +590,16 @@ totem_embedded_set_menu (TotemEmbedded *
 		emb->app = NULL;
 	}
 
-	if (enable == FALSE)
+	if (enable_copy == FALSE)
 		return;
 
-	if (IS_FD_STREAM) {
+	if (emb->mimetype) {
 		emb->app = gnome_vfs_mime_get_default_application_for_uri
-			(emb->orig_filename, emb->mimetype);
+				(emb->current_uri, emb->mimetype);
 	} else {
 		emb->app = gnome_vfs_mime_get_default_application_for_uri
-			(emb->filename, emb->mimetype);
+				(emb->current_uri,
+				 gnome_vfs_get_mime_type_for_name (emb->current_uri));
 	}
 
 	if (emb->app == NULL)
@@ -361,14 +629,15 @@ on_open1_activate (GtkButton *button, To
 
 	g_return_if_fail (emb->app != NULL);
 
-	if (IS_FD_STREAM) {
-		l = g_list_prepend (l, (gpointer) emb->orig_filename);
-	} else if (emb->href != NULL) {
+	/* FIXME  */
+	 if (emb->type == TOTEM_PLUGIN_TYPE_NARROWSPACE &&
+	     emb->href != NULL) {
 		l = g_list_prepend (l, emb->href);
 	} else {
-		l = g_list_prepend (l, emb->filename);
+		l = g_list_prepend (l, emb->current_uri);
 	}
 
+	/* FIXME: launch with startup ID and startup notification! */
 	if (gnome_vfs_mime_application_launch (emb->app, l) == GNOME_VFS_OK) {
 		totem_embedded_stop (emb, NULL);
 	}
@@ -385,6 +654,7 @@ on_about1_activate (GtkButton *button, T
 	{
 		"Bastien Nocera <hadess@hadess.net>",
 		"Ronald Bultje <rbultje@ronald.bitfreak.net>",
+		"Christian Persch",
 		NULL
 	};
 	
@@ -412,12 +682,12 @@ on_about1_activate (GtkButton *button, T
 	g_free (description);
 
 	totem_interface_set_transient_for (GTK_WINDOW (emb->about),
-			GTK_WINDOW (emb->window));
+					   GTK_WINDOW (emb->window));
 
 	g_object_add_weak_pointer (G_OBJECT (emb->about),
-			(gpointer *)&emb->about);
+				   (gpointer *)&emb->about);
 	g_signal_connect (G_OBJECT (emb->about), "response",
-			G_CALLBACK (gtk_widget_destroy), NULL);
+			  G_CALLBACK (gtk_widget_destroy), NULL);
 
 	gtk_widget_show (emb->about);
 }
@@ -425,24 +695,28 @@ on_about1_activate (GtkButton *button, T
 static void
 on_copy_location1_activate (GtkButton *button, TotemEmbedded *emb)
 {
+	GdkDisplay *display;
 	GtkClipboard *clip;
-	const char *filename;
+	const char *uri;
 
-	if (IS_FD_STREAM) {
-		filename = emb->orig_filename;
+	if (emb->is_browser_stream) {
+		uri = emb->base_uri; /* FIXME! */
 	} else if (emb->href != NULL) {
-		filename = emb->href;
+		uri = emb->href;
 	} else {
-		filename = emb->filename;
+		uri = emb->current_uri;
 	}
 
+	display = gtk_widget_get_display (GTK_WIDGET (emb->window));
+
 	/* Set both the middle-click and the super-paste buffers */
-	clip = gtk_clipboard_get_for_display
-		(gdk_display_get_default(), GDK_SELECTION_CLIPBOARD);
-	gtk_clipboard_set_text (clip, filename, -1);
-	clip = gtk_clipboard_get_for_display
-		(gdk_display_get_default(), GDK_SELECTION_PRIMARY);
-	gtk_clipboard_set_text (clip, filename, -1);
+	clip = gtk_clipboard_get_for_display (display,
+					      GDK_SELECTION_CLIPBOARD);
+	gtk_clipboard_set_text (clip, uri, -1);
+
+	clip = gtk_clipboard_get_for_display (display,
+					      GDK_SELECTION_PRIMARY);
+	gtk_clipboard_set_text (clip, uri, -1);
 }
 
 static void
@@ -458,84 +732,90 @@ on_play_pause (GtkWidget *widget, TotemE
 static void
 on_got_redirect (GtkWidget *bvw, const char *mrl, TotemEmbedded *emb)
 {
-	gchar *new_filename;
+	gchar *new_uri;
 
-	g_message ("url: %s", emb->orig_filename);
+	g_message ("url: %s", emb->base_uri);
 	g_message ("redirect: %s", mrl);
 
-	if (emb->orig_filename)
-		new_filename = totem_resolve_relative_link (emb->orig_filename, mrl);
+	if (emb->base_uri)
+		new_uri = totem_resolve_relative_link (emb->base_uri, mrl);
 	else
-		new_filename = totem_resolve_relative_link (emb->filename, mrl);
+		new_uri = totem_resolve_relative_link (emb->current_uri, mrl);
 
-	g_free (emb->filename);
-	emb->filename = new_filename;
+	/* FIXME: use totem_embedded_open_uri? Or construct a playlist item and play it? */
+
+	totem_embedded_set_uri (emb, new_uri, emb->base_uri /* FIXME? */, FALSE);
 
 	bacon_video_widget_close (emb->bvw);
 	totem_embedded_set_state (emb, STATE_STOPPED);
 
-	if (totem_embedded_open (emb) != FALSE)
+	if (totem_embedded_open_internal (emb, NULL /* FIXME? */) != FALSE)
 		totem_embedded_play (emb, NULL);
 }
 
 static gboolean
-on_video_button_press_event (BaconVideoWidget *bvw, GdkEventButton *event,
-		TotemEmbedded *emb)
+on_video_button_press_event (BaconVideoWidget *bvw,
+			     GdkEventButton *event,
+			     TotemEmbedded *emb)
 {
+	guint state = event->state & gtk_accelerator_get_default_mod_mask ();
+	gboolean handled = FALSE;
+
+	//g_print ("button-press type %d button %d state %d play-state %d\n",
+	//	 event->type, event->button, state, emb->state);
+
 	if (event->type == GDK_BUTTON_PRESS &&
-			event->button == 1 &&
-			emb->href != NULL)
-	{
-		if (emb->target != NULL &&
-		    !g_ascii_strcasecmp (emb->target, "QuicktimePlayer")) {
-			gchar *cmd;
-			GError *err = NULL;
+	    event->button == 1 &&
+	    state == 0 &&
+	    emb->state == STATE_STOPPED) {
+		g_signal_emit (emb, signals[BUTTON_PRESS], 0,
+			       event->time,
+			       event->button);
+		handled = TRUE;
+	}
 
+#if 0
+		if (emb->state == STATE_STOPPED) {
+			g_signal_emit (emb, signals[START_PLAY], 0);
+		}
+#ifdef TOTEM_RUN_IN_SOURCE_TREE
 			if (g_file_test ("./totem",
 					 G_FILE_TEST_EXISTS) != FALSE) {
 				cmd = g_strdup_printf ("./totem %s",
 						       emb->href);
-			} else {
+			} else
+#endif
+			{
 				cmd = g_strdup_printf (BINDIR"/totem %s",
 						       emb->href);
 			}
+			/* FIXME Shouldn't this use gdk_spawn_on_screen? */
 			if (!g_spawn_command_line_async (cmd, &err)) {
+#ifdef NOTPORTED
+				/* FIXME this is unacceptable! */
 				totem_interface_error_blocking (
 					_("Failed to start stand-alone movie player"),
-					err ? err->message : _("Unknown reason"),
+					err->message,
 					GTK_WINDOW (emb->window));
+				g_error_free (err);
+#endif
 			}
 			g_free (cmd);
-		} else {
-			g_free (emb->filename);
-			emb->filename = emb->href;
-			emb->href = NULL;
-			bacon_video_widget_close (emb->bvw);
-			totem_embedded_set_state (emb, STATE_STOPPED);
-
-			if (emb->controller_hidden != FALSE) {
-				GtkWidget *controls;
-				controls = glade_xml_get_widget (emb->xml, "controls");
-				gtk_widget_show (controls);
-			}
-
-
-			if (totem_embedded_open (emb) != FALSE)
-				totem_embedded_play (emb, NULL);
+#endif
 
-			return TRUE;
-		}
-	} else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
+	else if (event->type == GDK_BUTTON_PRESS &&
+		 event->button == 3 &&
+		 state == 0) {
 		GtkMenu *menu;
 
 		menu = GTK_MENU (glade_xml_get_widget (emb->menuxml, "menu"));
 		gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
 				event->button, event->time);
 
-		return TRUE;
+		handled = TRUE;
 	}
 
-	return FALSE;
+	return handled;
 }
 
 static void
@@ -544,61 +824,52 @@ on_eos_event (GtkWidget *bvw, TotemEmbed
 	totem_embedded_set_state (emb, STATE_STOPPED);
 	gtk_adjustment_set_value (emb->seekadj, 0);
 
+	/* FIXME: the plugin needs to handle EOS itself, e.g. for QTNext */
+
 	/* No playlist if we have fd://0, right? */
-	if (IS_FD_STREAM) {
+	if (emb->is_browser_stream) {
+		/* FIXME: totem_embedded_set_state missing? */
+		/* FIXME: should find a way to enable playback of the stream again without re-requesting it */
 		totem_embedded_set_pp_state (emb, FALSE);
+	/* FIXME? else if (emb->playing_nth_item == emb->playlist_num_items) ? */
 	} else if (emb->num_items == 1) {
-		if (g_str_has_prefix (emb->filename, "file://") != FALSE) {
+		if (g_str_has_prefix (emb->current_uri, "file://") != FALSE) {
 			if (bacon_video_widget_is_seekable (emb->bvw) != FALSE) {
 				bacon_video_widget_pause (emb->bvw);
 				bacon_video_widget_seek (emb->bvw, 0.0, NULL);
 			} else {
 				bacon_video_widget_close (emb->bvw);
-				totem_embedded_open (emb);
+				totem_embedded_open_internal (emb, NULL /* FIXME? */);
 			}
 		} else {
 			bacon_video_widget_close (emb->bvw);
-			totem_embedded_open (emb);
-		}
-		if (emb->repeat != FALSE && emb->noautostart == FALSE)
-			totem_embedded_play (emb, NULL);
-	} else {
-		/* Multiple items on the playlist */
-		gboolean eop = FALSE, res;
-
-		if (emb->current->next == NULL) {
-			emb->current = emb->playlist;
-			eop = TRUE;
-		} else {
-			emb->current = emb->current->next;
+			totem_embedded_open_internal (emb, NULL /* FIXME? */);
 		}
-
-		g_free (emb->filename);
-		emb->filename = g_strdup (emb->current->data);
-		bacon_video_widget_close (emb->bvw);
-		res = totem_embedded_open (emb);
-		if (res != FALSE &&
-				((eop != FALSE && emb->repeat != FALSE)
-				 || (eop == FALSE))) {
+		if (emb->repeat != FALSE && emb->autostart)
 			totem_embedded_play (emb, NULL);
-		}
+	} else if (emb->current) {
+		totem_embedded_open_playlist_item (emb, emb->current->next);
 	}
 }
 
 static void
-on_error_event (BaconVideoWidget *bvw, char *message,
-                gboolean playback_stopped, gboolean fatal, TotemEmbedded *emb)
+on_error_event (BaconVideoWidget *bvw,
+		char *message,
+                gboolean playback_stopped,
+		gboolean fatal,
+		TotemEmbedded *emb)
 {
-	if (playback_stopped)
+	if (playback_stopped) {
+		/* FIXME: necessary? */
+		if (emb->is_browser_stream)
+			g_signal_emit (emb, signals[STOP_STREAM], 0);
+	
 		totem_embedded_set_state (emb, STATE_STOPPED);
-
-	if (fatal == FALSE) {
-		totem_interface_error (_("An error occurred"), message,
-		                       GTK_WINDOW (emb->window));
-	} else {
-		totem_embedded_error_and_exit (_("An error occurred"),
-				message, emb);
 	}
+
+	/* FIXME: what does 'fatal' mean here?? */
+
+	totem_embedded_set_error (emb, _("An error occurred"), message);
 }
 
 static void
@@ -675,37 +946,81 @@ cb_on_seek (GtkWidget *widget, GdkEventB
 	return FALSE;
 }
 
+#ifdef GNOME_ENABLE_DEBUG
 static void
-totem_embedded_add_children (TotemEmbedded *emb)
+controls_size_allocate_cb (GtkWidget *controls,
+			   GtkAllocation *allocation,
+			   gpointer data)
+{
+	g_message ("Viewer: Controls height is %dpx", allocation->height);
+	g_signal_handlers_disconnect_by_func (controls, G_CALLBACK (controls_size_allocate_cb), NULL);
+}
+#endif
+
+static gboolean
+totem_embedded_construct (TotemEmbedded *emb,
+			  GdkNativeWindow xid,
+			  int width,
+			  int height)
 {
 	GtkWidget *child, *container, *pp_button, *vbut;
-	BvwUseType type = BVW_USE_TYPE_VIDEO;
+	BvwUseType type;
 	GError *err = NULL;
 	GConfClient *gc;
+	GdkPixbuf *icon;
 	int volume;
 
-	emb->xml = totem_interface_load_with_root ("mozilla-viewer.glade",
-			"vbox1", _("Plugin"), TRUE,
-			GTK_WINDOW (emb->window));
-	emb->menuxml = totem_interface_load_with_root ("mozilla-viewer.glade",
-			"menu", _("Menu"), TRUE,
-			GTK_WINDOW (emb->window));
+	if (xid != 0) {
+		g_assert (!emb->hidden);
+
+		emb->window = gtk_plug_new (xid);
+#if 0
+		/* FIXME: well; this is spectacularly useless,
+		* since gtk_plug_new constructs the plug
+		* which emits the signal. So we're way too
+		* late with this connection!
+		*/
+		g_signal_connect (emb->window, "embedded",
+				  G_CALLBACK (embedded_cb), emb);
+#endif
+		/* FIXME why? */
+		gtk_widget_realize (emb->window);
 
-	if (emb->xml == NULL || emb->menuxml == NULL)
-		totem_embedded_exit (emb);
+		emb->xml = totem_interface_load_with_root ("mozilla-viewer.glade",
+				"content_box", _("Plugin") /* FIXME! */, TRUE,
+				GTK_WINDOW (emb->window));
+		g_assert (emb->xml);
+
+		child = glade_xml_get_widget (emb->xml, "content_box");
+		gtk_container_add (GTK_CONTAINER (emb->window), child);
+	} else {
+		g_assert (emb->hidden);
+
+		emb->xml = totem_interface_load_with_root ("mozilla-viewer.glade",
+				"window", _("Plugin") /* FIXME! */, TRUE,
+				GTK_WINDOW (emb->window));
+		g_assert (emb->xml);
 
-	child = glade_xml_get_widget (emb->xml, "vbox1");
-	gtk_container_add (GTK_CONTAINER (emb->window), child);
+		emb->window = glade_xml_get_widget (emb->xml, "window");
+	}
+
+	//gtk_widget_realize (embedded->window);
 
 	if (emb->hidden)
 		type = BVW_USE_TYPE_AUDIO;
+	else
+		type = BVW_USE_TYPE_VIDEO;
 
 	emb->bvw = BACON_VIDEO_WIDGET (bacon_video_widget_new
 			(-1, -1, type, &err));
 
+	/* FIXME! */
 	if (emb->bvw == NULL)
 	{
+		/* FIXME! */
+		/* FIXME construct and show error message */
 		totem_embedded_error_and_exit (_("The Totem plugin could not startup."), err != NULL ? err->message : _("No reason."), emb);
+
 		if (err != NULL)
 			g_error_free (err);
 	}
@@ -721,9 +1036,10 @@ totem_embedded_add_children (TotemEmbedd
 	g_signal_connect (G_OBJECT(emb->bvw), "tick",
 			G_CALLBACK (on_tick), emb);
 
-	container = glade_xml_get_widget (emb->xml, "hbox4");
+	container = glade_xml_get_widget (emb->xml, "video_box");
 	gtk_container_add (GTK_CONTAINER (container), GTK_WIDGET (emb->bvw));
 	if (type == BVW_USE_TYPE_VIDEO) {
+		/* FIXME: why can't this wait until the whole window is realised? */
 		gtk_widget_realize (GTK_WIDGET (emb->bvw));
 		gtk_widget_show (GTK_WIDGET (emb->bvw));
 	}
@@ -752,18 +1068,58 @@ totem_embedded_add_children (TotemEmbedd
 	g_signal_connect (G_OBJECT (vbut), "scroll_event",
 			G_CALLBACK (on_volume_scroll_event), emb);
 
-	emb->statusbar = TOTEM_STATUSBAR (glade_xml_get_widget
-			(emb->xml, "emb_statusbar"));
+	emb->statusbar = TOTEM_STATUSBAR (glade_xml_get_widget (emb->xml, "statusbar"));
 
+	/* FIXME: again?? */
 	gtk_widget_realize (emb->window);
-	gtk_widget_set_size_request (emb->window, emb->width, emb->height);
+	gtk_widget_set_size_request (emb->window, width, height);
+
+#if GNOME_ENABLE_DEBUG
+	child = glade_xml_get_widget (emb->xml, "controls");
+	g_signal_connect_after (child, "size-allocate", G_CALLBACK (controls_size_allocate_cb), NULL);
+#endif
 
 	if (emb->controller_hidden != FALSE) {
 		child = glade_xml_get_widget (emb->xml, "controls");
 		gtk_widget_hide (child);
 	}
 
+	if (!emb->show_statusbar) {
+		child = glade_xml_get_widget (emb->xml, "statusbar");
+		gtk_widget_hide (child);
+	}
+
+	/* Try to make controls smaller */
+	{
+        	GtkRcStyle *rcstyle;
+
+		rcstyle = gtk_rc_style_new ();
+		rcstyle->xthickness = rcstyle->ythickness = 0;
+
+		child = glade_xml_get_widget (emb->xml, "pp_button");
+		gtk_widget_modify_style (child, rcstyle);
+		
+		child = glade_xml_get_widget (emb->xml, "time_hscale");
+		gtk_widget_modify_style (child, rcstyle);
+
+		child = glade_xml_get_widget (emb->xml, "volume_button");
+		gtk_widget_modify_style (child, rcstyle);
+
+		gtk_rc_style_unref (rcstyle);
+	}
+
+	totem_embedded_set_state (emb, STATE_STOPPED);
+
+	if (!emb->hidden) {
+		gtk_widget_show (emb->window);
+	}
+
 	/* popup */
+	emb->menuxml = totem_interface_load_with_root ("mozilla-viewer.glade",
+			"menu", _("Menu"), TRUE,
+			GTK_WINDOW (emb->window));
+	g_assert (emb->menuxml);
+
 	child = glade_xml_get_widget (emb->menuxml, "about1");
 	g_signal_connect (G_OBJECT (child), "activate",
 			  G_CALLBACK (on_about1_activate), emb);
@@ -772,64 +1128,160 @@ totem_embedded_add_children (TotemEmbedd
 			G_CALLBACK (on_copy_location1_activate), emb);
 	child = glade_xml_get_widget (emb->menuxml, "preferences1");
 	gtk_widget_hide (child);
+
+	/* Create cursor */
+	/* FIXME: this looks hideous! */
+	if (!emb->hidden) {
+		icon = gtk_widget_render_icon (emb->window,
+					GTK_STOCK_MEDIA_PLAY,
+					GTK_ICON_SIZE_BUTTON,
+					NULL);
+		emb->cursor = gdk_cursor_new_from_pixbuf (gtk_widget_get_display (emb->window),
+							icon,
+							gdk_pixbuf_get_width (icon) / 2,
+							gdk_pixbuf_get_height (icon) / 2);
+		gdk_pixbuf_unref (icon);
+	}
+
+	return TRUE;
 }
 
-static void
-totem_embedded_create_cursor (TotemEmbedded *emb)
+static gboolean
+totem_embedded_construct_placeholder (TotemEmbedded *emb,
+			  GdkNativeWindow xid,
+			  int width,
+			  int height)
 {
-	GtkWidget *label;
-	GdkPixbuf *icon;
+	GtkWidget *window, *label;
+
+	if (xid == 0)
+		return TRUE;
+
+	window = gtk_plug_new (xid);
+	/* FIXME why? */
+	gtk_widget_realize (window);
 
-	label = gtk_label_new ("");
-	icon = gtk_widget_render_icon (label, GTK_STOCK_MEDIA_PLAY,
-			GTK_ICON_SIZE_BUTTON, NULL);
-	gtk_widget_destroy (label);
-	emb->cursor = gdk_cursor_new_from_pixbuf (gdk_display_get_default (),
-			icon,
-			gdk_pixbuf_get_width (icon) / 2,
-			gdk_pixbuf_get_height (icon) / 2);
-	gdk_pixbuf_unref (icon);
+	label = gtk_label_new ("Not yet supported");
+	gtk_container_add (GTK_CONTAINER (window), label);
+
+	gtk_widget_show_all (window);
+
+	return TRUE;
+}
+
+static gboolean
+totem_embedded_set_window (TotemEmbedded *embedded,
+			   const char *controls,
+			   guint window,
+			   int width,
+			   int height,
+			   GError **error)
+{
+	g_print ("Viewer: SetWindow XID %u size %d:%d\n", window, width, height);
+
+	if (embedded->type == TOTEM_PLUGIN_TYPE_COMPLEX) {
+		/* FIXME!!! */
+		if (strcmp (controls, "All") != 0 &&
+		    strcmp (controls, "ImageWindow") != 0) {
+			totem_embedded_construct_placeholder (embedded, (GdkNativeWindow) window, width, height);
+			return TRUE;
+		}
+	} else {
+		if (strcmp (controls, "All") != 0 &&
+		    strcmp (controls, "ImageWindow") != 0) {
+			g_set_error (error,
+				TOTEM_EMBEDDED_ERROR_QUARK,
+				TOTEM_EMBEDDED_SETWINDOW_UNSUPPORTED_CONTROLS,
+				"Unsupported controls '%s'", controls);
+			return FALSE;
+		}
+	}
+
+	if (embedded->window != NULL) {
+		g_warning ("Viewer: Already have a window!");
+
+		g_set_error (error,
+			     TOTEM_EMBEDDED_ERROR_QUARK,
+			     TOTEM_EMBEDDED_SETWINDOW_HAVE_WINDOW,
+			     "Already have a window");
+		return FALSE;
+	}
+
+	if (window == 0) {
+		g_set_error (error,
+			     TOTEM_EMBEDDED_ERROR_QUARK,
+			     TOTEM_EMBEDDED_SETWINDOW_INVALID_XID,
+			     "Invalid XID");
+		return FALSE;
+	}
+
+	embedded->width = width;
+	embedded->height = height;
+
+	totem_embedded_construct (embedded, (GdkNativeWindow) window,
+				  width, height);
+
+	return TRUE;
+}
+
+static gboolean
+totem_embedded_unset_window (TotemEmbedded *embedded,
+			    guint window,
+			    GError **error)
+{
+	g_warning ("UnsetWindow unimplemented");
+	return TRUE;
 }
 
 static void
-entry_added (TotemPlParser *parser, const char *uri, const char *title,
-		                const char *genre, gpointer data)
+entry_added (TotemPlParser *parser,
+	     const char *uri,
+	     const char *title,
+	     const char *genre,
+	     gpointer data)
 {
-	TotemEmbedded *emb = (TotemEmbedded *) data;
+	GList **list = (GList **) data;
 
 	g_print ("added URI '%s' with title '%s' genre '%s'\n", uri,
 			title ? title : "empty", genre);
 
+	//FIXMEchpe
 	//FIXME need new struct to hold that
-	emb->playlist = g_list_prepend (emb->playlist, g_strdup (uri));
-	emb->num_items++;
+	*list = g_list_prepend (*list, g_strdup (uri));
 }
 
 static gboolean
 totem_embedded_push_parser (gpointer data)
 {
-	TotemPlParser *parser = totem_pl_parser_new ();
 	TotemEmbedded *emb = (TotemEmbedded *) data;
+	TotemPlParser *parser;
 	TotemPlParserResult res;
+	GList *list = NULL;
+
+	emb->parser_id = 0;
+	emb->num_items = 0;
+	/* FIXME: delete old playlist! */
+	emb->playlist = emb->current = NULL;
 
 	parser = totem_pl_parser_new ();
-	g_object_set (G_OBJECT (parser), "force", TRUE, NULL);
-	g_object_set (G_OBJECT (parser), "disable-unsafe", TRUE, NULL);
-	g_signal_connect (G_OBJECT (parser), "entry", G_CALLBACK (entry_added), emb);
-	res = totem_pl_parser_parse (parser, emb->filename, FALSE);
+	g_object_set (parser, "force", TRUE,
+			      "disable-unsafe", TRUE,
+			       NULL);
+	g_signal_connect (parser, "entry", G_CALLBACK (entry_added), &list);
+	res = totem_pl_parser_parse (parser, emb->current_uri, FALSE); /* FIXME base_uri? */
 	g_object_unref (parser);
 
 	if (res != TOTEM_PL_PARSER_RESULT_SUCCESS) {
 		//FIXME show a proper error message
 		switch (res) {
 		case TOTEM_PL_PARSER_RESULT_UNHANDLED:
-			g_print ("url '%s' unhandled\n", emb->filename);
+			g_print ("url '%s' unhandled\n", emb->current_uri);
 			break;
 		case TOTEM_PL_PARSER_RESULT_ERROR:
-			g_print ("error handling url '%s'\n", emb->filename);
+			g_print ("error handling url '%s'\n", emb->current_uri);
 			break;
 		case TOTEM_PL_PARSER_RESULT_IGNORED:
-			g_print ("ignored url '%s'\n", emb->filename);
+			g_print ("ignored url '%s'\n", emb->current_uri);
 			break;
 		default:
 			g_assert_not_reached ();
@@ -838,88 +1290,93 @@ totem_embedded_push_parser (gpointer dat
 	}
 
 	/* Check if we have anything in the playlist now */
-	if (emb->playlist == NULL) {
-		g_message ("NO PLAYLIST");
-		totem_embedded_error_and_exit ("Can't parse that",
-				"no files",
-				emb);
-		//FIXME error out
+	if (list == NULL) {
+		g_message ("No playlist or playlist empty");
+		totem_embedded_set_error (emb, _("No playlist or playlist empty") /* FIXME */,
+					  NULL);
 		return FALSE;
 	}
 
-	emb->playlist = g_list_reverse (emb->playlist);
-	g_free (emb->filename);
-	emb->filename = g_strdup (emb->playlist->data);
-	emb->current = emb->playlist;
+	emb->playlist = g_list_reverse (list);
+	emb->num_items = g_list_length (emb->playlist);
 
-	g_main_loop_quit (emb->loop);
+	totem_embedded_open_playlist_item (emb, NULL /* first */);
 
+	/* don't run again */
 	return FALSE;
 }
 
-static void embedded (GtkPlug *plug, TotemEmbedded *emb)
-{
-	emb->embedded_done = TRUE;
-}
-
-static int arg_width = -1;
-static int arg_height = -1;
-static char *arg_url = NULL;
-static char *arg_href = NULL;
-static char *arg_target = NULL;
-static char *arg_mime_type = NULL;
-static char **arg_remaining = NULL;
-static Window xid = 0;
-static gboolean arg_no_controls = FALSE;
-static gboolean arg_statusbar = FALSE;
-static gboolean arg_hidden = FALSE;
-static gboolean arg_is_playlist = FALSE;
-static gboolean arg_repeat = FALSE;
-static gboolean arg_no_autostart = FALSE;
-
-GtkWidget *
+G_MODULE_EXPORT GtkWidget *
 totem_volume_create (void)
 {
 	GtkWidget *widget;
 
 	widget = bacon_volume_button_new (GTK_ICON_SIZE_MENU,
 					  0, 100, -1);
+	gtk_button_set_focus_on_click (GTK_BUTTON (widget), FALSE);
 	gtk_widget_show (widget);
 
 	return widget;
 }
 
-GtkWidget *
+G_MODULE_EXPORT GtkWidget *
 totem_statusbar_create (void)
 {
 	GtkWidget *widget;
 
 	widget = totem_statusbar_new ();
 	totem_statusbar_set_has_resize_grip (TOTEM_STATUSBAR (widget), FALSE);
-	if (arg_statusbar != FALSE)
-		gtk_widget_show (widget);
+	gtk_widget_show (widget);
 
 	return widget;
 }
 
+static char *arg_user_agent = NULL;
+static char *arg_url = NULL;
+static char *arg_href = NULL;
+static char *arg_target = NULL;
+static char *arg_mime_type = NULL;
+static char **arg_remaining = NULL;
+static gboolean arg_no_controls = FALSE;
+static gboolean arg_statusbar = FALSE;
+static gboolean arg_hidden = FALSE;
+static gboolean arg_is_playlist = FALSE;
+static gboolean arg_repeat = FALSE;
+static gboolean arg_no_autostart = FALSE;
+static TotemPluginType arg_plugin_type = TOTEM_PLUGIN_TYPE_LAST;
+
 static gboolean
-parse_xid (const gchar *option_name,
-	   const gchar *value,
-	   gpointer data,
-	   GError **error)
-{
-	xid = (Window) g_ascii_strtoull (value, NULL, 10);
+parse_plugin_type (const gchar *option_name,
+	           const gchar *value,
+	           gpointer data,
+	           GError **error)
+{
+	const char types[TOTEM_PLUGIN_TYPE_LAST][12] = {
+		"basic",
+		"gmp",
+		"complex",
+		"narrowspace",
+		"mully"
+	};
+	TotemPluginType type;
 
-	return TRUE;
+	for (type = 0; type < TOTEM_PLUGIN_TYPE_LAST; ++type) {
+		if (strcmp (value, types[type]) == 0) {
+			arg_plugin_type = type;
+			return TRUE;
+		}
+	}
+
+	g_print ("Unknown plugin type '%s'\n", value);
+	exit (1);
 }
 
 static GOptionEntry option_entries [] =
 {
-	{ TOTEM_OPTION_WIDTH, 0, 0, G_OPTION_ARG_INT, &arg_width, NULL, NULL },
-	{ TOTEM_OPTION_HEIGHT, 0, 0, G_OPTION_ARG_INT, &arg_height, NULL, NULL },
+	{ TOTEM_OPTION_PLUGIN_TYPE, 0, 0, G_OPTION_ARG_CALLBACK, parse_plugin_type, NULL, NULL },
+	{ TOTEM_OPTION_USER_AGENT, 0, 0, G_OPTION_ARG_STRING, &arg_user_agent, NULL, NULL },
 	{ TOTEM_OPTION_URL, 0, 0, G_OPTION_ARG_STRING, &arg_url, NULL, NULL },
 	{ TOTEM_OPTION_HREF, 0, 0, G_OPTION_ARG_STRING, &arg_href, NULL, NULL },
-	{ TOTEM_OPTION_XID, 0, 0, G_OPTION_ARG_CALLBACK, parse_xid, NULL, NULL },
 	{ TOTEM_OPTION_TARGET, 0, 0, G_OPTION_ARG_STRING, &arg_target, NULL, NULL },
 	{ TOTEM_OPTION_MIMETYPE, 0, 0, G_OPTION_ARG_STRING, &arg_mime_type, NULL, NULL },
 	{ TOTEM_OPTION_CONTROLS_HIDDEN, 0, 0, G_OPTION_ARG_NONE, &arg_no_controls, NULL, NULL },
@@ -932,6 +1389,7 @@ static GOptionEntry option_entries [] =
 	{ NULL }
 };
 
+#include "totem-mozilla-interface.h"
 
 int main (int argc, char **argv)
 {
@@ -939,8 +1397,8 @@ int main (int argc, char **argv)
 	DBusGProxy *proxy;
 	DBusGConnection *conn;
 	guint res;
-	gchar *svcname;
 	GError *e = NULL;
+	char svcname[256];
 
 	bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
@@ -949,13 +1407,13 @@ int main (int argc, char **argv)
 #ifdef GNOME_ENABLE_DEBUG
 	{
 		int i;
-		g_print ("Viewer args: ");
+		g_print ("Viewer: PID %d, args: ", getpid ());
 		for (i = 0; i < argc; i++)
 			g_print ("%s ", argv[i]);
 		g_print ("\n");
 	}
 #endif
-	
+
 	if (XInitThreads () == 0)
 	{
 		gtk_init (&argc, &argv);
@@ -963,6 +1421,44 @@ int main (int argc, char **argv)
 	}
 
 	g_thread_init (NULL);
+	dbus_g_thread_init ();
+
+#ifdef GNOME_ENABLE_DEBUG
+{
+	const char *env;
+
+	env = g_getenv ("TOTEM_EMBEDDED_GDB");
+	if (env && g_ascii_strtoull (env, NULL, 10) == 1) {
+		char *gdbargv[6];
+		char pid[32];
+		GError *gdberr = NULL;
+		int gdbargc = 0;
+
+		g_snprintf (pid, sizeof (pid), "%d", getpid ());
+
+		gdbargv[gdbargc++] = "/usr/bin/xterm";
+		gdbargv[gdbargc++] = "-e";
+		gdbargv[gdbargc++] = "gdb";
+		gdbargv[gdbargc++] = argv[0];
+		gdbargv[gdbargc++] = pid;
+		gdbargv[gdbargc++] = NULL;
+
+		if (!g_spawn_async (NULL,
+				    gdbargv,
+				    NULL /* env */,
+				    0,
+				    NULL, NULL,
+				    NULL,
+				    &gdberr)) {
+			g_warning ("Failed to spawn debugger: %s", gdberr->message);
+			g_error_free (gdberr);
+		} else {
+			g_print ("Sleeping....\n");
+			g_usleep (10* 1000 * 1000); /* 10s */
+		}
+	}
+}
+#endif
 
 	if (!gtk_init_with_args (&argc, &argv, NULL, option_entries, GETTEXT_PACKAGE, &e))
 	{
@@ -971,106 +1467,90 @@ int main (int argc, char **argv)
 		exit (1);
 	}
 
-	/* FIXME: are there enough checks? */
-	if (arg_remaining == NULL) {
-		return 1;
+        // FIXME check that ALL necessary params were given!
+	if (arg_plugin_type == TOTEM_PLUGIN_TYPE_LAST) {
+		g_warning ("Plugin type is required\n");
+		exit (1);
+	}
+
+	/* FIXME: check the UA strings of the legacy plugins themselves */
+	/* FIXME: at least hxplayer seems to send different UAs depending on the protocol!? */
+	if (arg_user_agent != NULL) {
+		g_setenv ("GNOME_VFS_HTTP_USER_AGENT", arg_user_agent, TRUE);
+		g_free (arg_user_agent);
+		arg_user_agent = NULL;
 	}
 
 	bacon_video_widget_init_backend (NULL, NULL);
 	gnome_vfs_init ();
 
 	dbus_g_object_type_install_info (TOTEM_TYPE_EMBEDDED,
-		&dbus_glib_totem_embedded_object_info);
-	svcname = g_strdup_printf ("org.totem_%d.MozillaPluginService",
-				   getpid());
-	if (!(conn = dbus_g_bus_get (DBUS_BUS_SESSION, &e)) ||
-	    !(proxy = dbus_g_proxy_new_for_name (conn, "org.freedesktop.DBus",
-						 "/org/freedesktop/DBus",
-						 "org.freedesktop.DBus")) ||
-	    !dbus_g_proxy_call (proxy, "RequestName", &e,
-			G_TYPE_STRING, svcname,
-			G_TYPE_UINT, DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT,
-			G_TYPE_INVALID,
-			G_TYPE_UINT, &res,
-			G_TYPE_INVALID)) {
-		g_print ("Failed to get DBUS connection for %s: %s\n",
-			 svcname, e->message);
-		return 1;
+					 &dbus_glib_totem_embedded_object_info);
+	g_snprintf (svcname, sizeof (svcname),
+		    TOTEM_PLUGIN_VIEWER_NAME_TEMPLATE, getpid());
+
+	if (!(conn = dbus_g_bus_get (DBUS_BUS_SESSION, &e))) {
+		g_warning ("Failed to get DBUS connection: %s", e->message);
+		g_error_free (e);
+		exit (1);
+	}
+
+	proxy = dbus_g_proxy_new_for_name (conn,
+					   "org.freedesktop.DBus",
+					   "/org/freedesktop/DBus",
+					   "org.freedesktop.DBus");
+	g_assert (proxy != NULL);
+
+	if (!dbus_g_proxy_call (proxy, "RequestName", &e,
+	     			G_TYPE_STRING, svcname,
+				G_TYPE_UINT, DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT,
+				G_TYPE_INVALID,
+				G_TYPE_UINT, &res,
+				G_TYPE_INVALID)) {
+		g_warning ("RequestName for '%s'' failed: %s\n",
+			   svcname, e->message);
+		g_error_free (e);
+
+		exit (1);
 	}
-	g_free (svcname);
 
 	emb = g_object_new (TOTEM_TYPE_EMBEDDED, NULL);
 
-	emb->state = STATE_STOPPED;
-	emb->width = arg_width;
-	emb->height = arg_height;
+	emb->state = LAST_STATE;
+	emb->width = -1;
+	emb->height = -1;
 	emb->controller_hidden = arg_no_controls;
-	emb->orig_filename = arg_url;
+	emb->show_statusbar = arg_statusbar;
+	//emb->base_uri = arg_url;
 	emb->href = arg_href;
-	emb->filename = arg_remaining ? arg_remaining[0] : NULL;
+	emb->current_uri = arg_remaining ? arg_remaining[0] : NULL;
 	emb->target = arg_target;
-	if (arg_mime_type != NULL) {
-		emb->mimetype = arg_mime_type;
-	} else {
-		emb->mimetype = g_strdup (gnome_vfs_get_mime_type_for_name (emb->filename));
-	}
+	emb->mimetype = arg_mime_type;
 	emb->hidden = arg_hidden;
 	emb->is_playlist = arg_is_playlist;
 	emb->repeat = arg_repeat;
-	emb->noautostart = arg_no_autostart;
+	emb->autostart = !arg_no_autostart;
+	emb->type = arg_plugin_type;
 
-	dbus_g_connection_register_g_object (conn, "/TotemEmbedded",
-					     G_OBJECT (emb));
+	//FIXME debug
+	emb->controller_hidden = FALSE;
 
-	/* XEMBED or stand-alone */
-	if (xid != 0) {
-		GtkWidget *window;
-
-		/* The miraculous XEMBED protocol */
-		window = gtk_plug_new ((GdkNativeWindow) xid);
-		gtk_signal_connect (GTK_OBJECT(window), "embedded",
-				G_CALLBACK (embedded), NULL);
-		gtk_widget_realize (window);
+	/* FIXME: register this BEFORE requesting the service name? */
+	dbus_g_connection_register_g_object (conn, TOTEM_PLUGIN_VIEWER_DBUS_PATH,
+					     G_OBJECT (emb));
 
-		emb->window = window;
-	} else {
-		/* Stand-alone version */
-		emb->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	/* If we're hidden, construct a hidden window;
+	 * else wait to be plugged in.
+	 */
+	if (emb->hidden) {
+		totem_embedded_construct (emb, 0, -1, -1);
 	}
 
-	/* Hidden or not as well? */
-	totem_embedded_add_children (emb);
-	totem_embedded_create_cursor (emb);
-	if (emb->hidden == FALSE)
-		gtk_widget_show (emb->window);
-
-	/* wait until we're embedded if we're to be, or shown */
-	if (xid != 0) {
-		while (emb->embedded_done == FALSE && gtk_events_pending ())
-			gtk_main_iteration ();
-	} else {
-		while (gtk_events_pending ())
-			gtk_main_iteration ();
-	}
+	gtk_main ();
 
-	/* Do we have a playlist we need to setup ourselves? */
-	if (emb->is_playlist != FALSE) {
-		g_idle_add (totem_embedded_push_parser, emb);
-		emb->loop = g_main_loop_new (NULL, FALSE);
-		g_main_loop_run (emb->loop);
-		g_main_loop_unref (emb->loop);
-	} else {
-		emb->playlist = g_list_prepend (emb->playlist,
-				g_strdup (emb->filename));
-		emb->num_items++;
-	}
+	g_object_unref (emb);
 
-	if (totem_embedded_open (emb) != FALSE
-			&& emb->noautostart == FALSE) {
-		totem_embedded_play (emb, NULL);
-	}
-
-	gtk_main ();
+	g_message ("Viewer [PID %d]: exiting\n", getpid ());
 
 	return 0;
 }
Index: browser-plugin/Makefile.am
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/Makefile.am,v
retrieving revision 1.4
diff -p -u -B -r1.4 Makefile.am
--- browser-plugin/Makefile.am	8 Oct 2006 15:32:36 -0000	1.4
+++ browser-plugin/Makefile.am	12 Nov 2006 20:08:25 -0000
@@ -13,6 +13,73 @@ gecko_include_subdirs = \
 	dom		\
 	necko
 
+# Totem Plugin Viewer
+
+# libexec_PROGRAMS = totem-plugin-viewer
+# 
+# totem_plugin_viewer_SOURCES = \
+# 	totem-mozilla-interface.h	\
+# 	totem-plugin-viewer-marshal.c	\
+# 	totem-plugin-viewer-marshal.h	\
+# 	totem-plugin-viewer.c		\
+# 	totem-mozilla-options.h
+# 
+# totem_plugin_viewer_CPPFLAGS = \
+# 	-D_REENTRANT					\
+# 	-DDBUS_API_SUBJECT_TO_CHANGE			\
+# 	-I$(top_srcdir)					\
+# 	-I$(srcdir)/plparse				\
+# 	-I$(srcdir)/backend				\
+# 	-I$(top_builddir)/src/plparse			\
+# 	-I$(top_builddir)/src/backend			\
+# 	-DGNOMELOCALEDIR="\"$(datadir)/locale\""	\
+# 	-DGCONF_PREFIX="\"/apps/totem\""		\
+# 	-DDATADIR="\"$(datadir)\""			\
+# 	-DLIBEXECDIR="\"$(libexecdir)\""		\
+# 	-DBINDIR="\"$(bindir)\""			\
+# 	-DSHAREDIR="\"$(pkgdatadir)\""			\
+# 	$(AM_CPPFLAGS)
+# 
+# totem_plugin_viewer_CFLAGS = \
+# 	$(EXTRA_GNOME_CFLAGS)	\
+# 	$(WARN_CFLAGS)		\
+# 	$(DBUS_CFLAGS)		\
+# 	$(NVTV_CFLAGS)		\
+# 	$(AM_CFLAGS)
+# 
+# totem_plugin_viewer_LDADD = \
+# 	$(top_builddir)/src/backend/libbaconvideowidget.la	\
+# 	$(top_builddir)/src/libbaconpropertiespage.la		\
+# 	$(top_builddir)/src/libtotem_player.la			\
+# 	$(top_builddir)/src/plparse/libtotem-plparser.la	\
+# 	$(EXTRA_GNOME_LIBS)	\
+# 	$(XVIDMODE_LIBS) 	\
+# 	$(DBUS_LIBS)		\
+# 	$(NVTV_LIBS) 		\
+# 	$(XTEST_LIBS)		\
+# 	$(X_LIBS)
+# 
+# BUILT_SOURCES = \
+# 	totem-mozilla-interface.h
+# 
+# totem-mozilla-interface.h: totem-mozilla-interface.xml
+# 	$(DBUS_BIND) --prefix=totem_embedded --mode=glib-server $< > $@
+# 
+# totem-plugin-viewer-marshal.c: totem-plugin-viewer-marshal.list
+# 	$(GLIB_GENMARSHAL) --prefix=totem_embedded_marshal $< --header --body > $@
+# 
+# totem-plugin-viewer-marshal.h: totem-plugin-viewer-marshal.list
+# 	$(GLIB_GENMARSHAL) --prefix=totem_embedded_marshal $< --header > $@
+# 
+# # FIXME until we can depend on a fixed dbus-binding-tool, fixup the binding with sed
+# # see https://bugs.freedesktop.org/show_bug.cgi?id=6358
+# totem-mozilla-client.h: totem-mozilla-interface.xml
+# 	$(DBUS_BIND) --prefix=totem_embedded --mode=glib-client $< | \
+# 	sed -e 's/DBusGAsyncData \*data = user_data/DBusGAsyncData *data = (DBusGAsyncData *) user_data/g' > $@
+# 
+# EXTRA_DIST += \
+# 	totem-mozilla-interface.xml
+
 # Totem Basic Plugin
 
 if ENABLE_BASIC_PLUGIN
@@ -65,7 +132,8 @@ libtotem_basic_plugin_la_CXXFLAGS = \
 libtotem_basic_plugin_la_LIBADD = \
 	$(top_builddir)/src/plparse/libtotem-plparser-mini.la	\
 	$(DBUS_LIBS)	\
- 	$(MOZILLA_LIBS)
+ 	$(MOZILLA_LIBS)	\
+	$(LIBXPCOMGLUE_S)
 
 libtotem_basic_plugin_la_LDFLAGS = \
 	-avoid-version	\
@@ -127,7 +195,8 @@ libtotem_gmp_plugin_la_CXXFLAGS = \
 libtotem_gmp_plugin_la_LIBADD = \
 	$(top_builddir)/src/plparse/libtotem-plparser-mini.la	\
 	$(DBUS_LIBS)	\
- 	$(MOZILLA_LIBS)
+ 	$(MOZILLA_LIBS)	\
+	$(LIBXPCOMGLUE_S)
 
 libtotem_gmp_plugin_la_LDFLAGS = \
 	-avoid-version	\
@@ -189,7 +258,8 @@ libtotem_complex_plugin_la_CXXFLAGS = \
 libtotem_complex_plugin_la_LIBADD = \
 	$(top_builddir)/src/plparse/libtotem-plparser-mini.la	\
 	$(DBUS_LIBS)	\
- 	$(MOZILLA_LIBS)
+ 	$(MOZILLA_LIBS)	\
+	$(LIBXPCOMGLUE_S)
 
 libtotem_complex_plugin_la_LDFLAGS = \
 	-avoid-version	\
@@ -251,7 +321,8 @@ libtotem_narrowspace_plugin_la_CXXFLAGS 
 libtotem_narrowspace_plugin_la_LIBADD = \
 	$(top_builddir)/src/plparse/libtotem-plparser-mini.la	\
 	$(DBUS_LIBS)	\
- 	$(MOZILLA_LIBS)
+ 	$(MOZILLA_LIBS)	\
+	$(LIBXPCOMGLUE_S)
 
 libtotem_narrowspace_plugin_la_LDFLAGS = \
 	-avoid-version	\
@@ -313,7 +384,8 @@ libtotem_mully_plugin_la_CXXFLAGS = \
 libtotem_mully_plugin_la_LIBADD = \
 	$(top_builddir)/src/plparse/libtotem-plparser-mini.la	\
 	$(DBUS_LIBS)	\
- 	$(MOZILLA_LIBS)
+ 	$(MOZILLA_LIBS)	\
+	$(LIBXPCOMGLUE_S)
 
 libtotem_mully_plugin_la_LDFLAGS = \
 	-avoid-version	\
Index: browser-plugin/README
===================================================================
RCS file: browser-plugin/README
diff -N browser-plugin/README
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ browser-plugin/README	12 Nov 2006 20:08:25 -0000
@@ -0,0 +1,17 @@
+Notes on the plugin's internas
+==============================
+
+
+Special topics: Complex plugin
+==============================
+
+The complex plugin supports multiple plugins working together, see
+[http://service.real.com/help/library/guides/realone/ProductionGuide/HTML/realpgd.htm?page=htmfiles/embed.htm].
+
+To implement this, we maintain a list of totem complex plugins.
+The plugins are partitioned into classes by their 'console' name; and a
+random plugin (well, the first one instantiated) is chosen as representant.
+This one owns the plugin viewer and all viewer interaction of the linked plugins
+are done trough it. When this plugin is destroyed, we search for a new
+representant, transfer the viewer ownership to it and update the representant
+pointer in all of the classes' plugins.
Index: browser-plugin/TODO
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/TODO,v
retrieving revision 1.2
diff -p -u -B -r1.2 TODO
--- browser-plugin/TODO	11 Aug 2006 13:47:57 -0000	1.2
+++ browser-plugin/TODO	12 Nov 2006 20:08:25 -0000
@@ -2,6 +2,12 @@ Plugin:
 - get info about play state, URL etc back from viewer to the plugin
 - support all <embed> and <object> attributes that the legacy plugins support
 - there still seem to be some unnecessary symbols exported in the plugin .so's
+- use peer-2-peer DBUS instead of session bus
+- (maybe) use libdbus directly instead of dbus-glib
+- unify playlist handling into the viewer (is it possible to write a gstreamer element
+  that handles the playlist internally?)
+- make StreamAsFile use async calls; need to keep a link to the file around!!
+- put back narrowspace URL extension parsing
 
 GMP plugin:
 - write IDL files (where [_not_ MSDN !] to find documentation?)
Index: browser-plugin/totem-commands.h
===================================================================
RCS file: browser-plugin/totem-commands.h
diff -N browser-plugin/totem-commands.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ browser-plugin/totem-commands.h	12 Nov 2006 20:08:26 -0000
@@ -0,0 +1,28 @@
+/* Totem Viewer commands
+ *
+ * Copyright © 2006 Christian Persch
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TOTEM_COMMANDS_H__
+#define __TOTEM_COMMANDS_H__
+
+#define TOTEM_COMMAND_PLAY		"Play"
+#define TOTEM_COMMAND_PAUSE		"Pause"
+#define TOTEM_COMMAND_STOP		"Stop"
+
+#endif /* !__TOTEM_COMMANDS_H__ */
Index: browser-plugin/totemBasicPlugin.cpp
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemBasicPlugin.cpp,v
retrieving revision 1.7
diff -p -u -B -r1.7 totemBasicPlugin.cpp
--- browser-plugin/totemBasicPlugin.cpp	8 Oct 2006 15:36:24 -0000	1.7
+++ browser-plugin/totemBasicPlugin.cpp	12 Nov 2006 20:08:26 -0000
@@ -183,7 +183,7 @@ totemScriptablePlugin::Play ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Play ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_PLAY);
 }
 
 NS_IMETHODIMP
@@ -191,7 +191,7 @@ totemScriptablePlugin::Rewind ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Pause ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_PAUSE);
 }
 
 NS_IMETHODIMP
@@ -199,5 +199,5 @@ totemScriptablePlugin::Stop ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Stop ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_STOP);
 }
Index: browser-plugin/totemBasicPlugin.h
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemBasicPlugin.h,v
retrieving revision 1.1
diff -p -u -B -r1.1 totemBasicPlugin.h
--- browser-plugin/totemBasicPlugin.h	11 Aug 2006 13:25:44 -0000	1.1
+++ browser-plugin/totemBasicPlugin.h	12 Nov 2006 20:08:26 -0000
@@ -40,7 +40,7 @@ class totemScriptablePlugin : public tot
     totemScriptablePlugin (totemPlugin *aPlugin);
 
     PRBool IsValid () { return mPlugin != nsnull; }
-    void UnsetPlugin () { mPlugin = nsnull; }
+    void SetPlugin (totemPlugin *aPlugin) { mPlugin = aPlugin; }
 
     static char *PluginDescription ();
     static void PluginMimeTypes (const totemPluginMimeEntry **, PRUint32 *);
Index: browser-plugin/totemComplexPlugin.cpp
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemComplexPlugin.cpp,v
retrieving revision 1.8
diff -p -u -B -r1.8 totemComplexPlugin.cpp
--- browser-plugin/totemComplexPlugin.cpp	17 Oct 2006 22:55:44 -0000	1.8
+++ browser-plugin/totemComplexPlugin.cpp	12 Nov 2006 20:08:26 -0000
@@ -181,7 +181,7 @@ totemScriptablePlugin::DoPause (PRBool *
 {
   NS_ENSURE_STATE (IsValid ());
 
-  nsresult rv = mPlugin->Pause ();
+  nsresult rv = mPlugin->DoCommand (TOTEM_COMMAND_PAUSE);
 
   *_retval = PR_TRUE;
   return rv;
@@ -192,7 +192,7 @@ totemScriptablePlugin::DoPlay (PRBool *_
 {
   NS_ENSURE_STATE (IsValid ());
 
-  nsresult rv = mPlugin->Play ();
+  nsresult rv = mPlugin->DoCommand (TOTEM_COMMAND_PLAY);
 
   *_retval = PR_TRUE;
   return rv;
@@ -203,7 +203,7 @@ totemScriptablePlugin::DoStop (PRBool *_
 {
   NS_ENSURE_STATE (IsValid ());
 
-  nsresult rv = mPlugin->Stop ();
+  nsresult rv = mPlugin->DoCommand (TOTEM_COMMAND_STOP);
 
   *_retval = PR_TRUE;
   return rv;
Index: browser-plugin/totemComplexPlugin.h
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemComplexPlugin.h,v
retrieving revision 1.1
diff -p -u -B -r1.1 totemComplexPlugin.h
--- browser-plugin/totemComplexPlugin.h	11 Aug 2006 13:25:44 -0000	1.1
+++ browser-plugin/totemComplexPlugin.h	12 Nov 2006 20:08:26 -0000
@@ -40,7 +40,7 @@ class totemScriptablePlugin : public tot
     totemScriptablePlugin (totemPlugin *aPlugin);
 
     PRBool IsValid () { return mPlugin != nsnull; }
-    void UnsetPlugin () { mPlugin = nsnull; }
+    void SetPlugin (totemPlugin *aPlugin) { mPlugin = aPlugin; }
 
     static char *PluginDescription ();
     static void PluginMimeTypes (const totemPluginMimeEntry **, PRUint32 *);
Index: browser-plugin/totemGMPPlugin.cpp
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemGMPPlugin.cpp,v
retrieving revision 1.14
diff -p -u -B -r1.14 totemGMPPlugin.cpp
--- browser-plugin/totemGMPPlugin.cpp	24 Oct 2006 22:58:50 -0000	1.14
+++ browser-plugin/totemGMPPlugin.cpp	12 Nov 2006 20:08:26 -0000
@@ -208,7 +208,7 @@ totemScriptablePlugin::Pause ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Pause ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_PAUSE);
 }
 
 NS_IMETHODIMP
@@ -216,7 +216,7 @@ totemScriptablePlugin::Play ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Play ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_PLAY);
 }
 
 NS_IMETHODIMP
@@ -224,5 +224,5 @@ totemScriptablePlugin::Stop ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Stop ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_STOP);
 }
Index: browser-plugin/totemGMPPlugin.h
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemGMPPlugin.h,v
retrieving revision 1.1
diff -p -u -B -r1.1 totemGMPPlugin.h
--- browser-plugin/totemGMPPlugin.h	11 Aug 2006 13:25:44 -0000	1.1
+++ browser-plugin/totemGMPPlugin.h	12 Nov 2006 20:08:26 -0000
@@ -44,7 +44,7 @@ class totemScriptablePlugin : public tot
     totemScriptablePlugin (totemPlugin *aPlugin);
 
     PRBool IsValid () { return mPlugin != nsnull; }
-    void UnsetPlugin () { mPlugin = nsnull; }
+    void SetPlugin (totemPlugin *aPlugin) { mPlugin = aPlugin; }
 
     static char *PluginDescription ();
     static void PluginMimeTypes (const totemPluginMimeEntry **, PRUint32 *);
Index: browser-plugin/totemMullYPlugin.cpp
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemMullYPlugin.cpp,v
retrieving revision 1.4
diff -p -u -B -r1.4 totemMullYPlugin.cpp
--- browser-plugin/totemMullYPlugin.cpp	21 Oct 2006 20:24:56 -0000	1.4
+++ browser-plugin/totemMullYPlugin.cpp	12 Nov 2006 20:08:26 -0000
@@ -179,7 +179,7 @@ totemScriptablePlugin::Rewind ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Pause ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_PAUSE);
 }
 
 NS_IMETHODIMP
@@ -187,7 +187,7 @@ totemScriptablePlugin::Play ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Play ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_PLAY);
 }
 
 NS_IMETHODIMP
@@ -195,5 +195,5 @@ totemScriptablePlugin::Stop ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Stop ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_STOP);
 }
Index: browser-plugin/totemMullYPlugin.h
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemMullYPlugin.h,v
retrieving revision 1.1
diff -p -u -B -r1.1 totemMullYPlugin.h
--- browser-plugin/totemMullYPlugin.h	7 Sep 2006 22:11:44 -0000	1.1
+++ browser-plugin/totemMullYPlugin.h	12 Nov 2006 20:08:26 -0000
@@ -41,7 +41,7 @@ class totemScriptablePlugin : public tot
     totemScriptablePlugin (totemPlugin *aPlugin);
 
     PRBool IsValid () { return mPlugin != nsnull; }
-    void UnsetPlugin () { mPlugin = nsnull; }
+    void SetPlugin (totemPlugin *aPlugin) { mPlugin = aPlugin; }
 
     static char *PluginDescription ();
     static void PluginMimeTypes (const totemPluginMimeEntry **, PRUint32 *);
Index: browser-plugin/totemNarrowSpacePlugin.cpp
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemNarrowSpacePlugin.cpp,v
retrieving revision 1.7
diff -p -u -B -r1.7 totemNarrowSpacePlugin.cpp
--- browser-plugin/totemNarrowSpacePlugin.cpp	8 Oct 2006 15:13:12 -0000	1.7
+++ browser-plugin/totemNarrowSpacePlugin.cpp	12 Nov 2006 20:08:26 -0000
@@ -181,7 +181,7 @@ totemScriptablePlugin::Play ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Play ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_PLAY);
 }
 
 NS_IMETHODIMP
@@ -189,7 +189,7 @@ totemScriptablePlugin::Rewind ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Pause ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_PAUSE);
 }
 
 NS_IMETHODIMP
@@ -197,5 +197,5 @@ totemScriptablePlugin::Stop ()
 {
   NS_ENSURE_STATE (IsValid ());
 
-  return mPlugin->Stop ();
+  return mPlugin->DoCommand (TOTEM_COMMAND_STOP);
 }
Index: browser-plugin/totemNarrowSpacePlugin.h
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemNarrowSpacePlugin.h,v
retrieving revision 1.1
diff -p -u -B -r1.1 totemNarrowSpacePlugin.h
--- browser-plugin/totemNarrowSpacePlugin.h	11 Aug 2006 13:25:44 -0000	1.1
+++ browser-plugin/totemNarrowSpacePlugin.h	12 Nov 2006 20:08:26 -0000
@@ -40,7 +40,7 @@ class totemScriptablePlugin : public tot
     totemScriptablePlugin (totemPlugin *aPlugin);
 
     PRBool IsValid () { return mPlugin != nsnull; }
-    void UnsetPlugin () { mPlugin = nsnull; }
+    void SetPlugin (totemPlugin *aPlugin) { mPlugin = aPlugin; }
 
     static char *PluginDescription ();
     static void PluginMimeTypes (const totemPluginMimeEntry **, PRUint32 *);
Index: browser-plugin/totemPlugin.cpp
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemPlugin.cpp,v
retrieving revision 1.42
diff -p -u -B -r1.42 totemPlugin.cpp
--- browser-plugin/totemPlugin.cpp	1 Nov 2006 11:43:31 -0000	1.42
+++ browser-plugin/totemPlugin.cpp	12 Nov 2006 20:08:27 -0000
@@ -2,7 +2,7 @@
  * 
  * Copyright (C) 2004-2006 Bastien Nocera <hadess@hadess.net>
  * Copyright (C) 2002 David A. Schleef <ds@schleef.org>
- * Copyright (C) 2006 Christian Persch
+ * Copyright © 2006 Christian Persch
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -49,10 +49,26 @@
 #include <nsIInterfaceRequestorUtils.h>
 #include <nsIWebNavigation.h>
 
+#include <nsIServiceManager.h>
+#include <nsIDOMDocument.h>
+#include <nsIDOMElement.h>
+#include <nsIDOM3Node.h>
+#include <nsIIOService.h>
+#include <nsIURI.h>
+#include <nsITimer.h>
+#include <nsIComponentManager.h>
+#include <nsIServiceManager.h>
+
+/* for NS_IOSERVICE_CONTRACTID */
+#include <nsNetCID.h>
+
 #define GNOME_ENABLE_DEBUG 1
 /* define GNOME_ENABLE_DEBUG for more debug spew */
 #include "debug.h"
 
+// really noisy debug
+#define DD(args...)
+
 #include "totemPluginGlue.h"
 #include "totemPlugin.h"
 
@@ -61,102 +77,394 @@
 /* How much data bytes to request */
 #define PLUGIN_STREAM_CHUNK_SIZE (8 * 1024)
 
-extern NPNetscapeFuncs sMozillaFuncs;
+NPNetscapeFuncs totemPlugin::sNPN;
+
+#ifdef TOTEM_COMPLEX_PLUGIN
+nsTArray<totemPlugin*> *totemPlugin::sPlugins;
+
+/* Keep the same order as totemPlugin::Control enum! */
+static const char *kControl[] = {
+	"All",
+	"ControlPanel",
+	"FFCtrl",
+	"HomeCtrl",
+	"ImageWindow",
+	"InfoPanel",
+	"InfoVolumePanel",
+	"MuteCtrl",
+	"MuteVolume",
+	"PauseButton",
+	"PlayButton",
+	"PlayOnlyButton",
+	"PositionField",
+	"PositionSlider",
+	"RWCtrl",
+	"StatusBar",
+	"StatusField",
+	"StopButton",
+	"TACCtrl",
+	"VolumeSlider",
+};
+#endif /* TOTEM_COMPLEX_PLUGIN */
 
 void*
 totemPlugin::operator new (size_t aSize) CPP_THROW_NEW
 {
-  void *object = ::operator new (aSize);
-  if (object) {
-    memset (object, 0, aSize);
-  }
+	void *object = ::operator new (aSize);
+	if (object) {
+		memset (object, 0, aSize);
+	}
 
-  return object;
+	return object;
 }
 
 totemPlugin::totemPlugin (NPP aInstance)
-: mInstance(aInstance),
-  mWidth(-1),
-  mHeight(-1),
-  mSendFD(-1)
+:	mInstance (aInstance),
+	mWidth (-1),
+	mHeight (-1),
+	mViewerFD (-1),
+#ifdef TOTEM_NARROWSPACE_PLUGIN
+	mAutostart (PR_FALSE),
+#else
+	mAutostart (PR_TRUE),
+#endif /* TOTEM_NARROWSPACE_PLUGIN */
+	mNeedViewer (PR_TRUE)
 {
-  D ("totemPlugin ctor [%p]", (void*) this);
+	D ("totemPlugin ctor [%p]", (void*) this);
+
+#ifdef TOTEM_COMPLEX_PLUGIN
+	/* Add |this| to the global plugins list */
+	NS_ASSERTION (sPlugins->IndexOf (this) == NoIndex, "WTF?");
+	if (!sPlugins->AppendElement (this)) {
+		D ("Couldn't maintain plugin list!");
+	}
+#endif /* TOTEM_COMPLEX_PLUGIN */
 }
 
 totemPlugin::~totemPlugin ()
 {
-  if (mScriptable) {
-    mScriptable->UnsetPlugin ();
-    NS_RELEASE (mScriptable);
-  }
-
-  if (mSendFD >= 0) {
-    close (mSendFD);
-    mSendFD = -1;
-  }
-
-  if (mPlayerPID) {
-    kill (mPlayerPID, SIGKILL);
-    g_spawn_close_pid (mPlayerPID);
-    mPlayerPID = 0;
-  }
-
-  g_free (mLocal);
-  g_free (mTarget);
-  g_free (mSrc);
-  g_free (mHref);
+#ifdef TOTEM_COMPLEX_PLUGIN
+	/* Remove us from the plugins list */
+	NS_ASSERTION (sPlugins->IndexOf (this) != NoIndex, "WTF?");
+	sPlugins->RemoveElement (this);
+
+	TransferConsole ();
+#endif /* TOTEM_COMPLEX_PLUGIN */
+
+	if (mScriptable) {
+		mScriptable->SetPlugin (nsnull);
+		NS_RELEASE (mScriptable);
+		mScriptable = nsnull;
+	}
+
+	if (mBusProxy) {
+		dbus_g_proxy_disconnect_signal (mBusProxy,
+						"NameOwnerChanged",
+						G_CALLBACK (NameOwnerChangedCallback),
+						NS_REINTERPRET_CAST (void*, this));
+		g_object_unref (mBusProxy);
+		mBusProxy = NULL;
+	}
+
+	ViewerCleanup ();
+
+	if (mTimer) {
+		mTimer->Cancel ();
+		NS_RELEASE (mTimer);
+		mTimer = nsnull;
+	}
+
+	NS_IF_RELEASE (mServiceManager);
+	NS_IF_RELEASE (mIOService);
+	NS_IF_RELEASE (mPluginDOMElement);
+	NS_IF_RELEASE (mBaseURI);
+	NS_IF_RELEASE (mRequestBaseURI);
+	NS_IF_RELEASE (mRequestURI);
+	NS_IF_RELEASE (mSrcURI);
 
-  if (mProxy)
-    g_object_unref (mProxy);
+#ifdef TOTEM_GMP_PLUGIN
+	NS_IF_RELEASE (mURLURI);
+#endif
+
+#ifdef TOTEM_NARROWSPACE_PLUGIN
+	NS_IF_RELEASE (mHrefURI);
+	NS_IF_RELEASE (mQtsrcURI);
+#endif
+
+#ifdef TOTEM_COMPLEX_PLUGIN
+	NS_IF_RELEASE (mPluginOwnerDocument);
+#endif /* TOTEM_COMPLEX_PLUGIN */
 
-  D ("totemPlugin dtor [%p]", (void*) this);
+	D ("totemPlugin dtor [%p]", (void*) this);
 }
 
 /* public functions */
 
 nsresult
-totemPlugin::Play ()
+totemPlugin::DoCommand (const char *aCommand)
 {
-	D ("play");
+	D ("DoCommand '%s'", aCommand);
 
-	if (!mGotSvc)
+	/* FIXME: queue the action instead */
+	if (!mViewerReady)
 		return NS_OK;
 
-	NS_ASSERTION (mProxy, "No DBUS proxy!");
-	dbus_g_proxy_call_no_reply(mProxy, "Play",
-				   G_TYPE_INVALID, G_TYPE_INVALID);
+	NS_ASSERTION (mViewerProxy, "No viewer proxy");
+	dbus_g_proxy_call_no_reply (mViewerProxy,
+				    "DoCommand",
+				    G_TYPE_STRING, aCommand,
+				    G_TYPE_INVALID,
+				    G_TYPE_INVALID);
 
 	return NS_OK;
 }
 
-nsresult
-totemPlugin::Stop ()
+void
+totemPlugin::SetRequestURL (const nsACString &aURL,
+                            PRBool aIsSrc)
 {
-	D ("stop");
+#if 0
+	/* FIXME: if we're just between OpenStream/OpenURI and reply, we need to cancel the mViewerPendingCall ? */
+	NS_ASSERTION (!mViewerPendingCall, "Have a pending call");
 
-	if (!mGotSvc)
-		return NS_OK;
+	if (mRequestURI) {
+		NS_RELEASE (mRequestURI);
+		mRequestURI = nsnull;
+	}
 
-	NS_ASSERTION (mProxy, "No DBUS proxy!");
-	dbus_g_proxy_call_no_reply (mProxy, "Stop",
-				    G_TYPE_INVALID, G_TYPE_INVALID);
+	mRequestURL = aURL;
+        mRequestIsSrc = aIsSrc;
+	D ("SetRequestURL '%s'", mRequestURL.get());
 
-	return NS_OK;
+	if (mRequestURL.IsEmpty ())
+		return;
+
+	nsresult rv = mIOService->NewURI (mRequestURL, nsnull,
+					  mBaseURI, &mRequestURI);
+	if (NS_SUCCEEDED (rv)) {
+		mRequestURI->GetSpec (mRequestURL);
+
+		D ("Resolved request URI '%s' is-supported %d",
+		   mRequestURL.get(), mRequestURISchemeSupported);
+	} else {
+		mRequestURI = nsnull;
+
+		D ("Failed to resolve request URI (rv=%x)", rv);
+	}
+#endif
 }
 
-nsresult
-totemPlugin::Pause ()
+void
+totemPlugin::ViewerSetup ()
 {
-	D ("pause");
+	/* already set up */
+	if (mViewerSetUp)
+		return;
 
-	if (!mGotSvc)
-		return NS_OK;
+	mViewerSetUp = PR_TRUE;
 
-	NS_ASSERTION (mProxy, "No DBUS proxy!");
-	dbus_g_proxy_call_no_reply (mProxy, "Pause",
-				    G_TYPE_INVALID, G_TYPE_INVALID);
+	D ("ViewerSetup");
 
-	return NS_OK;
+	/* Cancel timeout */
+	nsresult rv = mTimer->Cancel ();
+	if (NS_FAILED (rv)) {
+		D ("Failed to cancel timer");
+	}
+
+	mViewerProxy = dbus_g_proxy_new_for_name (mBusConnection,
+						  mViewerServiceName.get (),
+						  TOTEM_PLUGIN_VIEWER_DBUS_PATH,
+						  TOTEM_PLUGIN_VIEWER_INTERFACE_NAME);
+
+	dbus_g_proxy_add_signal (mViewerProxy, "ButtonPress",
+				 G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (mViewerProxy,
+				     "ButtonPress",
+				     G_CALLBACK (ButtonPressCallback),
+				     NS_REINTERPRET_CAST (void*, this),
+				     NULL);
+
+	dbus_g_proxy_add_signal (mViewerProxy,
+				 "StopStream",
+				 G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (mViewerProxy,
+				     "StopStream",
+				     G_CALLBACK (StopStreamCallback),
+				     NS_REINTERPRET_CAST (void*, this),
+				     NULL);
+
+	if (mHidden) {
+		ViewerReady ();
+	} else {
+		ViewerSetWindow ();
+	}
+
+#ifdef TOTEM_COMPLEX_PLUGIN
+	/* Notify all consoles */
+	PRUint32 count = sPlugins->Length ();
+	for (PRUint32 i = 0; i < count; ++i) {
+		totemPlugin *plugin = sPlugins->ElementAt (i);
+
+		if (plugin->mConsoleClassRepresentant == this)
+			plugin->UnownedViewerSetup ();
+	}
+#endif /* TOTEM_COMPLEX_PLUGIN */
+}
+
+void
+totemPlugin::ViewerSetWindow ()
+{
+	if (mWindowSet || mWindow == 0)
+		return;
+
+	if (!mViewerProxy) {
+		D ("No viewer proxy yet, deferring SetWindow");
+		return;
+	}
+
+	/* FIXME this shouldn't happen here */
+	if (mHidden) {
+		mWindowSet = PR_TRUE;
+		ViewerReady ();
+		return;
+	}
+
+	NS_ASSERTION (mViewerPendingCall == NULL, "Have a pending call");
+
+	D ("Calling SetWindow");
+	mViewerPendingCall = 
+		dbus_g_proxy_begin_call (mViewerProxy,
+					 "SetWindow",
+					 ViewerSetWindowCallback,
+					 NS_REINTERPRET_CAST (void*, this),
+					 NULL,
+#ifdef TOTEM_COMPLEX_PLUGIN
+					 G_TYPE_STRING, mControls.get (),
+#else
+					 G_TYPE_STRING, "All",
+#endif
+					 G_TYPE_UINT, (guint) mWindow,
+					 G_TYPE_INT, (gint) mWidth,
+					 G_TYPE_INT, (gint) mHeight,
+					 G_TYPE_INVALID);
+		
+	mWindowSet = PR_TRUE;
+}
+
+void
+totemPlugin::ViewerReady ()
+{
+	D ("ViewerReady");
+
+	NS_ASSERTION (!mViewerReady, "Viewer already ready");
+
+	mViewerReady = PR_TRUE;
+
+	if (mAutostart) {
+		RequestStream ();
+	} else {
+		mWaitingForButtonPress = PR_TRUE;
+	}
+}
+
+void
+totemPlugin::ClearRequest ()
+{
+	if (mRequestBaseURI) {
+		NS_RELEASE (mRequestBaseURI);
+		mRequestBaseURI = nsnull;
+	}
+	if (mRequestURI) {
+		NS_RELEASE (mRequestURI);
+		mRequestURI = nsnull;
+	}
+}
+
+void
+totemPlugin::RequestStream ()
+{
+	NS_ASSERTION (mViewerReady, "Viewer not ready");
+
+	if (mStream) {
+		D ("Unexpectedly have a stream!");
+		/* FIXME cancel existing stream, schedule new timer to try again */
+		return;
+	}
+
+	ClearRequest ();
+
+	/* Now work out which URL to request */
+	nsIURI *baseURI = nsnull;
+	nsIURI *requestURI = nsnull;
+
+#ifdef TOTEM_GMP_PLUGIN
+	/* Prefer filename over src */
+	if (mURLURI) {
+		requestURI = mURLURI;
+		baseURI = mSrcURI; /* FIXME: that correct? */
+	}
+#endif
+#ifdef TOTEM_NARROWSPACE_PLUGIN
+	/* Prefer qtsrc over src */
+	if (mQtsrcURI) {
+		requestURI = mQtsrcURI;
+		baseURI = mSrcURI;
+	}
+#if 0
+	if (href && !requestURL) {
+	 	/* FIXME this looks wrong? any real-world testcase sites around? */
+		requestURL = href;
+	}
+#endif
+#endif
+
+	/* Fallback */
+	if (!requestURI)
+		requestURI = mSrcURI;
+
+	if (!baseURI)
+		baseURI = mBaseURI;
+
+	/* Nothing to do */
+	if (!requestURI)
+		return;
+
+	NS_ADDREF (mRequestBaseURI = baseURI);
+	NS_ADDREF (mRequestURI = requestURI);
+
+	/* FIXME use the right base! */
+	nsCString baseSpec, spec;
+	baseURI->GetSpec (baseSpec);
+	requestURI->GetSpec (spec);
+
+	/* Shouldn't happen, but who knows */
+	if (spec.IsEmpty ())
+		return;
+
+	/* If the URL is supported  we call OpenStream, and otherwise OpenURI. */
+	if (IsSchemeSupported (requestURI)) {
+		mViewerPendingCall =
+			dbus_g_proxy_begin_call (mViewerProxy,
+						 "OpenStream",
+						 ViewerOpenStreamCallback,
+						 NS_REINTERPRET_CAST (void*, this),
+						 NULL,
+						 G_TYPE_STRING, spec.get (),
+						 G_TYPE_STRING, baseSpec.get (),
+						 G_TYPE_INVALID);
+	} else {
+		mViewerPendingCall =
+			dbus_g_proxy_begin_call (mViewerProxy,
+						 "OpenURI",
+						 ViewerOpenURICallback,
+						 NS_REINTERPRET_CAST (void*, this),
+						 NULL,
+						 G_TYPE_STRING, spec.get (),
+						 G_TYPE_STRING, baseSpec.get (),
+						 G_TYPE_INVALID);
+	}
+
+	/* FIXME: start playing in the callbacks ! */
 }
 
 void
@@ -165,49 +473,283 @@ totemPlugin::UnsetStream ()
 	if (!mStream)
 		return;
 
-	if (CallNPN_DestroyStreamProc (sMozillaFuncs.destroystream,
+	if (CallNPN_DestroyStreamProc (sNPN.destroystream,
 	    			       mInstance,
 				       mStream,
 				       NPRES_DONE) != NPERR_NO_ERROR) {
 		    g_warning ("Couldn't destroy the stream");
+		    /* FIXME: set mStream to NULL here too? */
 		    return;
 	}
 
-	mStream = nsnull;
+	/* Calling DestroyStream should already have set this to NULL */
+	NS_ASSERTION (!mStream, "Should not have a stream anymore");
+	mStream = nsnull; /* just to make sure */
+}
+
+void
+totemPlugin::ViewerButtonPressed ()
+{
+	if (!mWaitingForButtonPress)
+		return;
+
+	D ("ButtonPress");
+
+	mWaitingForButtonPress = PR_FALSE;
+
+#ifdef TOTEM_NARROWSPACE_PLUGIN
+	if (!mHref.IsEmpty () && // FIXME: if (mHrefURI && ... instead?
+	    !mTarget.IsEmpty ()) {
+		if (g_ascii_strcasecmp (mTarget.get (), "quicktimeplayer") == 0) {
+			D ("Launching totem with URL '%s'", mHref.get ());
+			/* FIXME: launch totem */
+			return;
+		}
+		if (g_ascii_strcasecmp (mTarget.get (), "myself") == 0 ||
+		    mTarget.Equals (NS_LITERAL_CSTRING ("_current")) ||
+		    mTarget.Equals (NS_LITERAL_CSTRING ("_self"))) {
+			D ("Opening movie '%s'", mHref.get ());
+			/* FIXME: Open href movie in the plugin */
+			return;
+		}
+
+		/* Load URL in browser. This will either open a new website,
+		 * or execute some javascript.
+		 */
+		nsCString href;
+		if (mHrefURI) {
+			mHrefURI->GetSpec (href);
+		} else {
+			href = mHref;
+		}
+
+		if (CallNPN_GetURLProc (sNPN.geturl,
+					mInstance,
+					href.get (),
+					mTarget.get ()) != NPERR_NO_ERROR) {
+			D ("Failed to launch URL '%s' in browser", mHref.get ());
+		}
+
+		return;
+	}
+#endif
+
+	/* Now is the time to start the stream */
+	if (!mAutostart &&
+	    !mStream) {
+		RequestStream ();
+	}
 }
 
+void
+totemPlugin::NameOwnerChanged (const char *aName,
+			       const char *aOldOwner,
+			       const char *aNewOwner)
+{
+	if (!mViewerPID)
+		return;
+
+	/* Construct viewer interface name */
+	if (NS_UNLIKELY (mViewerServiceName.IsEmpty ())) {
+		char name[256];
+
+		g_snprintf (name, sizeof (name),
+			    TOTEM_PLUGIN_VIEWER_NAME_TEMPLATE,
+			    mViewerPID);
+		mViewerServiceName.Assign (name);
+
+		D ("Viewer DBus interface name is '%s'", mViewerServiceName.get ());
+	}
+
+	if (!mViewerServiceName.Equals (nsDependentCString (aName)))
+		return;
+
+	D ("NameOwnerChanged old-owner '%s' new-owner '%s'", aOldOwner, aNewOwner);
+
+	if (aOldOwner[0] == '\0' /* empty */ &&
+	    aNewOwner[0] != '\0' /* non-empty */) {
+		if (mViewerBusAddress.Equals (nsDependentCString (aNewOwner))) {
+			D ("Already have owner, why are we notified again?");
+		} else if (!mViewerBusAddress.IsEmpty ()) {
+			D ("WTF, new owner!?");
+		} else {
+			/* This is the regular case */
+			D ("Viewer now connected to the bus");
+		}
+
+		mViewerBusAddress.Assign (aNewOwner);
+
+		ViewerSetup ();
+	} else if (!mViewerBusAddress.IsEmpty () &&
+		   mViewerBusAddress.Equals (nsDependentCString (aOldOwner))) {
+		D ("Viewer lost connection!");
+
+		mViewerBusAddress.SetLength (0); /* truncate */
+		/* FIXME */
+		/* ViewerCleanup () ? */
+		/* FIXME if we're not quitting, put up error viewer */
+	}
+	/* FIXME do we really need the lost-connection case?
+	 * We could just disconnect the handler in ViewerSetup
+	 */
+}
+
+/* Callbacks */
+
 /* static */ void PR_CALLBACK 
 totemPlugin::NameOwnerChangedCallback (DBusGProxy *proxy,
-				       const char *svc,
-				       const char *old_owner,
-				       const char *new_owner,
-				       totemPlugin *plugin)
-{
-	D ("Received notification for '%s' old-owner '%s' new-owner '%s'",
-	   svc, old_owner, new_owner);
-
-	if (plugin->mWaitForSvc &&
-	    strcmp (svc, plugin->mWaitForSvc) == 0) {
-		plugin->mGotSvc = TRUE;
-	}
+				       const char *aName,
+				       const char *aOldOwner,
+				       const char *aNewOwner,
+				       void *aData)
+{
+	totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData);
+
+	plugin->NameOwnerChanged (aName, aOldOwner, aNewOwner);
 }
 
 /* static */ void PR_CALLBACK
-totemPlugin::StopSendingDataCallback (DBusGProxy *proxy,
-				      totemPlugin *plugin)
+totemPlugin::ViewerForkTimeoutCallback (nsITimer *aTimer,
+				        void *aData)
 {
-	D("Stop sending data signal received");
+	totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData);
+
+	D ("ViewerForkTimeoutCallback");
+
+	/* FIXME: can this really happen? */
+	NS_ASSERTION (!plugin->mViewerReady, "Viewer ready but timeout running?");
+
+	plugin->ViewerCleanup ();
+	/* FIXME start error viewer */
+}
+
+/* static */ void PR_CALLBACK
+totemPlugin::ButtonPressCallback (DBusGProxy *proxy,
+				  //guint aButton,
+				  //guint aTimestamp,
+			          void *aData)
+{
+	totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData);
+
+	D ("ButtonPress signal received");
+
+	plugin->ViewerButtonPressed ();
+}
+
+/* static */ void PR_CALLBACK
+totemPlugin::StopStreamCallback (DBusGProxy *proxy,
+			         void *aData)
+{
+	totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData);
+
+	D ("StopStream signal received");
 
-	/* FIXME do it from a timer? */
 	plugin->UnsetStream ();
 }
 
-char *
-totemPlugin::GetRealMimeType (const char *mimetype)
+/* static */ void PR_CALLBACK
+totemPlugin::ViewerSetWindowCallback (DBusGProxy *aProxy,
+				      DBusGProxyCall *aCall,
+				      void *aData)
 {
+	totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData);
+
+	D ("SetWindow reply");
+
+	NS_ASSERTION (aCall == plugin->mViewerPendingCall, "SetWindow not the current call");
+
+	plugin->mViewerPendingCall = NULL;
+
+	GError *error = NULL;
+	if (!dbus_g_proxy_end_call (aProxy, aCall, &error, G_TYPE_INVALID)) {
+		/* FIXME: mViewerFailed = PR_TRUE */
+		g_warning ("SetWindow failed: %s", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	plugin->ViewerReady ();
+}
+
+/* static */ void PR_CALLBACK
+totemPlugin::ViewerOpenStreamCallback (DBusGProxy *aProxy,
+				       DBusGProxyCall *aCall,
+				       void *aData)
+{
+	totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData);
+
+	D ("OpenStream reply");
+
+	NS_ASSERTION (aCall == plugin->mViewerPendingCall, "OpenStream not the current call");
+
+	plugin->mViewerPendingCall = NULL;
+
+	GError *error = NULL;
+	if (!dbus_g_proxy_end_call (aProxy, aCall, &error, G_TYPE_INVALID)) {
+		g_warning ("OpenStream failed: %s", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	/* FIXME this isn't the best way... */
+	if (plugin->mHidden &&
+	    plugin->mAutostart) {
+		plugin->DoCommand (TOTEM_COMMAND_PLAY);
+	}
+
+	NS_ASSERTION (!plugin->mExpectingStream, "Already expecting a stream");
+	NS_ENSURE_TRUE (plugin->mRequestURI, );
+
+	plugin->mExpectingStream = PR_TRUE;
+
+	nsCString spec;
+	plugin->mRequestURI->GetSpec (spec);
+
+	NPError err = CallNPN_GetURLProc (sNPN.geturl,
+					  plugin->mInstance,
+					  spec.get (),
+					  NULL);
+	if (err != NPERR_NO_ERROR) {
+		plugin->mExpectingStream = PR_FALSE;
+
+		D ("GetURL '%s' failed with error %d", spec.get (), err);
+	}
+}
+
+/* static */ void PR_CALLBACK
+totemPlugin::ViewerOpenURICallback (DBusGProxy *aProxy,
+				    DBusGProxyCall *aCall,
+				    void *aData)
+{
+	totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData);
+
+	D ("OpenURI reply");
+
+	NS_ASSERTION (aCall == plugin->mViewerPendingCall, "OpenURI not the current call");
+
+	plugin->mViewerPendingCall = NULL;
+
+	GError *error = NULL;
+	if (!dbus_g_proxy_end_call (aProxy, aCall, &error, G_TYPE_INVALID)) {
+		g_warning ("OpenURI failed: %s", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	/* FIXME this isn't the best way... */
+	if (plugin->mAutostart) {
+		plugin->DoCommand (TOTEM_COMMAND_PLAY);
+	}
+}
+
+void
+totemPlugin::GetRealMimeType (const char *mimetype,
+			      nsACString &_retval)
+{
+	_retval.SetLength (0);
+
 	const totemPluginMimeEntry *mimetypes;
 	PRUint32 count;
-
 	totemScriptablePlugin::PluginMimeTypes (&mimetypes, &count);
 
 	for (PRUint32 i = 0; i < count; ++i) {
@@ -212,25 +754,25 @@ totemPlugin::GetRealMimeType (const char
 
 	for (PRUint32 i = 0; i < count; ++i) {
 		if (strcmp (mimetypes[i].mimetype, mimetype) == 0) {
-			if (mimetypes[i].mime_alias != NULL)
-				return g_strdup (mimetypes[i].mime_alias);
-			else
-				return g_strdup (mimetype);
+			if (mimetypes[i].mime_alias != NULL) {
+				_retval.Assign (mimetypes[i].mime_alias);
+			} else {
+				_retval.Assign (mimetype);
+			}
+			return;
 		}
 	}
 
 	D ("Real mime-type for '%s' not found", mimetype);
-	return NULL;
 }
 
 PRBool
-totemPlugin::IsMimeTypeSupported (const char *mimetype, const char *url)
+totemPlugin::IsMimeTypeSupported (const char *mimetype,
+				  const char *url)
 {
-	const totemPluginMimeEntry *mimetypes;
-	PRUint32 count;
-	const char *guessed;
+	NS_ENSURE_TRUE (mimetype, PR_FALSE);
 
-#if defined(TOTEM_MULLY_PLUGIN) || defined (TOTEM_GMP_PLUGIN)
+#if defined(TOTEM_MULLY_PLUGIN) || defined(TOTEM_GMP_PLUGIN)
 	/* We can always play those image types */
 	if (strcmp (mimetype, "image/jpeg") == 0)
 		return PR_TRUE;
@@ -242,6 +784,8 @@ totemPlugin::IsMimeTypeSupported (const 
 	if (strcmp (mimetype, GNOME_VFS_MIME_TYPE_UNKNOWN) == 0)
 		return PR_TRUE;
 
+	const totemPluginMimeEntry *mimetypes;
+	PRUint32 count;
 	totemScriptablePlugin::PluginMimeTypes (&mimetypes, &count);
 
 	for (PRUint32 i = 0; i < count; ++i) {
@@ -250,7 +794,7 @@ totemPlugin::IsMimeTypeSupported (const 
 	}
 
 	/* Not supported? Probably a broken webserver */
-	guessed = gnome_vfs_get_mime_type_for_name (url);
+	const char *guessed = gnome_vfs_get_mime_type_for_name (url);
 
 	D ("Guessed mime-type '%s' for '%s'", guessed, url);
 	for (PRUint32 i = 0; i < count; ++i) {
@@ -278,299 +822,617 @@ totemPlugin::IsMimeTypeSupported (const 
 }
 
 PRBool
-totemPlugin::IsSchemeSupported (const char *url)
+totemPlugin::IsSchemeSupported (nsIURI *aURI)
 {
-	if (url == NULL)
+	if (!aURI)
 		return PR_FALSE;
 
-	if (g_str_has_prefix (url, "mms:") != FALSE)
+	PRBool value;
+	/* FIXME what about mmsh etc? */
+	if (NS_SUCCEEDED (aURI->SchemeIs ("mms", &value)) && value)
 		return PR_FALSE;
-	if (g_str_has_prefix (url, "rtsp:") != FALSE)
+
+	if (NS_SUCCEEDED (aURI->SchemeIs ("rtsp", &value)) && value)
 		return PR_FALSE;
 
 	return PR_TRUE;
 }
 
-PRBool
-totemPlugin::Fork ()
+NPError
+totemPlugin::ViewerFork ()
 {
-	GTimeVal then, now;
-	GPtrArray *arr;
-	char **argv;
-	GError *err = NULL;
-	gboolean use_fd = FALSE;
-
-	if (mSrc == NULL) {
-		g_warning ("No URLs passed!");
-		return FALSE;
-	}
+#ifdef TOTEM_COMPLEX_PLUGIN
+	/* Don't fork a viewer if we're going to use another one */
+	if (!mNeedViewer)
+		return NPERR_NO_ERROR;
+#endif /* TOTEM_COMPLEX_PLUGIN */
 
-	/* Make sure we don't get both an XID and hidden */
-	if (mWindow && mHidden) {
-		g_warning ("Both hidden and a window!");
-		return FALSE;
+	const char *userAgent = CallNPN_UserAgentProc (sNPN.uagent,
+						       mInstance);
+	if (!userAgent) {
+		/* See https://bugzilla.mozilla.org/show_bug.cgi?id=328778 */
+		D ("User agent has more than 127 characters; fix your browser!");
 	}
 
-	arr = g_ptr_array_new ();
+        GPtrArray *arr = g_ptr_array_new ();
+	/* FIXME! no need to strdup, all args are const! */
 
+	/* FIXME what use is this anyway? */
 #ifdef TOTEM_RUN_IN_SOURCE_TREE
-	if (g_file_test ("./totem-mozilla-viewer",
-				G_FILE_TEST_EXISTS) != FALSE) {
-		g_ptr_array_add (arr, g_strdup ("./totem-mozilla-viewer"));
+	if (g_file_test ("../src/totem-mozilla-viewer",
+			 G_FILE_TEST_EXISTS) != FALSE) {
+			 g_ptr_array_add (arr, g_strdup ("../src/totem-mozilla-viewer"));
 	} else {
 		g_ptr_array_add (arr,
-				g_strdup (LIBEXECDIR"/totem-mozilla-viewer"));
+				 g_build_filename (LIBEXECDIR, "totem-mozilla-viewer", NULL));
 	}
 #else
 	g_ptr_array_add (arr,
-			g_strdup (LIBEXECDIR"/totem-mozilla-viewer"));
+			 g_build_filename (LIBEXECDIR, "totem-mozilla-viewer", NULL));
+#endif
+
+	/* So we can debug X errors in the viewer */
+	const char *sync = g_getenv ("TOTEM_EMBEDDED_DEBUG_SYNC");
+	if (sync && sync[0] == '1') {
+		g_ptr_array_add (arr, g_strdup ("--sync"));
+	}
+
+	g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_PLUGIN_TYPE));
+#if defined(TOTEM_BASIC_PLUGIN)
+	g_ptr_array_add (arr, g_strdup ("basic"));
+#elif defined(TOTEM_GMP_PLUGIN)
+	g_ptr_array_add (arr, g_strdup ("gmp"));
+#elif defined(TOTEM_COMPLEX_PLUGIN)
+	g_ptr_array_add (arr, g_strdup ("complex"));
+#elif defined(TOTEM_NARROWSPACE_PLUGIN)
+	g_ptr_array_add (arr, g_strdup ("narrowspace"));
+#elif defined(TOTEM_MULLY_PLUGIN)
+	g_ptr_array_add (arr, g_strdup ("mully"));
+#else
+#error Unknown plugin type
+#endif
+	
+	if (userAgent) {
+		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_USER_AGENT));
+		g_ptr_array_add (arr, g_strdup (userAgent));
+	}
+
+	/* FIXME: remove this */
+	if (!mMimeType.IsEmpty ()) {
+		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_MIMETYPE));
+		g_ptr_array_add (arr, g_strdup (mMimeType.get()));
+	}
+
+	if (mControllerHidden) {
+		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_CONTROLS_HIDDEN));
+	}
+
+	if (mShowStatusbar) {
+		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_STATUSBAR));
+	}
+
+	if (mHidden) {
+		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_HIDDEN));
+	}
+
+ 	if (mRepeat) {
+ 		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_REPEAT));
+ 	}
+
+	if (!mAutostart) {
+		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_NOAUTOSTART));
+	}
+
+	g_ptr_array_add (arr, NULL);
+	char **argv = (char **) g_ptr_array_free (arr, FALSE);
+
+#ifdef GNOME_ENABLE_DEBUG
+	{
+		GString *s;
+		int i;
+
+		s = g_string_new ("Launching: ");
+		for (i = 0; argv[i] != NULL; i++) {
+			g_string_append (s, argv[i]);
+			g_string_append (s, " ");
+		}
+		g_string_append (s, "\n");
+		D("%s", s->str);
+		g_string_free (s, TRUE);
+	}
+#endif
+
+	mViewerReady = PR_FALSE;
+
+	/* Don't wait forever! */
+	const PRUint32 kViewerTimeout = 30 * 1000; /* ms */
+	nsresult rv = mTimer->InitWithFuncCallback (ViewerForkTimeoutCallback,
+						    NS_REINTERPRET_CAST (void*, this),
+						    kViewerTimeout,
+						    nsITimer::TYPE_ONE_SHOT);
+	if (NS_FAILED (rv)) {
+		D ("Failed to initialise timer");
+		return NPERR_GENERIC_ERROR;
+	}
+
+	/* FIXME: once gecko is multihead-safe, this should use gdk_spawn_on_screen_with_pipes */
+	GError *error = NULL;
+	if (g_spawn_async_with_pipes (NULL /* working directory FIXME: use $TMPDIR ? */,
+				      argv,
+				      NULL /* environment */,
+				      GSpawnFlags(0),
+				      NULL /* child setup func */, NULL,
+				      &mViewerPID,
+				      &mViewerFD, NULL, NULL,
+				      &error) == FALSE)
+	{
+		g_warning ("Failed to spawn viewer: %s", error->message);
+		g_error_free(error);
+
+		g_strfreev (argv);
+
+		return NPERR_GENERIC_ERROR;
+	}
+
+	g_strfreev (argv);
+
+	D("Viewer spawned, PID %d", mViewerPID);
+
+	/* FIXME: can this happen? */
+	if (mViewerFD < 0) {
+		ViewerCleanup ();
+		return NPERR_GENERIC_ERROR;
+	}
+
+	/* Set mViewerFD nonblocking */
+	fcntl (mViewerFD, F_SETFL, O_NONBLOCK);
+
+	return NPERR_NO_ERROR;
+}
+
+void
+totemPlugin::ViewerCleanup ()
+{
+	mViewerBusAddress.SetLength (0);
+	mViewerServiceName.SetLength (0);
+
+	if (mViewerPendingCall) {
+		dbus_g_proxy_cancel_call (mViewerProxy, mViewerPendingCall);
+		mViewerPendingCall = NULL;
+	}
+
+	if (mViewerProxy) {
+		dbus_g_proxy_disconnect_signal (mViewerProxy,
+						"ButtonPress",
+						G_CALLBACK (ButtonPressCallback),
+						NS_REINTERPRET_CAST (void*, this));
+		dbus_g_proxy_disconnect_signal (mViewerProxy,
+						"StopStream",
+						G_CALLBACK (StopStreamCallback),
+						NS_REINTERPRET_CAST (void*, this));
+
+		g_object_unref (mViewerProxy);
+		mViewerProxy = NULL;
+	}
+
+	if (mViewerFD >= 0) {
+		close (mViewerFD);
+		mViewerFD = -1;
+	}
+
+	if (mViewerPID) {
+		kill (mViewerPID, SIGKILL);
+		g_spawn_close_pid (mViewerPID);
+		mViewerPID = 0;
+	}
+}
+
+#if 0
+#ifdef TOTEM_NARROWSPACE_PLUGIN
+static char *
+parse_url_extensions (char *href)
+{
+	char *uri, *s1, *s2;
+
+	g_return_val_if_fail (href != NULL || href[0] != '<', NULL);
+
+	s1 = href + 1;
+	s2 = strchr (s1, '>');
+	if (s2 == NULL)
+		return g_strdup (href);
+
+	uri = g_strndup (s1, s2 - s1);
+
+	return uri;
+}
 #endif
+#endif
+
+PRBool
+totemPlugin::ParseBoolean (const char *key,
+			   const char *value,
+			   PRBool default_val)
+{
+	if (value == NULL || strcmp (value, "") == 0)
+		return default_val;
+	if (g_ascii_strcasecmp (value, "false") == 0
+			|| g_ascii_strcasecmp (value, "0") == 0)
+		return PR_FALSE;
+	if (g_ascii_strcasecmp (value, "true") == 0
+			|| g_ascii_strcasecmp (value, "1") == 0)
+		return PR_TRUE;
+	/* FIXME: "no"/"yes"? */
+
+	D ("Unknown value '%s' for parameter '%s'", value, key);
+
+	return default_val;
+}
+
+PRBool
+totemPlugin::GetBooleanValue (GHashTable *args,
+			      const char *key,
+			      PRBool default_val)
+{
+	const char *value;
+
+	value = (const char *) g_hash_table_lookup (args, key);
+	if (value == NULL)
+		return default_val;
+
+	return ParseBoolean (key, value, default_val);
+}
+
+PRUint32
+totemPlugin::GetEnumIndex (GHashTable *args,
+			   const char *key,
+			   const char *values[],
+			   PRUint32 n_values,
+			   PRUint32 default_value)
+{
+	const char *value = (const char *) g_hash_table_lookup (args, key);
+	if (!value)
+		return default_value;
+
+	for (PRUint32 i = 0; i < n_values; ++i) {
+		if (g_ascii_strcasecmp (value, values[i]) == 0)
+			return i;
+	}
+
+	return default_value;
+}
+
+nsresult
+totemPlugin::SetSrc (const nsACString& aURL)
+{
+	if (mSrcURI) {
+		NS_RELEASE (mSrcURI);
+		mSrcURI = nsnull;
+	}
+
+	nsresult rv = mBaseURI->Resolve (aURL, mSrc);
+	if (NS_FAILED (rv)) {
+		/* Save unresolve src */
+		mSrc = aURL;
+	}
+
+	rv = mIOService->NewURI (aURL, nsnull /* FIXME! use document charset */,
+				 mBaseURI, &mSrcURI);
+	if (NS_FAILED (rv)) {
+		D ("Failed to create src URI (rv=%x)", rv);
+		mSrcURI = nsnull;
+	}
+
+	return rv;
+}
+
+#ifdef TOTEM_GMP_PLUGIN
+
+nsresult
+totemPlugin::SetURL (const nsACString& aURL)
+{
+	if (mURLURI) {
+		NS_RELEASE (mURLURI);
+		mURLURI = nsnull;
+	}
+
+	/* FIXME: what is the correct base for the URL param? */
+	nsresult rv;
+	nsIURI *baseURI;
+	if (mSrcURI) {
+		baseURI = mSrcURI;
+	} else {
+		baseURI = mBaseURI;
+	}
+
+	rv = mIOService->NewURI (aURL, nsnull /* FIXME document charset? */,
+				 baseURI, &mURLURI);
+	if (NS_FAILED (rv)) {
+		D ("Failed to create URL URI (rv=%x)", rv);
+	}
+
+	/* FIXME: security checks? */
+
+	return rv;
+}
+
+#endif /* TOTEM_GMP_PLUGIN */
+
+#ifdef TOTEM_NARROWSPACE_PLUGIN
+
+nsresult
+totemPlugin::SetQtsrc (const nsACString& aURL)
+{
+	/* FIXMEchpe parse URL extensions? */
+
+	if (mQtsrcURI) {
+		NS_RELEASE (mQtsrcURI);
+		mQtsrcURI = nsnull;
+	}
+
+	nsresult rv;
+	nsIURI *baseURI;
+	if (mSrcURI) {
+		baseURI = mSrcURI;
+	} else {
+		baseURI = mBaseURI;
+	}
+
+	rv = mIOService->NewURI (aURL, nsnull /* FIXME document charset? */,
+				 baseURI, &mQtsrcURI);
+	if (NS_FAILED (rv)) {
+		D ("Failed to create QTSRC URI (rv=%x)", rv);
+	}
+
+	/* FIXME: security checks? */
+
+	return rv;
+}
+
+nsresult
+totemPlugin::SetHref (const nsACString& aURL)
+{
+	/* FIXMEchpe parse URL extensions! */
 
-	/* Most for RealAudio streams, but also used as a replacement for
-	 * HIDDEN=TRUE */
-	if (mWidth == 0 && mHeight == 0) {
-		mWindow = 0;
-		mHidden = TRUE;
+	nsresult rv;
+	nsIURI *baseURI;
+	if (mQtsrcURI) {
+		baseURI = mQtsrcURI;
+	} else if (mSrcURI) {
+		baseURI = mSrcURI;
+	} else {
+		baseURI = mBaseURI;
 	}
 
-	if (mWindow) {
-		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_XID));
-		g_ptr_array_add (arr, g_strdup_printf ("%lu", mWindow));
+	rv = baseURI->Resolve (aURL, mHref);
+	if (NS_FAILED (rv)) {
+		D ("Failed to resolve HREF (rv=%x)", rv);
+		mHref = aURL; /* save unresolved HREF */
+	} else {
+		D ("Resolved HREF '%s'", mHref.get());
 	}
 
-	if (mWidth > 0) {
-		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_WIDTH));
-		g_ptr_array_add (arr, g_strdup_printf ("%d", mWidth));
-	}
+	return rv;
+}
 
-	if (mHeight > 0) {
-		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_HEIGHT));
-		g_ptr_array_add (arr, g_strdup_printf ("%d", mHeight));
-	}
+#endif /* TOTEM_NARROWSPACE_PLUGIN */
 
-	if (mSrc) {
-		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_URL));
-		g_ptr_array_add (arr, g_strdup (mSrc));
-	}
+#ifdef TOTEM_COMPLEX_PLUGIN
 
-	if (mHref) {
-		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_HREF));
-		g_ptr_array_add (arr, g_strdup (mHref));
+totemPlugin *
+totemPlugin::FindConsoleClassRepresentant ()
+{
+	/* FIXME: this treats "master" incorrectly */
+	if (!mSrcURI ||
+	    mConsole.IsEmpty () ||
+	    mConsole.Equals (NS_LITERAL_CSTRING ("_unique")) ||
+	    mConsole.Equals (NS_LITERAL_CSTRING ("_master"))) {
+		D ("We're the representant for the console class");
+		return nsnull;
 	}
 
-	if (mTarget) {
-		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_TARGET));
-		g_ptr_array_add (arr, g_strdup (mTarget));
-	}
+	totemPlugin *representant = nsnull;
 
-	if (mMimeType) {
-		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_MIMETYPE));
-		g_ptr_array_add (arr, g_strdup (mMimeType));
-	}
+	/* Try to find a the representant of the console class */
+	PRUint32 count = sPlugins->Length ();
+	for (PRUint32 i = 0; i < count; ++i) {
+		totemPlugin *plugin = sPlugins->ElementAt (i);
 
-	if (mControllerHidden) {
-		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_CONTROLS_HIDDEN));
+		PRBool equal = PR_FALSE;
+		/* FIXME: is this correct? Maybe we should use the toplevel document
+		 * to allow frames (and check the frames for same-origin, obviously) ?
+		 */
+		if (plugin != this &&
+		    plugin->mPluginOwnerDocument == mPluginOwnerDocument &&
+		    mConsole.Equals (plugin->mConsole) &&
+		    plugin->mSrcURI &&
+		    NS_SUCCEEDED (plugin->mSrcURI->Equals (mSrcURI, &equal)) &&
+		    equal) {
+			if (plugin->mConsoleClassRepresentant) {
+				representant = plugin->mConsoleClassRepresentant;
+			} else {
+				representant = plugin;
+			}
+			break;
+		}
 	}
 
-	if (mStatusbar) {
-		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_STATUSBAR));
-	}
+	D ("Representant for the console class is %p", (void*) representant);
 
-	if (mHidden) {
-		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_HIDDEN));
-	}
+	return representant;
+}
 
- 	if (mRepeat) {
- 		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_REPEAT));
- 	}
+nsresult
+totemPlugin::SetConsole (const nsACString &aConsole)
+{
+	/* Can't change console group */
+	if (!mConsole.IsEmpty ())
+		return NS_ERROR_ALREADY_INITIALIZED;
 
-	if (mNoAutostart) {
-		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_NOAUTOSTART));
-	}
- 
- 	if (mIsPlaylist) {
- 		g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_PLAYLIST));
- 		g_ptr_array_add (arr, g_strdup (mLocal));
- 	} else {
-		/* mLocal is only TRUE for playlists */
-		if (mIsSupportedSrc == FALSE) {
-			g_ptr_array_add (arr, g_strdup (mSrc));
-		} else {
-			g_ptr_array_add (arr, g_strdup ("fd://0"));
-			use_fd = TRUE;
-		}
-	}
+	/* FIXME: we might allow this, and kill the viewer instead.
+	 * Or maybe not spawn the viewer if we don't have a console yet?
+	 */
+	if (mViewerPID)
+		return NS_ERROR_ALREADY_INITIALIZED;
 
-	g_ptr_array_add (arr, NULL);
-	argv = (char **) g_ptr_array_free (arr, FALSE);
+	mConsole = aConsole;
 
-#ifdef GNOME_ENABLE_DEBUG
-	{
-		GString *s;
-		int i;
+	NS_ASSERTION (mConsoleClassRepresentant == nsnull, "Already have a representant");
 
-		s = g_string_new ("Launching: ");
-		for (i = 0; argv[i] != NULL; i++) {
-			g_string_append (s, argv[i]);
-			g_string_append (s, " ");
+	mConsoleClassRepresentant = FindConsoleClassRepresentant ();
+	mNeedViewer = (nsnull == mConsoleClassRepresentant);
+
+	return NS_OK;
+}
+
+void
+totemPlugin::TransferConsole ()
+{
+	/* Find replacement representant */
+	totemPlugin *representant = nsnull;
+
+	PRUint32 i, count = sPlugins->Length ();
+	for (i = 0; i < count; ++i) {
+		totemPlugin *plugin = sPlugins->ElementAt (i);
+
+		if (plugin->mConsoleClassRepresentant == this) {
+			representant = plugin;
+			break;
 		}
-		g_string_append (s, "\n");
-		D("%s", s->str);
-		g_string_free (s, TRUE);
 	}
-#endif
 
-	if (g_spawn_async_with_pipes (NULL, argv, NULL,
-				      GSpawnFlags(0), NULL, NULL, &mPlayerPID,
-				      use_fd ? &mSendFD : NULL, NULL, NULL, &err) == FALSE)
-	{
-		D("Spawn failed");
+	/* If there are no other elements in this console class, there's nothing to do */
+	if (!representant)
+		return;
 
-		if (err) {
-			g_warning ("%s\n", err->message);
-			g_error_free(err);
-		}
+	D ("Transferring console from %p to %p", (void*) this, (void*) representant);
 
-		g_strfreev (argv);
-		return FALSE;
+	/* Store new representant in the plugins */
+	representant->mConsoleClassRepresentant = nsnull;
+	/* We can start at i since we got out when we found the first one in the loop above */
+	for ( ; i < count; ++i) {
+		totemPlugin *plugin = sPlugins->ElementAt (i);
+
+		if (plugin->mConsoleClassRepresentant == this)
+			plugin->mConsoleClassRepresentant = representant;
 	}
 
-	g_strfreev (argv);
+	/* Now transfer viewer ownership */
+	if (mScriptable) {
+		NS_ASSERTION (!representant->mScriptable, "WTF");
+		representant->mScriptable = mScriptable;
+		mScriptable->SetPlugin (representant);
+		mScriptable = nsnull;
+	}
 
-	D("player PID: %d", mPlayerPID);
+	representant->mNeedViewer = PR_TRUE;
 
-	/* FIXME: not >= 0 ? */
-	if (mSendFD > 0)
-		fcntl(mSendFD, F_SETFL, O_NONBLOCK);
-
-	/* now wait until startup is complete */
-	mGotSvc = FALSE;
-	mWaitForSvc = g_strdup_printf
-		("org.totem_%d.MozillaPluginService", mPlayerPID);
-	D("waiting for signal %s", mWaitForSvc);
-	dbus_g_proxy_add_signal (mProxy, "NameOwnerChanged",
-				 G_TYPE_STRING, G_TYPE_STRING,
-				 G_TYPE_STRING, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (mProxy, "NameOwnerChanged",
-				     G_CALLBACK (NameOwnerChangedCallback),
-				     this, NULL);
+	representant->mViewerPID = mViewerPID;
+	mViewerPID = 0;
 
-	/* Store this in a local, since we cannot access mScriptable if we get deleted in the loop */
-	totemScriptablePlugin *scriptable = mScriptable;
-	g_get_current_time (&then);
-	g_time_val_add (&then, G_USEC_PER_SEC * 5);
-	NS_ADDREF (scriptable);
-	/* FIXME FIXME! this loop is evil! */
-	do {
-		g_main_context_iteration (NULL, TRUE);
-		g_get_current_time (&now);
-	} while (scriptable->IsValid () &&
-		 !mGotSvc &&
-		 (now.tv_sec <= then.tv_sec));
-
-	if (!scriptable->IsValid ()) {
-		/* we were destroyed in one of the iterations of the
-		 * mainloop, get out ASAP */
-		D("We no longer exist");
-		NS_RELEASE (scriptable);
-		return FALSE;
-	}
-	NS_RELEASE (scriptable);
-
-	dbus_g_proxy_disconnect_signal (mProxy, "NameOwnerChanged",
-					G_CALLBACK (NameOwnerChangedCallback), this);
-	if (!mGotSvc) {
-		g_warning ("Failed to receive DBUS interface response");
-		g_free (mWaitForSvc);
-		mWaitForSvc = nsnull;
-
-		if (mPlayerPID) {
-			kill (mPlayerPID, SIGKILL);
-			g_spawn_close_pid (mPlayerPID);
-			mPlayerPID = 0;
-		}
-		return FALSE;
-	}
-	g_object_unref (mProxy);
+	representant->mViewerFD = mViewerFD;
+	mViewerFD = -1;
 
-	/* now get the proxy for the player functions */
-	mProxy =
-		dbus_g_proxy_new_for_name (mConn, mWaitForSvc,
-					   "/TotemEmbedded",
-					   "org.totem.MozillaPluginInterface");
-	dbus_g_proxy_add_signal (mProxy, "StopSendingData",
-				 G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (mProxy, "StopSendingData",
-				     G_CALLBACK (StopSendingDataCallback),
-				     this, NULL);
-
-	g_free (mWaitForSvc);
-	mWaitForSvc = nsnull;
-	D("Done forking, new proxy=%p", mProxy);
+	representant->mViewerBusAddress = mViewerBusAddress;
+	representant->mViewerServiceName = mViewerServiceName;
 
-	return TRUE;
+	representant->ViewerSetup ();
 }
 
-static char *
-resolve_relative_uri (nsIURI *docURI,
-		      const char *uri)
+void
+totemPlugin::UnownedViewerSetup ()
 {
-	if (docURI) {
-		nsresult rv;
-		nsEmbedCString resolved;
-		rv = docURI->Resolve (nsEmbedCString (uri), resolved);
-		if (NS_SUCCEEDED (rv)) {
-			return g_strdup (resolved.get());
-		}
+	/* already set up */
+	if (mUnownedViewerSetUp)
+		return;
+
+	mUnownedViewerSetUp = PR_TRUE;
+
+	D ("UnownedViewerSetup");
+
+	NS_ASSERTION (mConsoleClassRepresentant, "We own the viewer!");
+
+	UnownedViewerSetWindow ();
+}
+
+void
+totemPlugin::UnownedViewerSetWindow ()
+{
+	if (mWindowSet || mWindow == 0)
+		return;
+
+	if (!mUnownedViewerSetUp) {
+		D ("No unowned viewer yet, deferring SetWindow");
+		return;
 	}
 
-	return g_strdup (uri);
+	NS_ASSERTION (mConsoleClassRepresentant, "We own the viewer!");
+
+	NS_ENSURE_TRUE (mConsoleClassRepresentant->mViewerProxy, );
+
+	dbus_g_proxy_call_no_reply (mConsoleClassRepresentant->mViewerProxy,
+				    "SetWindow",
+				    G_TYPE_STRING, mControls.get (),
+				    G_TYPE_UINT, (guint) mWindow,
+				    G_TYPE_INT, (gint) mWidth,
+				    G_TYPE_INT, (gint) mHeight,
+				    G_TYPE_INVALID);
+
+	mWindowSet = PR_TRUE;
 }
 
-#ifdef TOTEM_NARROWSPACE_PLUGIN
-static char *
-parse_url_extensions (char *href)
+void
+totemPlugin::UnownedViewerUnsetWindow ()
 {
-	char *uri, *s1, *s2;
+	if (!mWindowSet || mWindow == 0)
+		return;
 
-	g_return_val_if_fail (href != NULL || href[0] != '<', NULL);
+	if (!mUnownedViewerSetUp)
+		return;
 
-	s1 = href + 1;
-	s2 = strchr (s1, '>');
-	if (s2 == NULL)
-		return g_strdup (href);
+	NS_ASSERTION (mConsoleClassRepresentant, "We own the viewer!");
 
-	uri = g_strndup (s1, s2 - s1);
+	NS_ENSURE_TRUE (mConsoleClassRepresentant->mViewerProxy, );
 
-	return uri;
+	dbus_g_proxy_call_no_reply (mConsoleClassRepresentant->mViewerProxy,
+				    "UnsetWindow",
+				    G_TYPE_UINT, (guint) mWindow,
+				    G_TYPE_INVALID);
+
+	mWindowSet = PR_FALSE;
 }
-#endif
 
-gboolean
-totem_parse_boolean (char *key, char *value, gboolean default_val)
+#endif /* TOTEM_COMPLEX_PLUGIN */
+
+/* Plugin glue functions */
+
+/* static */ NPError
+totemPlugin::Initialise ()
 {
-	if (value == NULL || strcmp (value, "") == 0)
-		return default_val;
-	if (g_ascii_strcasecmp (value, "false") == 0
-			|| g_ascii_strcasecmp (value, "0") == 0)
-		return FALSE;
-	if (g_ascii_strcasecmp (value, "true") == 0
-			|| g_ascii_strcasecmp (value, "1") == 0)
-		return TRUE;
+#ifdef TOTEM_COMPLEX_PLUGIN
+	sPlugins = new nsTArray<totemPlugin*> (32);
+	if (!sPlugins)
+		return NPERR_OUT_OF_MEMORY_ERROR;
+	D ("Capacity: %d", sPlugins->Capacity ());
+#endif /* TOTEM_COMPLEX_PLUGIN */
 
-	g_warning ("Unknown value '%s' for parameter '%s'", value, key);
-	return default_val;
+	return NPERR_NO_ERROR;
 }
 
-gboolean
-totem_get_boolean_value (GHashTable *args, char *key, gboolean default_val)
+/* static */ NPError
+totemPlugin::Shutdown ()
 {
-	char *value;
+#ifdef TOTEM_COMPLEX_PLUGIN
+	if (sPlugins) {
+		if (!sPlugins->IsEmpty ()) {
+			D ("WARNING: sPlugins not empty on shutdown, count: %d", sPlugins->Length ());
+		}
 
-	value = (char *) g_hash_table_lookup (args, key);
-	if (value == NULL)
-		return default_val;
-	return totem_parse_boolean (key, value, default_val);
+		delete sPlugins;
+		sPlugins = nsnull;
+	}
+#endif /* TOTEM_COMPLEX_PLUGIN */
+
+	return NPERR_NO_ERROR;
 }
 
 NPError
@@ -581,168 +1443,292 @@ totemPlugin::Init (NPMIMEType mimetype,
 		   char *argv[],
 		   NPSavedData *saved)
 {
-	GError *e = NULL;
-	gboolean need_req = FALSE;
-	int i;
-	GHashTable *args;
-	char *value;
+	D ("Init mimetype '%s' mode %d", (const char *) mimetype, mode);
 
-	mScriptable = new totemScriptablePlugin (this);
-	if (!mScriptable)
-		return NPERR_OUT_OF_MEMORY_ERROR;
+	/* Make sure the plugin stays resident, to avoid
+	 * reloading the GObject types.
+	 */
+	CallNPN_SetValueProc (sNPN.setvalue,
+			      mInstance,
+			      NPPVpluginKeepLibraryInMemory,
+			      NS_INT32_TO_PTR (PR_TRUE));
+
+	/* mode is NP_EMBED, NP_FULL, or NP_BACKGROUND (see npapi.h) */
+	/* FIXME we should error out if we are in fullscreen mode
+	 * FIXME: This might be possible on gecko trunk by returning an
+ 	 * error code from the NewStream function.
+	 */
+
+	NPError err;
+	err = CallNPN_GetValueProc (sNPN.getvalue,
+				    mInstance, NPNVserviceManager,
+				    NS_REINTERPRET_CAST (void *,
+							 NS_REINTERPRET_CAST (void **, &mServiceManager)));
+	if (err != NPERR_NO_ERROR || !mServiceManager) {
+		D ("Failed to get the service manager");
+		return NPERR_GENERIC_ERROR;
+	}
 
-	NS_ADDREF (mScriptable);
+	nsresult rv;
+	rv = mServiceManager->GetServiceByContractID (NS_IOSERVICE_CONTRACTID,
+						      NS_GET_IID (nsIIOService),
+						      NS_REINTERPRET_CAST (void **, &mIOService));
+	if (NS_FAILED (rv) || !mIOService) {
+		D ("Failed to get IO service");
+		return NPERR_GENERIC_ERROR;
+	}
 
-	if (!(mConn = dbus_g_bus_get (DBUS_BUS_SESSION, &e))) {
-		printf ("Failed to open DBUS session: %s\n", e->message);
-		g_error_free (e);
-		return NPERR_OUT_OF_MEMORY_ERROR;
-	} else if (!(mProxy = dbus_g_proxy_new_for_name (mConn,
-					"org.freedesktop.DBus",
-					"/org/freedesktop/DBus",
-					"org.freedesktop.DBus"))) {
-		printf ("Failed to open DBUS proxy: %s\n", e->message);
-		g_error_free (e);
-		return NPERR_OUT_OF_MEMORY_ERROR;
+	err = CallNPN_GetValueProc (sNPN.getvalue,
+				    mInstance, NPNVDOMElement,
+				    NS_REINTERPRET_CAST (void *,
+						         NS_REINTERPRET_CAST (void **, &mPluginDOMElement)));
+	if (err != NPERR_NO_ERROR || !mPluginDOMElement) {
+		D ("Failed to get our DOM Element");
+		return NPERR_GENERIC_ERROR;
 	}
 
-	/* mode is NP_EMBED, NP_FULL, or NP_BACKGROUND (see npapi.h) */
-	//FIXME we should error out if we are in fullscreen mode
-	printf("mode %d\n",mode);
-	printf("mime type: %s\n", mimetype);
-
-	/* to resolve relative URLs */
-	nsIDOMWindow *domWin = nsnull;
-	sMozillaFuncs.getvalue (mInstance, NPNVDOMWindow,
-				    NS_REINTERPRET_CAST (void**, &domWin));
-	nsIInterfaceRequestor *irq = nsnull;
-	if (domWin) {
-		domWin->QueryInterface (NS_GET_IID (nsIInterfaceRequestor),
-					NS_REINTERPRET_CAST (void**, &irq));
-		NS_RELEASE (domWin);
-	}
-
-	nsIWebNavigation *webNav = nsnull;
-	if (irq) {
-		irq->GetInterface (NS_GET_IID (nsIWebNavigation),
-				   NS_REINTERPRET_CAST (void**, &webNav));
-		NS_RELEASE (irq);
-	}
-
-	nsIURI *docURI = nsnull;
-	if (webNav) {
-		webNav->GetCurrentURI (&docURI);
-		NS_RELEASE (webNav);
+#ifdef TOTEM_COMPLEX_PLUGIN
+	rv = mPluginDOMElement->GetOwnerDocument (&mPluginOwnerDocument);
+	if (NS_FAILED (rv) || !mPluginOwnerDocument) {
+		D ("Plugin in a document!?");
+		return NPERR_GENERIC_ERROR;
 	}
+#endif /* TOTEM_COMPLEX_PLUGIN */
+
+	/* We'd like to get the base URI of our DOM element as a nsIURI,
+	 * but there's no frozen method to do so (nsIContent/nsINode isn't available
+	 * for non-MOZILLA_INTERNAL_API code). nsIDOM3Node isn't frozen either,
+	 * but should be safe enough.
+	 */
+	nsIDOM3Node *dom3Node = nsnull;
+	rv = CallQueryInterface (mPluginDOMElement, &dom3Node);
+	if (NS_FAILED (rv) || !dom3Node) {
+		D ("Failed to QI the DOM element to nsIDOM3Node");
+		return NPERR_GENERIC_ERROR;
+	}
+
+        /* FIXME: can we cache this, or can it change (so we'll need to re-get every time we use it)? */
+	nsString baseASpec;
+	rv = dom3Node->GetBaseURI (baseASpec);
+	if (NS_FAILED (rv) || baseASpec.IsEmpty ()) {
+		/* We can't go on without a base URI ! */
+		D ("Failed to get base URI spec");
+		return NPERR_GENERIC_ERROR;
+	}
+
+	NS_ConvertUTF16toUTF8 baseSpec (baseASpec);
+
+	D ("Base URI is '%s'", baseSpec.get ());
+
+	rv = mIOService->NewURI (baseSpec, nsnull /* FIXME: use document charset */,
+				 nsnull, &mBaseURI);
+	if (NS_FAILED (rv) || !mBaseURI) {
+		D ("Failed to construct base URI");
+		return NPERR_GENERIC_ERROR;
+	}
+
+        /* Create timer */
+        nsIComponentManager *compMan = nsnull;
+	rv = CallQueryInterface (mServiceManager, &compMan);
+        if (NS_FAILED (rv) || !compMan) {
+                D ("Failed to get component manager");
+                return NPERR_GENERIC_ERROR;
+        }
+
+        /* FIXME ? */
+        rv = compMan->CreateInstanceByContractID (NS_TIMER_CONTRACTID,
+                                                  nsnull,
+                                                  NS_GET_IID (nsITimer),
+                                                  NS_REINTERPRET_CAST (void **, &mTimer));
+        if (NS_FAILED (rv) || !mTimer) {
+                D ("Failed to create timer: rv=%x", rv);
+                return NPERR_GENERIC_ERROR;
+        }
+
+	/* Setup DBus connection handling */
+	GError *error = NULL;
+	if (!(mBusConnection = dbus_g_bus_get (DBUS_BUS_SESSION, &error))) {
+		g_message ("Failed to open DBUS session: %s", error->message);
+		g_error_free (error);
+
+		return NPERR_GENERIC_ERROR;
+	};
+
+	if (!(mBusProxy = dbus_g_proxy_new_for_name (mBusConnection,
+	      					     DBUS_SERVICE_DBUS,
+						     DBUS_PATH_DBUS,
+						     DBUS_INTERFACE_DBUS))) {
+		D ("Failed to get DBUS proxy");
+		return NPERR_OUT_OF_MEMORY_ERROR;
+	}
+
+	dbus_g_proxy_add_signal (mBusProxy,
+				 "NameOwnerChanged",
+				 G_TYPE_STRING,
+				 G_TYPE_STRING,
+				 G_TYPE_STRING,
+				 G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (mBusProxy,
+				     "NameOwnerChanged",
+				     G_CALLBACK (NameOwnerChangedCallback),
+				     NS_REINTERPRET_CAST (void*, this),
+				     NULL);
 
 	/* Find the "real" mime-type */
-	mMimeType = GetRealMimeType (mimetype);
+	GetRealMimeType (mimetype, mMimeType);
 
-	args = g_hash_table_new_full (g_str_hash, g_str_equal,
-			(GDestroyNotify) g_free, (GDestroyNotify) g_free);
-	for (i = 0; i < argc; i++) {
+	D ("Real mimetype for '%s' is '%s'", mimetype, mMimeType.get());
+
+	/* Now parse the attributes */
+	GHashTable *args = g_hash_table_new_full (g_str_hash,
+		g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) g_free);
+	for (int16_t i = 0; i < argc; i++) {
 		printf ("argv[%d] %s %s\n", i, argn[i], argv[i]);
 		g_hash_table_insert (args, g_ascii_strdown (argn[i], -1),
-				g_strdup (argv[i]));
+				     g_strdup (argv[i]));
 	}
 
-	/* The QuickTime href system, with the src video just being a link
-	 * to this video */
-#ifdef TOTEM_NARROWSPACE_PLUGIN
-	/* New URLs extensions:
-	 * http://developer.apple.com/documentation/QuickTime/WhatsNewQT5/QT5NewChapt1/chapter_1_section_32.html
-	 * http://developer.apple.com/documentation/QuickTime/Conceptual/QTScripting_HTML/QTScripting_HTML_Document/chapter_1000_section_3.html */
+	const char *value;
 
-	value = (char *) g_hash_table_lookup (args, "href");
+	/* We only use the size attributes to detect whether we're hidden;
+	 * we'll get our real size from SetWindow.
+	 */
+	PRInt32 width = -1, height = -1;
+
+	value = (const char *) g_hash_table_lookup (args, "width");
 	if (value != NULL) {
-		if (value[0] == '<') {
-			//FIXME fill the hashtable with the new values
-			//from the extended URLs
-			char *href = parse_url_extensions (value);
-
-			//FIXME
-			// http://developer.apple.com/documentation/QuickTime/Conceptual/QTScripting_HTML/QTScripting_HTML_Document/chapter_1000_section_3.html
-			// "Important: If you pass a relative URL in the HREF parameter, it must be relative to the currentlyloadedmovie, not relative to the current web page. If your movies are in a separate folder, specify URLs relative to the movies folder."
-			mHref = resolve_relative_uri (docURI, href);
-			g_free (href);
-		} else {
-			//FIXME see above
-			mHref = resolve_relative_uri (docURI, value);
-		}
+		width = strtol (value, NULL, 0);
 	}
-#endif /* TOTEM_NARROWSPACE_PLUGIN */
-
-	value = (char *) g_hash_table_lookup (args, "width");
+	value = (const char *) g_hash_table_lookup (args, "height");
 	if (value != NULL) {
-		/* FIXME! sanitize this value */
-		mWidth = strtol (value, NULL, 0);
+		height = strtol (value, NULL, 0);
 	}
+	
+	/* Are we hidden? */
+	mHidden = GetBooleanValue (args, "hidden", PR_FALSE);
 
-	value = (char *) g_hash_table_lookup (args, "height");
-	if (value != NULL) {
-		/* FIXME! sanitize this value */
-		mHeight = strtol (value, NULL, 0);
+	/* Most for RealAudio streams, but also used as a replacement for
+	 * HIDDEN=TRUE attribute.
+	 */
+	if (width == 0 && height == 0) {
+		mHidden = PR_TRUE;
 	}
 
-	//FIXME get the base URL here
+	/* Whether to automatically stream and play the content */
+	mAutostart = GetBooleanValue (args, "autoplay",
+				      GetBooleanValue (args, "autostart", mAutostart));
 
-	value = (char *) g_hash_table_lookup (args, "src");
-	if (value != NULL)
-		mSrc = resolve_relative_uri (docURI, value);
+	/* Whether to loop */
+	mRepeat = GetBooleanValue (args, "repeat",
+				   GetBooleanValue (args, "loop", PR_FALSE));
+
+	/* Now collect URI attributes */
+	const char *src = nsnull, *href = nsnull, *qtsrc = nsnull, *filename = nsnull;
+
+	src = (const char *) g_hash_table_lookup (args, "src");
 	/* DATA is only used in OBJECTs, see:
-	 * http://developer.mozilla.org/en/docs/Gecko_Plugin_API_Reference:Plug-in_Basics#Plug-in_Display_Modes */
-	if (mSrc == NULL) {
-		value = (char *) g_hash_table_lookup (args, "data");
-		if (value != NULL)
-			mSrc = resolve_relative_uri (docURI, value);
+	 * http://developer.mozilla.org/en/docs/Gecko_Plugin_API_Reference:Plug-in_Basics#Plug-in_Display_Modes
+	 */
+	/* FIXME: this is unnecessary, since gecko will automatically a synthetic
+ 	 * "src" attribute with the "data" atttribute's content if "src" is missing,
+ 	 * see http://lxr.mozilla.org/seamonkey/source/layout/generic/nsObjectFrame.cpp#2479
+ 	 */
+	if (!src) {
+		src = (const char *) g_hash_table_lookup (args, "data");
+	}
+	if (src) {
+		SetSrc (nsDependentCString (src));
 	}
 
-	/* Those parameters might replace the current src */
 #ifdef TOTEM_GMP_PLUGIN
 	/* http://windowssdk.msdn.microsoft.com/en-us/library/aa392440(VS.80).aspx */
-	value = (char *) g_hash_table_lookup (args, "filename");
-	if (value == NULL)
-		value = (char *) g_hash_table_lookup (args, "url");
-#elif TOTEM_NARROWSPACE_PLUGIN
+	filename = (const char *) g_hash_table_lookup (args, "filename");
+	if (!filename)
+		filename = (const char *) g_hash_table_lookup (args, "url");
+	if (filename) {
+		SetURL (nsDependentCString (filename));
+	}
+#endif /* TOTEM_GMP_PLUGIN */
+
+#ifdef TOTEM_NARROWSPACE_PLUGIN
+	/* Target */
+	value = (const char *) g_hash_table_lookup (args, "target");
+	if (value) {
+		mTarget.Assign (value);
+	}
+
+	href = (const char *) g_hash_table_lookup (args, "href");
+	if (href) {
+		SetHref (nsDependentCString (href));
+	}
+
 	/* http://developer.apple.com/documentation/QuickTime/QT6WhatsNew/Chap1/chapter_1_section_13.html */
-	value = (char *) g_hash_table_lookup (args, "qtsrc");
-#elif TOTEM_MULLY_PLUGIN
+	qtsrc = (const char *) g_hash_table_lookup (args, "qtsrc");
+	if (qtsrc) {
+		SetQtsrc (nsDependentCString (qtsrc));
+	}
+#endif /* TOTEM_NARROWSPACE_PLUGIN */
+
+#if 0 //def TOTEM_MULLY_PLUGIN
 	/* Click to play behaviour of the DivX plugin */
-	value = (char *) g_hash_table_lookup (args, "previewimage");
+	char *previewimage = (const char *) g_hash_table_lookup (args, "previewimage");
 	if (value != NULL)
 		mHref = g_strdup (mSrc);
-#else
-	value = NULL;
-#endif
-	if (value != NULL) {
-		if (mSrc == NULL || strcmp (mSrc, value) != 0) {
-			//FIXME need to cancel SRC if there's one
-			g_free (mSrc);
-			mSrc = resolve_relative_uri (docURI, value);
-			need_req = TRUE;
-		}
+#endif /* TOTEM_NARROWSPACE_PLUGIN */
+
+	/* If we're set to start automatically, we'll use the src stream */
+	if (mRequestURI &&
+	    mRequestURI == mSrcURI) {
+		mExpectingStream = mAutostart;
 	}
 
-#ifdef TOTEM_NARROWSPACE_PLUGIN
 	/* Caching behaviour */
-	mCache = totem_get_boolean_value (args, "cache", FALSE);
-	/* Target */
-	value = (char *) g_hash_table_lookup (args, "target");
-	if (value != NULL)
-		mTarget = g_strdup (value);
-#endif /* TOTEM_NARROWSPACE_PLUGIN */
+#ifdef TOTEM_NARROWSPACE_PLUGIN
+	if (strcmp (mimetype, "video/quicktime") != 0) {
+		mCache = PR_TRUE;
+	}
 
-	/* Whether the controls are all hidden, MSIE parameter
-	 * http://www.htmlcodetutorial.com/embeddedobjects/_EMBED_CONTROLLER.html */
-	gboolean controller;
-	controller = totem_get_boolean_value (args, "controller", TRUE);
-	mControllerHidden = (controller == FALSE);
+	mCache = GetBooleanValue (args, "cache", mCache);
+#endif /* TOTEM_NARROWSPACE_PLUGIN */
 
-	//FIXME add Netscape controls support
-	// http://www.htmlcodetutorial.com/embeddedobjects/_EMBED_CONTROLS.html
+#ifdef TOTEM_COMPLEX_PLUGIN
+	value = (const char *) g_hash_table_lookup (args, "console");
+	if (value) {
+		rv = SetConsole (nsDependentCString (value));
+		if (NS_FAILED (rv))
+			return NPERR_GENERIC_ERROR;
+	}
+
+	const char *kControls[] = {
+		"All",
+		"ControlPanel",
+		"FFCtrl",
+		"HomeCtrl",
+		"ImageWindow",
+		"InfoPanel",
+		"InfoVolumePanel",
+		"MuteCtrl",
+		"MuteVolume",
+		"PauseButton",
+		"PlayButton",
+		"PlayOnlyButton",
+		"PositionField",
+		"PositionSlider",
+		"RWCtrl",
+		"StatusBar",
+		"StatusField",
+		"StopButton",
+		"TACCtrl",
+		"VolumeSlider",
+	};
+	PRUint32 control = GetEnumIndex (args, "controls",
+				         kControls, G_N_ELEMENTS (kControls),
+					 0);
+	mControls = kControls[control];
 
-	/* Are we hidden? */
-	mHidden = totem_get_boolean_value (args, "hidden", FALSE);
+#endif /* TOTEM_COMPLEX_PLUGIN */
 
 #ifdef TOTEM_GMP_PLUGIN
 	/* uimode is either invisible, none, mini, or full
@@ -750,23 +1736,24 @@ totemPlugin::Init (NPMIMEType mimetype,
 	value = (char *) g_hash_table_lookup (args, "uimode");
 	if (value != NULL) {
 		if (g_ascii_strcasecmp (value, "none") == 0) {
-			mControllerHidden = TRUE;
+			mControllerHidden = PR_TRUE;
 		} else if (g_ascii_strcasecmp (value, "invisible") == 0) {
-			mHidden = TRUE;
+			mHidden = PR_TRUE;
 		} else if (g_ascii_strcasecmp (value, "full") == 0) {
-			mStatusbar = TRUE;
+			mShowStatusbar = PR_TRUE;
 		} else if (g_ascii_strcasecmp (value, "mini") == 0) {
 			;
 		}
 	}
+
+	/* Whether the controls are all hidden, MSIE parameter
+	 * http://www.htmlcodetutorial.com/embeddedobjects/_EMBED_CONTROLLER.html */
 	/* ShowXXX parameters as per http://support.microsoft.com/kb/285154 */
-	controller = totem_get_boolean_value (args, "showcontrols", TRUE);
-	if (mControllerHidden == FALSE)
-		mControllerHidden = (controller == FALSE);
-
-	gboolean statusbar;
-	statusbar = totem_get_boolean_value (args, "showstatusbar", mStatusbar);
-	mStatusbar = (statusbar == FALSE);
+	mControllerHidden = !GetBooleanValue (args, "controller",
+					      GetBooleanValue (args, "showcontrols", PR_TRUE));
+
+	mShowStatusbar = GetBooleanValue (args, "showstatusbar", mShowStatusbar);
+
 	//FIXME add showdisplay
 	// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmp6sdk/htm/userinterfaceelements.asp
 #endif /* TOTEM_GMP_PLUGIN */
@@ -774,105 +1761,135 @@ totemPlugin::Init (NPMIMEType mimetype,
 	/* Whether to NOT autostart */
 	//FIXME Doesn't handle playcount, or loop with numbers
 	// http://www.htmlcodetutorial.com/embeddedobjects/_EMBED_LOOP.html
-	gboolean autostart;
-	autostart = totem_get_boolean_value (args, "autostart", TRUE);
-	autostart = totem_get_boolean_value (args, "autoplay", autostart);
-	mNoAutostart = (autostart == FALSE);
-
-	/* Whether to loop */
-	mRepeat = totem_get_boolean_value (args, "loop", FALSE);
-	mRepeat = totem_get_boolean_value (args, "repeat", mRepeat);
-
-	g_message ("mSrc: %s", mSrc);
-	g_message ("mHref: %s", mHref);
-	g_message ("mHeight: %d mWidth: %d", mHeight, mWidth);
-	g_message ("mCache: %d", mCache);
-	g_message ("mTarget: %s", mTarget);
-	g_message ("mControllerHidden: %d", mControllerHidden);
-	g_message ("mStatusbar: %d", mStatusbar);
-	g_message ("mHidden: %d", mHidden);
-	g_message ("mNoAutostart: %d mRepeat: %d", mNoAutostart, mRepeat);
 
 	//FIXME handle starttime and endtime
 	// http://www.htmlcodetutorial.com/embeddedobjects/_EMBED_STARTTIME.html
 
-	g_hash_table_destroy (args);
-	NS_IF_RELEASE (docURI);
-
-	mIsSupportedSrc = IsSchemeSupported (mSrc);
+	/* Dump some disagnostics */
+	D ("mSrc: %s", mSrc.get ());
+	D ("mCache: %d", mCache);
+	D ("mControllerHidden: %d", mControllerHidden);
+	D ("mShowStatusbar: %d", mShowStatusbar);
+	D ("mHidden: %d", mHidden);
+	D ("mAutostart: %d, mRepeat: %d", mAutostart, mRepeat);
+#ifdef TOTEM_NARROWSPACE_PLUGIN
+	D ("mHref: %s", mHref.get ());
+	D ("mTarget: %s", mTarget.get ());
+#endif /* TOTEM_NARROWSPACE_PLUGIN */
+#ifdef TOTEM_COMPLEX_PLUGIN
+	D ("mConsole: %s", mConsole.get ());
+	D ("mControls: %s", mControls.get ());
+#endif /* TOTEM_COMPLEX_PLUGIN */
 
-	/* If filename is used, we need to request the stream ourselves */
-	if (need_req != FALSE) {
-		if (mIsSupportedSrc != FALSE) {
-			CallNPN_GetURLProc(sMozillaFuncs.geturl,
-					mInstance, mSrc, NULL);
-		}
-	}
+	g_hash_table_destroy (args);
 
-	return NPERR_NO_ERROR;
+	return ViewerFork ();
 }
 
 NPError
 totemPlugin::SetWindow (NPWindow *window)
 {
-	D("SetWindow [%p]", (void*) this);
+	if (mHidden && window->window != 0) {
+		D("SetWindow: hidden, can't set window");
+		return NPERR_GENERIC_ERROR;
+	}
 
-	if (mWindow) {
-		D ("existing window");
-		if (mWindow == (Window) window->window) {
-			D("resize");
-			/* Resize event */
-			/* Not currently handled */
-		} else {
-			D("change");
-			printf ("ack.  window changed!\n");
-		}
-	} else {
-		/* If not, wait for the first bits of data to come,
-		 * but not when the stream type isn't supported */
+	if (mWindow != 0 &&
+	    mWindow == (Window) window->window) {
+		mWidth = window->width;
+		mHeight = window->height;
+		DD ("Window resized or moved, now %dx%d", mWidth, mHeight);
+	} else if (mWindow == 0) {
 		mWindow = (Window) window->window;
-		if (mStream && mIsSupportedSrc) {
-			if (!Fork ())
-				return NPERR_GENERIC_ERROR;
-		/* If it's not a supported stream, we need
-		 * to launch now */
-		} else if (!mIsSupportedSrc) {
-			if (!Fork ())
-				return NPERR_GENERIC_ERROR;
-		} else {
-			D("waiting for data to come");
-		}
-	}
 
-	D("leaving plugin_set_window");
+		mWidth = window->width;
+		mHeight = window->height;
+
+		D ("Initial window set, XID %x size %dx%d",
+		   (guint) (Window) window->window, mWidth, mHeight);
+
+		ViewerSetWindow ();
+	} else {
+		D ("Setting a new window != mWindow, this is unsupported!");
+	}
 
 	return NPERR_NO_ERROR;
 }
 
 NPError
 totemPlugin::NewStream (NPMIMEType type,
-			NPStream* stream_ptr,
+			NPStream* stream,
 			NPBool seekable,
 			uint16* stype)
 {
-	D("plugin_new_stream");
+	if (!stream || !stream->url)
+		return NPERR_GENERIC_ERROR;
+
+	D ("NewStream mimetype '%s' URL '%s'", (const char *) type, stream->url);
 
 	/* We already have a live stream */
 	if (mStream) {
-		D("plugin_new_stream exiting, already have a live stream");
-		return NPERR_GENERIC_ERROR;
+		D ("Already have a live stream, aborting stream");
+
+		/* We don't just return NPERR_GENERIC_ERROR (or any other error code),
+		 * since, using gecko trunk (1.9), this causes the plugin to be destroyed,
+		 * if this is the automatic |src| stream. Same for the other calls below.
+		 */
+		return CallNPN_DestroyStreamProc (sNPN.destroystream,
+						  mInstance,
+						  stream,
+						  NPRES_DONE);
+	}
+
+	/* Either:
+	 * - this is the automatic first stream from the |src| or |data| attribute,
+	 *   but we want to request a different URL, or
+	 * - Gecko sometimes sends us 2 stream, and if the first is already in cache we'll
+	 *   be done it before it starts the 2nd time so the "if (mStream)" check above
+	 *   doesn't catch always this.
+	 */
+	if (!mExpectingStream) {
+		D ("Not expecting a new stream; aborting stream");
+
+		return CallNPN_DestroyStreamProc (sNPN.destroystream,
+						  mInstance,
+						  stream,
+						  NPRES_DONE);
+	}
+
+	/* This was an expected stream, no more expected */
+	mExpectingStream = PR_FALSE;
+
+#if 1 // #if 0
+	// This is fixed now _except_ the "if (!mViewerReady)" problem in StreamAsFile
+
+	/* For now, we'll re-request the stream when the viewer is ready.
+	 * As an optimisation, we could either just allow small (< ~128ko) streams
+	 * (which are likely to be playlists!), or any stream and cache them to disk
+	 * until the viewer is ready.
+	 */
+	if (!mViewerReady) {
+		D ("Viewer not ready, aborting stream");
+
+		return CallNPN_DestroyStreamProc (sNPN.destroystream,
+						  mInstance,
+						  stream,
+						  NPRES_DONE);
 	}
+#endif
 
-	D("plugin_new_stream type: %s url: %s", type, mSrc);
+	if (!IsMimeTypeSupported (type, stream->url)) {
+		D ("Unsupported mimetype, aborting stream");
 
-	if (IsMimeTypeSupported (type, mSrc) == FALSE) {
-		D("plugin_new_stream type: %s not supported, exiting\n", type);
-		return NPERR_INVALID_PLUGIN_ERROR;
+		return CallNPN_DestroyStreamProc (sNPN.destroystream,
+						  mInstance,
+						  stream,
+						  NPRES_DONE);
 	}
 
-	//FIXME need to find better semantics?
-	//what about saving the state, do we get confused?
-	if (g_str_has_prefix (mSrc, "file://")) {
+	/* FIXME: assign the stream URL to mRequestURI ? */
+
+	if (g_str_has_prefix (stream->url, "file://")) {
 		*stype = NP_ASFILEONLY;
 		mStreamType = NP_ASFILEONLY;
 	} else {
@@ -880,7 +1897,13 @@ totemPlugin::NewStream (NPMIMEType type,
 		mStreamType = NP_ASFILE;
 	}
 
-	mStream = stream_ptr;
+	mStream = stream;
+
+	mCheckedForPlaylist = PR_FALSE;
+	mIsPlaylist = PR_FALSE;
+
+	/* To track how many data we get from ::Write */
+	mBytesStreamed = 0;
 
 	return NPERR_NO_ERROR;
 }
@@ -889,35 +1912,46 @@ NPError
 totemPlugin::DestroyStream (NPStream* stream,
 			    NPError reason)
 {
-	D("plugin_destroy_stream, reason: %d", reason);
+	if (!mStream || mStream != stream)
+		return NPERR_GENERIC_ERROR;
 
-	if (mSendFD >= 0) {
-		close(mSendFD);
-		mSendFD = -1;
-	}
+	D ("DestroyStream reason %d", reason);
 
 	mStream = nsnull;
 
+#if 0
+	/* Close the viewer */
+	dbus_g_proxy_call_no_reply (mViewerProxy,
+				    "CloseStream",
+				    G_TYPE_INVALID,
+				    G_TYPE_INVALID);
+#endif
+
 	return NPERR_NO_ERROR;
 }
 
 int32
 totemPlugin::WriteReady (NPStream *stream)
 {
-	//D("plugin_write_ready");
+	/* FIXME this could probably be an assertion instead */
+	if (!mStream || mStream != stream)
+		return -1;
 
-	if (mIsSupportedSrc == FALSE)
-		return 0; /* FIXME not -1 ? */
+	/* Suspend the request until the viewer is ready;
+	 * we'll wake up in 100ms for another try.
+	 */
+	if (!mViewerReady)
+		return 0;
 
-	if (mSendFD < 0)
-		return (PLUGIN_STREAM_CHUNK_SIZE);
+	DD ("WriteReady");
 
 	struct pollfd fds;
 	fds.events = POLLOUT;
-	fds.fd = mSendFD;
+	fds.fd = mViewerFD;
 	if (poll (&fds, 1, 0) > 0)
 		return (PLUGIN_STREAM_CHUNK_SIZE);
 
+	/* suspend the request, we'll wake up in 100ms for another try */
 	return 0;
 }
 
@@ -927,59 +1961,56 @@ totemPlugin::Write (NPStream *stream,
 		    int32 len,
 		    void *buffer)
 {
-	int ret;
+	/* FIXME this could probably be an assertion instead */
+	if (!mStream || mStream != stream)
+		return -1;
 
-	//D("plugin_write");
+	DD ("Write offset %d len %d", offset, len);
 
-	/* We already know it's a playlist, don't try to check it again
-	 * and just wait for it to be on-disk */
-	if (mIsPlaylist != FALSE)
+	/* We already know it's a playlist, just wait for it to be on-disk. */
+	if (mIsPlaylist)
 		return len;
 
-	if (!mPlayerPID) {
-		if (!mStream) {
-			g_warning ("No stream in NPP_Write!?");
-			return -1;
-		}
-
-		mTriedWrite = TRUE;
+	/* Check for playlist.
+	 * Ideally we'd just always forward the data to the viewer and the viewer
+	 * always parse the playlist itself, but that's not yet implemented.
+	 */
+	/* FIXME we can only look at the current buffer, not at all the data so far.
+	 * So we can only do this at the start of the stream.
+	 */
+	if (!mCheckedForPlaylist) {
+		NS_ASSERTION (offset == 0, "Not checked for playlist but not at the start of the stream!?");
+
+		mCheckedForPlaylist = PR_TRUE;
+
+		if (totem_pl_parser_can_parse_from_data ((const char *) buffer, len, TRUE /* FIXME */)) {
+			D ("Is playlist; need to wait for the file to be downloaded completely");
+			mIsPlaylist = PR_TRUE;
 
-		/* FIXME this looks wrong since it'll look at the current data buffer,
-		 * not the cumulative data since the stream started 
-		 */
-		if (totem_pl_parser_can_parse_from_data ((const char *) buffer, len, TRUE /* FIXME */) != FALSE) {
-			D("Need to wait for the file to be downloaded completely");
-			mIsPlaylist = TRUE;
 			return len;
 		}
-
-		if (!Fork ())
-			return -1;
 	}
 
-	if (mSendFD < 0)
-		return -1;
-
-	if (mIsSupportedSrc == FALSE)
-		return -1;
+	int ret = write (mViewerFD, buffer, len);
+	/* FIXME shouldn't we retry if errno is EINTR ? */
 
-	ret = write (mSendFD, buffer, len);
-	if (ret < 0) {
-		/* FIXME what's this for? gecko will destroy the stream automatically if we return -1 */
+	if (NS_UNLIKELY (ret < 0)) {
 		int err = errno;
-		D("ret %d: [%d]%s", ret, errno, g_strerror (err));
+		D ("Write failed with errno %d: %s", err, g_strerror (err));
+
 		if (err == EPIPE) {
 			/* fd://0 got closed, probably because the backend
-			 * crashed on us */
-			if (CallNPN_DestroyStreamProc
-					(sMozillaFuncs.destroystream,
-					 mInstance,
-					 mStream,
-					 NPRES_DONE) != NPERR_NO_ERROR) {
+			 * crashed on us. Destroy the stream.
+			 */
+			if (CallNPN_DestroyStreamProc (sNPN.destroystream,
+			    			       mInstance,
+						       mStream,
+						       NPRES_DONE) != NPERR_NO_ERROR) {
 				g_warning ("Couldn't destroy the stream");
 			}
 		}
-		/* FIXME shouldn't we set ret=0 here? otherwise the stream will be destroyed */
+	} else /* ret >= 0 */ {
+		mBytesStreamed += ret;
 	}
 
 	return ret;
@@ -989,46 +2020,107 @@ void
 totemPlugin::StreamAsFile (NPStream *stream,
 			   const char* fname)
 {
-	NS_ASSERTION (stream == mStream, "Unknown stream");
+	if (!mStream || mStream != stream)
+		return;
 
-	D("plugin_stream_as_file: %s", fname);
+	D ("StreamAsFile filename '%s'", fname);
 
-	if (!mTriedWrite) {
+	/* FIXME: this reads the whole file into memory... try to
+	 * find a way to avoid it.
+	 */
+	if (!mCheckedForPlaylist) {
 		mIsPlaylist = totem_pl_parser_can_parse_from_filename
-			(fname, TRUE);
+				(fname, TRUE) != FALSE;
 	}
 
-	if (!mPlayerPID && mIsPlaylist) {
-		mLocal = g_filename_to_uri (fname, NULL, NULL);
-		Fork ();
+	/* FIXME! This happens when we're using the automatic |src| stream and
+	 * it finishes before we're ready.
+	 */
+	if (!mViewerReady) {
 		return;
-	} else if (!mPlayerPID) {
-		if (!Fork ())
-			return;
 	}
 
-	if (mIsPlaylist != FALSE)
-		return;
+	NS_ASSERTION (mViewerProxy, "No viewer proxy");
+	NS_ASSERTION (mViewerReady, "Viewer not ready");
 
-	GError *err = NULL;
-	if (!dbus_g_proxy_call (mProxy, "SetLocalFile", &err,
-			G_TYPE_STRING, fname, G_TYPE_INVALID,
-			G_TYPE_INVALID)) {
-		g_warning ("Error: %s", err->message);
-		g_error_free (err);
-	}
+	NS_ENSURE_TRUE (mRequestBaseURI && mRequestURI, );
 
-	D("plugin_stream_as_file done");
+	nsCString baseSpec, spec;
+	mRequestBaseURI->GetSpec (baseSpec);
+	mRequestURI->GetSpec (spec);
+
+	/* FIXME: these calls need to be async!!
+	 * But the file may be unlinked as soon as we return from this
+	 * function... do we need to keep a link?
+	 */
+	gboolean retval = TRUE;
+	GError *error = NULL;
+	if (mIsPlaylist) {
+		retval = dbus_g_proxy_call (mViewerProxy,
+					    "SetPlaylist",
+					    &error,
+					    G_TYPE_STRING, fname,
+					    G_TYPE_STRING, spec.get (),
+					    G_TYPE_STRING, baseSpec.get (),
+					    G_TYPE_INVALID,
+					    G_TYPE_INVALID);
+	}
+	/* Only call SetLocalFile if we haven't already streamed the file!
+	 * (It happens that we get no ::Write calls if the file is
+	 * completely in the cache.)
+	 */
+	else if (mBytesStreamed == 0) {
+		retval = dbus_g_proxy_call (mViewerProxy,
+					    "SetLocalFile",
+					    &error,
+					    G_TYPE_STRING, fname,
+					    G_TYPE_STRING, spec.get (),
+					    G_TYPE_STRING, baseSpec.get (),
+					    G_TYPE_INVALID,
+					    G_TYPE_INVALID);
+	}
+
+	if (!retval) {
+		g_warning ("Viewer error: %s", error->message);
+		g_error_free (error);
+	}
+}
+    
+void
+totemPlugin::URLNotify (const char *url,
+		        NPReason reason,
+		        void *notifyData)
+{
+	D ("URLNotify URL '%s' reason %d", url ? url : "", reason);
+
+	/* If we get called when we expect a stream,
+	 * it means that the stream failed.
+	 */
+	if (reason == NPRES_NETWORK_ERR &&
+	    mExpectingStream) {
+		/* FIXME: set/show error */
+		mExpectingStream = PR_FALSE;
+	}
 }
 
 NPError
 totemPlugin::GetScriptable (void *_retval)
 {
-	D ("GetScriptable [%p], mInstance %p", (void*) this, mInstance);
+	D ("GetScriptable [%p]", (void*) this);
 
-	/* FIXME this shouldn't happen, but seems to happen nevertheless?!? */
-	if (!mScriptable)
-		return NPERR_INVALID_PLUGIN_ERROR;
+#ifdef TOTEM_COMPLEX_PLUGIN
+	if (mConsoleClassRepresentant) {
+		return mConsoleClassRepresentant->GetScriptable (_retval);
+	}
+#endif /* TOTEM_COMPLEX_PLUGIN */
+
+	if (!mScriptable) {
+		mScriptable = new totemScriptablePlugin (this);
+		if (!mScriptable)
+			return NPERR_OUT_OF_MEMORY_ERROR;
+
+		NS_ADDREF (mScriptable);
+	}
 
 	nsresult rv = mScriptable->QueryInterface (NS_GET_IID (nsISupports),
 						   NS_REINTERPRET_CAST (void **, _retval));
Index: browser-plugin/totemPlugin.h
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemPlugin.h,v
retrieving revision 1.9
diff -p -u -B -r1.9 totemPlugin.h
--- browser-plugin/totemPlugin.h	30 Oct 2006 23:47:50 -0000	1.9
+++ browser-plugin/totemPlugin.h	12 Nov 2006 20:08:30 -0000
@@ -1,7 +1,8 @@
-/* Totem Mozilla plugin
+/* Totem browser plugin
  *
  * Copyright (C) 2004 Bastien Nocera <hadess@hadess.net>
  * Copyright (C) 2002 David A. Schleef <ds@schleef.org>
+ * Copyright © 2006 Christian Persch
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -24,9 +25,21 @@
 
 #include <stdint.h>
 #include <dbus/dbus-glib.h>
-#include "npapi.h"
-
+#include <npapi.h>
+#include <nsStringAPI.h>
+#include <nsTArray.h>
+
+#include "totem-commands.h"
+
+class nsIDOMDocument;
+class nsIDOMElement;
+class nsIIOService;
+class nsIServiceManager;
+class nsITimer;
+class nsIURI;
+class nsVoidArray;
 class totemScriptablePlugin;
+struct _NPNetscapeFuncs;
 
 class totemPlugin {
   public:
@@ -35,9 +48,17 @@ class totemPlugin {
   
     void* operator new (size_t aSize) CPP_THROW_NEW;
 
-    nsresult Play ();
-    nsresult Stop ();
-    nsresult Pause ();
+    /* Interface to scriptable */
+
+    nsresult DoCommand (const char *aCommand);
+
+    nsresult SetSrc (const nsACString &aURL);
+
+    /* plugin glue */
+    static _NPNetscapeFuncs sNPN;
+
+    static NPError Initialise ();
+    static NPError Shutdown ();
 
     NPError Init (NPMIMEType mimetype,
                   uint16_t mode,
@@ -49,11 +70,11 @@ class totemPlugin {
     NPError SetWindow (NPWindow *aWindow);
   
     NPError NewStream (NPMIMEType type,
-                      NPStream* stream_ptr,
-                      NPBool seekable,
-                      uint16* stype);
+                       NPStream* stream_ptr,
+                       NPBool seekable,
+                       uint16* stype);
     NPError DestroyStream (NPStream* stream,
-                          NPError reason);
+                           NPError reason);
   
     int32 WriteReady (NPStream *stream);
     int32 Write (NPStream *stream,
@@ -61,64 +82,182 @@ class totemPlugin {
                 int32 len,
                 void *buffer);
     void StreamAsFile (NPStream *stream,
-                      const char* fname);
-  
+                       const char* fname);
+
+    void URLNotify (const char *url,
+		    NPReason reason,
+		    void *notifyData);
+
     NPError GetScriptable (void *_retval);
 
+  private:
+
     static void PR_CALLBACK NameOwnerChangedCallback (DBusGProxy *proxy,
 						      const char *svc,
 						      const char *old_owner,
 						      const char *new_owner,
-						      totemPlugin *plugin);
+						      void *aData);
 
-    static void PR_CALLBACK StopSendingDataCallback (DBusGProxy  *proxy,
-						     totemPlugin *plugin);
+    static void PR_CALLBACK ViewerForkTimeoutCallback (nsITimer *aTimer,
+						       void *aData);
 
-  private:
-
-    PRBool Fork ();
+    static void PR_CALLBACK ButtonPressCallback (DBusGProxy  *proxy,
+		    				 //guint aButton,
+						 //guint aTimestamp,
+					         void *aData);
+
+    static void PR_CALLBACK StopStreamCallback (DBusGProxy  *proxy,
+						void *aData);
+
+    static void PR_CALLBACK ViewerSetWindowCallback (DBusGProxy *aProxy,
+						     DBusGProxyCall *aCall,
+						     void *aData);
+    static void PR_CALLBACK ViewerOpenStreamCallback (DBusGProxy *aProxy,
+						      DBusGProxyCall *aCall,
+						      void *aData);
+    static void PR_CALLBACK ViewerOpenURICallback (DBusGProxy *aProxy,
+						   DBusGProxyCall *aCall,
+						   void *aData);
+
+    NPError ViewerFork ();
+    void ViewerSetup ();
+    void ViewerSetWindow ();
+    void ViewerReady ();
+    void ViewerCleanup ();
+    void ViewerButtonPressed ();
+
+    void NameOwnerChanged (const char *aName,
+			   const char *aOldOwner,
+			   const char *aNewOwner);
+
+    void ComputeRequest ();
+    void ClearRequest ();
+
+    void SetRequestURL (const nsACString &aURL,
+			PRBool aIsSrc = PR_FALSE);
+    void RequestStream ();
     void UnsetStream ();
 
-    PRBool IsMimeTypeSupported (const char *aMimeType, const char *aURL);
-    PRBool IsSchemeSupported (const char *aURL);
-    char *GetRealMimeType (const char *aMimeType);
+    PRBool IsMimeTypeSupported (const char *aMimeType,
+				const char *aURL);
+    PRBool IsSchemeSupported (nsIURI *aURI);
+    void GetRealMimeType (const char *aMimeType,
+			  nsACString &_retval);
+    PRBool ParseBoolean (const char *key,
+			 const char *value,
+			 PRBool default_val);
+    PRBool GetBooleanValue (GHashTable *args,
+			    const char *key,
+			    PRBool default_val);
+    PRUint32 GetEnumIndex (GHashTable *args,
+			   const char *key,
+			   const char *values[],
+			   PRUint32 n_values,
+			   PRUint32 default_value);
 
     NPP mInstance;
 
-    /* FIXME make this a nsCOMPtr */
+    /* FIXME make these use nsCOMPtr<> !! */
     totemScriptablePlugin *mScriptable;
+    nsIServiceManager *mServiceManager;
+    nsIIOService *mIOService;
+    nsIDOMElement *mPluginDOMElement;
+    nsITimer *mTimer;
+    nsIURI *mBaseURI;
+
+    nsIURI *mRequestBaseURI;
+    nsIURI *mRequestURI;
 
     NPStream *mStream;
-    char *mMimeType;
+    PRUint32 mBytesStreamed;
     PRUint8 mStreamType;
 
+    nsCString mMimeType;
+
+    nsCString mSrc;
+    nsIURI *mSrcURI;
+
     Window mWindow;
+    PRInt32 mWidth;
+    PRInt32 mHeight;
 
-    char *mSrc;
-    char *mLocal;
-    char *mHref;
-    char *mTarget;
-
-    int mWidth;
-    int mHeight;
-
-    DBusGConnection *mConn;
-    DBusGProxy *mProxy;
-    char *mWaitForSvc;
+    DBusGConnection *mBusConnection;
+    DBusGProxy *mBusProxy;
+    DBusGProxy *mViewerProxy;
+    DBusGProxyCall *mViewerPendingCall;
+    nsCString mViewerBusAddress;
+    nsCString mViewerServiceName;
+    int mViewerFD;
+    int mViewerPID;
+
+#ifdef TOTEM_GMP_PLUGIN
+  public:
+    nsresult SetURL (const nsACString &aURL);
+
+  private:
+    nsIURI *mURLURI;
+#endif
 
-    int mSendFD;
-    int mPlayerPID;
+#ifdef TOTEM_NARROWSPACE_PLUGIN
+  public:
+    nsresult SetQtsrc (const nsACString &aURL);
+    nsresult SetHref (const nsACString& aURL);
+
+  private:
+    nsIURI *mQtsrcURI;
+
+    nsCString mHref;
+    nsIURI *mHrefURI;
+
+    nsCString mTarget;
+#endif
+
+#ifdef TOTEM_COMPLEX_PLUGIN
+  public:
+
+    nsresult SetConsole (const nsACString &aConsole);
+
+  private:
+
+    totemPlugin* FindConsoleClassRepresentant ();
+    void TransferConsole ();
+
+    void UnownedViewerSetup ();
+    void UnownedViewerSetWindow ();
+    void UnownedViewerUnsetWindow ();
+
+    nsIDOMDocument *mPluginOwnerDocument;
+    nsCString mConsole;
+    nsCString mControls;
+
+    /* nsnull if we're the representant ourself */
+    totemPlugin *mConsoleClassRepresentant;
+
+    static nsTArray<totemPlugin*> *sPlugins;
+
+#endif /* TOTEM_COMPLEX_PLUGIN */
+
+  private:
 
+    PRUint32 mAutostart : 1;
     PRUint32 mCache : 1;
+    PRUint32 mCheckedForPlaylist : 1;
     PRUint32 mControllerHidden : 1;
-    PRUint32 mGotSvc : 1;
+    PRUint32 mExpectingStream : 1;
+    PRUint32 mHadStream : 1;
     PRUint32 mHidden : 1;
     PRUint32 mIsPlaylist : 1;
     PRUint32 mIsSupportedSrc : 1;
-    PRUint32 mNoAutostart : 1;
+    PRUint32 mNeedViewer : 1;
     PRUint32 mRepeat : 1;
-    PRUint32 mTriedWrite : 1;
-    PRUint32 mStatusbar : 1;
+    PRUint32 mRequestIsSrc : 1;
+    PRUint32 mShowStatusbar : 1;
+    PRUint32 mTimerRunning : 1;
+    PRUint32 mUnownedViewerSetUp : 1;
+    PRUint32 mViewerReady : 1;
+    PRUint32 mViewerSetUp : 1;
+    PRUint32 mWaitingForButtonPress : 1;
+    PRUint32 mWindowSet : 1;
 };
 
 typedef struct {
Index: browser-plugin/totemPluginGlue.cpp
===================================================================
RCS file: /cvs/gnome/totem/browser-plugin/totemPluginGlue.cpp,v
retrieving revision 1.3
diff -p -u -B -r1.3 totemPluginGlue.cpp
--- browser-plugin/totemPluginGlue.cpp	8 Oct 2006 15:13:12 -0000	1.3
+++ browser-plugin/totemPluginGlue.cpp	12 Nov 2006 20:08:30 -0000
@@ -37,7 +37,6 @@
 #include "totemPluginGlue.h"
 #include "totemPlugin.h"
 
-NPNetscapeFuncs sMozillaFuncs;
 static char *mime_list = NULL;
 
 static NPError
@@ -49,20 +48,10 @@ totem_plugin_new_instance (NPMIMEType mi
 			   char *argv[],
 			   NPSavedData *savedData)
 {
-	totemPlugin *plugin;
-
-	D("totem_plugin_new_instance");
-
 	if (!instance)
 		return NPERR_INVALID_INSTANCE_ERROR;
 
-	/* Make sure the plugin stays resident to avoid crashers when 
-	 * reloading the GObject types.
-	 */
-	CallNPN_SetValueProc (sMozillaFuncs.setvalue, instance,
-			      NPPVpluginKeepLibraryInMemory, NS_INT32_TO_PTR(PR_TRUE));
-
-	plugin = new totemPlugin (instance);
+	totemPlugin *plugin = new totemPlugin (instance);
 	if (!plugin)
 		return NPERR_OUT_OF_MEMORY_ERROR;
 
@@ -81,8 +70,6 @@ static NPError
 totem_plugin_destroy_instance (NPP instance,
 			       NPSavedData **save)
 {
-	D("plugin_destroy");
-
 	if (!instance)
 		return NPERR_INVALID_INSTANCE_ERROR;
 
@@ -198,14 +185,21 @@ totem_plugin_url_notify (NPP instance,
 			 NPReason reason,
 			 void* notifyData)
 {
-	D("plugin_url_notify");
+	if (!instance)
+		return;
+
+	totemPlugin *plugin = (totemPlugin *) instance->pdata;
+	if (!plugin)
+		return;
+
+	plugin->URLNotify (url, reason, notifyData);
 }
 
 static void
 totem_plugin_print (NPP instance,
                     NPPrint* platformPrint)
 {
-	D("plugin_print");
+	D ("Print");
 }
 
 static char *
@@ -223,7 +217,7 @@ totem_plugin_get_value (NPP instance,
 	NPError err = NPERR_NO_ERROR;
 
 	/* See NPPVariable in npapi.h */
-	D("plugin_get_value %d (%x)\n", variable, variable);
+	D ("GetValue variable %d (%x)", variable, variable);
 
 	if (instance) {
 		plugin = (totemPlugin *) instance->pdata;
@@ -240,7 +234,7 @@ totem_plugin_get_value (NPP instance,
 		*((NPBool *)value) = TRUE;
 		break;
 	case NPPVpluginScriptableIID: {
-		nsIID* ptr = NS_STATIC_CAST (nsIID *, sMozillaFuncs.memalloc (sizeof (nsIID)));
+		nsIID* ptr = NS_STATIC_CAST (nsIID *, totemPlugin::sNPN.memalloc (sizeof (nsIID)));
 		if (ptr) {
 			*ptr = NS_GET_IID (nsISupports);
 			*NS_STATIC_CAST (nsIID **, value) = ptr;
@@ -259,7 +253,7 @@ totem_plugin_get_value (NPP instance,
 		break;
 	}
 	default:
-		D("unhandled variable %d (%x)", variable, variable);
+		D ("Unhandled variable");
 		err = NPERR_INVALID_PARAM;
 		break;
 	}
@@ -272,7 +266,7 @@ totem_plugin_set_value (NPP instance,
 			NPNVariable variable,
 			void *value)
 {
-	D("plugin_set_value %d (%x)", variable, variable);
+	D ("SetValue variable %d (%x)", variable, variable);
 
 	return NPERR_NO_ERROR;
 }
@@ -332,7 +326,7 @@ NP_Initialize (NPNetscapeFuncs * aMozill
 	NPBool supportsXEmbed = PR_FALSE;
 	NPNToolkitType toolkit = (NPNToolkitType) 0;
 
-	D("NP_Initialize\n");
+	D ("NP_Initialize");
 
 	/* Do we support XEMBED? */
 	err = CallNPN_GetValueProc (aMozillaFuncs->getvalue, NULL,
@@ -354,6 +348,7 @@ NP_Initialize (NPNetscapeFuncs * aMozill
 
 	if ((aMozillaFuncs->version >> 8) > NP_VERSION_MAJOR)
 		return NPERR_INCOMPATIBLE_VERSION_ERROR;
+	/* FIXME: check instead: indexof (last known entry in NPNetscapeFuncs) */
 	if (aMozillaFuncs->size < sizeof (NPNetscapeFuncs))
 		return NPERR_INVALID_FUNCTABLE_ERROR;
 	if (plugin_funcs->size < sizeof (NPPluginFuncs))
@@ -366,29 +361,29 @@ NP_Initialize (NPNetscapeFuncs * aMozill
 	 * structure, because the Mozilla function table could actually be
 	 * bigger than what we expect.
 	 */
-	sMozillaFuncs.size             = aMozillaFuncs->size;
-	sMozillaFuncs.version          = aMozillaFuncs->version;
-	sMozillaFuncs.geturl           = aMozillaFuncs->geturl;
-	sMozillaFuncs.posturl          = aMozillaFuncs->posturl;
-	sMozillaFuncs.requestread      = aMozillaFuncs->requestread;
-	sMozillaFuncs.newstream        = aMozillaFuncs->newstream;
-	sMozillaFuncs.write            = aMozillaFuncs->write;
-	sMozillaFuncs.destroystream    = aMozillaFuncs->destroystream;
-	sMozillaFuncs.status           = aMozillaFuncs->status;
-	sMozillaFuncs.uagent           = aMozillaFuncs->uagent;
-	sMozillaFuncs.memalloc         = aMozillaFuncs->memalloc;
-	sMozillaFuncs.memfree          = aMozillaFuncs->memfree;
-	sMozillaFuncs.memflush         = aMozillaFuncs->memflush;
-	sMozillaFuncs.reloadplugins    = aMozillaFuncs->reloadplugins;
-	sMozillaFuncs.getJavaEnv       = aMozillaFuncs->getJavaEnv;
-	sMozillaFuncs.getJavaPeer      = aMozillaFuncs->getJavaPeer;
-	sMozillaFuncs.geturlnotify     = aMozillaFuncs->geturlnotify;
-	sMozillaFuncs.posturlnotify    = aMozillaFuncs->posturlnotify;
-	sMozillaFuncs.getvalue         = aMozillaFuncs->getvalue;
-	sMozillaFuncs.setvalue         = aMozillaFuncs->setvalue;
-	sMozillaFuncs.invalidaterect   = aMozillaFuncs->invalidaterect;
-	sMozillaFuncs.invalidateregion = aMozillaFuncs->invalidateregion;
-	sMozillaFuncs.forceredraw      = aMozillaFuncs->forceredraw;
+	totemPlugin::sNPN.size             = aMozillaFuncs->size;
+	totemPlugin::sNPN.version          = aMozillaFuncs->version;
+	totemPlugin::sNPN.geturl           = aMozillaFuncs->geturl;
+	totemPlugin::sNPN.posturl          = aMozillaFuncs->posturl;
+	totemPlugin::sNPN.requestread      = aMozillaFuncs->requestread;
+	totemPlugin::sNPN.newstream        = aMozillaFuncs->newstream;
+	totemPlugin::sNPN.write            = aMozillaFuncs->write;
+	totemPlugin::sNPN.destroystream    = aMozillaFuncs->destroystream;
+	totemPlugin::sNPN.status           = aMozillaFuncs->status;
+	totemPlugin::sNPN.uagent           = aMozillaFuncs->uagent;
+	totemPlugin::sNPN.memalloc         = aMozillaFuncs->memalloc;
+	totemPlugin::sNPN.memfree          = aMozillaFuncs->memfree;
+	totemPlugin::sNPN.memflush         = aMozillaFuncs->memflush;
+	totemPlugin::sNPN.reloadplugins    = aMozillaFuncs->reloadplugins;
+	totemPlugin::sNPN.getJavaEnv       = aMozillaFuncs->getJavaEnv;
+	totemPlugin::sNPN.getJavaPeer      = aMozillaFuncs->getJavaPeer;
+	totemPlugin::sNPN.geturlnotify     = aMozillaFuncs->geturlnotify;
+	totemPlugin::sNPN.posturlnotify    = aMozillaFuncs->posturlnotify;
+	totemPlugin::sNPN.getvalue         = aMozillaFuncs->getvalue;
+	totemPlugin::sNPN.setvalue         = aMozillaFuncs->setvalue;
+	totemPlugin::sNPN.invalidaterect   = aMozillaFuncs->invalidaterect;
+	totemPlugin::sNPN.invalidateregion = aMozillaFuncs->invalidateregion;
+	totemPlugin::sNPN.forceredraw      = aMozillaFuncs->forceredraw;
 
 	/*
 	 * Set up a plugin function table that Mozilla will use to call
@@ -422,14 +417,18 @@ NP_Initialize (NPNetscapeFuncs * aMozill
 	plugin_funcs->getvalue = NewNPP_GetValueProc(totem_plugin_get_value);
 	plugin_funcs->setvalue = NewNPP_SetValueProc(totem_plugin_set_value);
 
-	return NPERR_NO_ERROR;
+	D ("NP_Initialize succeeded");
+
+	return totemPlugin::Initialise ();
 }
 
 NPError
 NP_Shutdown(void)
 {
+	D ("NP_Shutdown");
+
 	g_free (mime_list);
 	mime_list = NULL;
 
-	return NPERR_NO_ERROR;
+	return totemPlugin::Shutdown ();
 }
