/*
 *  Copyright (C) 2000, 2001, 2002 Marco Pesenti Gritti
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "galeon-config.h"
#include "gtkmozembed.h"
#include "gul-string.h"
#include "gul-gobject-misc.h"
#include "galeon-embed-utils.h"
#include "galeon-embed.h"
#include "mozilla-embed.h"
#include "MozillaPrivate.h"
#include "GaleonWrapper.h"
#include "EventContext.h"
#include "galeon-debug.h"

#include <glib/gi18n.h>

#include <nsIURI.h>
#include <nsIURL.h>
#include <nsNetUtil.h>
#include <nsString.h>
#include <nsIRequest.h>
#include <nsIWebProgressListener.h>
#include <nsIWindowWatcher.h>
#include <nsIDOMStyleSheetList.h>
#include <nsIDOMStyleSheet.h>
#include <nsIDOMHTMLLinkElement.h>

#include <math.h>

#define WINDOWWATCHER_CONTRACTID "@mozilla.org/embedcomp/window-watcher;1"

static void
mozilla_embed_class_init (MozillaEmbedClass *klass);
static void
mozilla_embed_init (MozillaEmbed *gs);
static void
mozilla_embed_finalize (GObject *object);
static void
galeon_embed_init (GaleonEmbedClass *embed_class);

static void
impl_get_capabilities (GaleonEmbed *embed,
                       EmbedCapabilities *caps);
static gresult 
impl_load_url (GaleonEmbed *embed, 
               const char *url);
static gresult 
impl_stop_load (GaleonEmbed *embed);
static gresult 
impl_can_go_back (GaleonEmbed *embed);
static gresult 
impl_can_go_forward (GaleonEmbed *embed);
static gresult 
impl_can_go_up (GaleonEmbed *embed);
static gresult
impl_get_go_up_list (GaleonEmbed *embed, GSList **l);
static gresult 
impl_go_back (GaleonEmbed *embed);
static gresult  
impl_go_forward (GaleonEmbed *embed);
static gresult
impl_go_up (GaleonEmbed *embed);
static gresult 
impl_render_data (GaleonEmbed *embed, 
                  const char *data,
                  guint32 len,
                  const char *base_uri, 
                  const char *mime_type);
static gresult
impl_open_stream (GaleonEmbed *embed,
                  const char *base_uri,
                  const char *mime_type);
static gresult
impl_append_data (GaleonEmbed *embed,
                  const char *data, 
                  guint32 len);
static gresult
impl_close_stream (GaleonEmbed *embed);
static gresult
impl_get_title (GaleonEmbed *embed,
                char **title);
static gresult 
impl_get_location (GaleonEmbed *embed, 
                   gboolean toplevel,
                   gboolean requested,
                   char **location);
static gresult 
impl_reload (GaleonEmbed *embed, 
             EmbedReloadFlags flags);
static gresult
impl_copy_page (GaleonEmbed *dest,
		GaleonEmbed *source,
		EmbedDisplayType display_type);
static gresult 
impl_grab_focus (GaleonEmbed *embed);
static gresult
impl_get_link_tags (GaleonEmbed *embed,
                    const char *link_type,
                    GList **tags);
static gresult
impl_zoom_set (GaleonEmbed *embed, 
               int zoom, 
               gboolean reflow);
static gresult
impl_zoom_get (GaleonEmbed *embed,
               int *zoom);
static gresult 
impl_selection_can_cut (GaleonEmbed *embed);
static gresult 
impl_selection_can_copy (GaleonEmbed *embed);
static gresult 
impl_can_paste (GaleonEmbed *embed);
static gresult 
impl_select_all (GaleonEmbed *embed);
static gresult
impl_selection_cut (GaleonEmbed *embed);
static gresult
impl_selection_copy (GaleonEmbed *embed);
static gresult
impl_paste (GaleonEmbed *embed);
static gresult
impl_shistory_count  (GaleonEmbed *embed,
                      int *count);
static gresult
impl_shistory_get_nth (GaleonEmbed *embed, 
                       int nth,
                       gboolean is_relative,
                       char **url,
                       char **title);
static gresult
impl_shistory_get_pos (GaleonEmbed *embed,
                       int *pos);
static gresult
impl_shistory_go_nth (GaleonEmbed *embed, 
                      int nth);
static gboolean
impl_shistory_copy (GaleonEmbed *source,
		    GaleonEmbed *dest,
		    gboolean back_history,
		    gboolean forward_history,
		    gboolean set_current);

static gresult
impl_shistory_clear (GaleonEmbed *embed);

static gresult
impl_scroll (GaleonEmbed *embed, 
             EmbedScrollDirection direction);
static gresult
impl_fine_scroll (GaleonEmbed *embed,
		  int horiz, int vert);
static gresult
impl_get_security_level (GaleonEmbed *embed, 
                         EmbedSecurityLevel *level,
                         char **description);
static gresult 
impl_find_next (GaleonEmbed *embed, 
		gboolean backwards);

static gresult
impl_find_set_properties (GaleonEmbed *embed, 
                          char *search_string,
	                  gboolean case_sensitive,
			  gboolean wrap_around);

static gresult
impl_set_encoding (GaleonEmbed *embed,
		   const char *charset);

static gresult
impl_get_encoding_info (GaleonEmbed *embed,
			GaleonEncodingPageInfo **info);

static gresult
impl_has_modified_forms (GaleonEmbed *embed);

static gresult
impl_can_view_source (GaleonEmbed *embed);

static gresult
impl_print (GaleonEmbed *embed, 
            EmbedPrintInfo *info);

static gresult
impl_print_preview_close (GaleonEmbed *embed);

static gresult
impl_print_preview_num_pages (GaleonEmbed *embed,
			      gint *retNum);
static gresult
impl_print_preview_navigate (GaleonEmbed *embed,
			     EmbedPrintPreviewNavType navType,
			     gint pageNum);

static gresult
impl_get_page_properties(GaleonEmbed *embed,
			 EmbedPageProperties **retProperties);
static gresult
impl_show_page_certificate (GaleonEmbed *embed);
static gresult
impl_get_stylesheets(GaleonEmbed *embed,
		     GList **retSheets);
static gresult
impl_get_selected_stylesheet(GaleonEmbed *embed,
			     EmbedStyleSheet **retSheet);
static gresult
impl_set_stylesheet(GaleonEmbed *embed,
		    EmbedStyleSheet *sheet);
static gresult
impl_apply_user_stylesheet(GaleonEmbed *embed,
			   const gchar *sheetfile,
			   EmbedStyleSheet **retSheet);
static gresult
impl_remove_user_stylesheet(GaleonEmbed *embed,
			    EmbedStyleSheet *sheet);
static gboolean
stylesheet_is_alternate(nsIDOMStyleSheet *item);

static void 
mozilla_embed_connect_signals (MozillaEmbed *membed);
static char *
mozilla_embed_get_uri_parent (const char *uri);
static void
mozilla_embed_location_changed_cb (GtkMozEmbed *embed, MozillaEmbed *membed);
static void
mozilla_embed_title_changed_cb (GtkMozEmbed *embed, MozillaEmbed *membed);
static void
mozilla_embed_net_state_all_cb (GtkMozEmbed *embed, const char *aURI,
                                gint state, guint status, MozillaEmbed *membed);
static void
mozilla_embed_net_stop_cb (GtkMozEmbed *embed, MozillaEmbed *membed);
static void
mozilla_embed_net_start_cb (GtkMozEmbed *embed, MozillaEmbed *membed);
static void
mozilla_embed_progress_cb (GtkMozEmbed *embed, const char *aURI,
                           gint curprogress, gint maxprogress, MozillaEmbed *membed);
static void
mozilla_embed_link_message_cb (GtkMozEmbed *embed, MozillaEmbed *membed);
static void
mozilla_embed_js_status_cb (GtkMozEmbed *embed, MozillaEmbed *membed);
static void
mozilla_embed_visibility_cb (GtkMozEmbed *embed, gboolean visibility, MozillaEmbed *membed);
static void
mozilla_embed_destroy_brsr_cb (GtkMozEmbed *embed, MozillaEmbed *embed);
static gint
mozilla_embed_dom_mouse_click_cb (GtkMozEmbed *embed, gpointer dom_event, MozillaEmbed *membed);
static gint
mozilla_embed_dom_mouse_down_cb (GtkMozEmbed *embed, gpointer dom_event, 
				 MozillaEmbed *membed);
static void
mozilla_embed_size_to_cb (GtkMozEmbed *embed, gint width, gint height, MozillaEmbed *membed);
static void
mozilla_embed_new_window_cb (GtkMozEmbed *embed, 
			     GtkMozEmbed **newEmbed,
                             guint chromemask, MozillaEmbed *membed);

static void
mozilla_embed_security_change_cb (GtkMozEmbed *embed, 
				  gpointer request,
                                  guint state, MozillaEmbed *membed);

static gint
mozilla_embed_dom_key_down_cb (GtkMozEmbed *embed, gpointer dom_event,
		               MozillaEmbed *membed);

static EmbedSecurityLevel
mozilla_embed_security_level (MozillaEmbed *membed);

/* signals to connect on each embed widget */
static const struct
{ 
	char *event; 
        void *func; /* should be a GtkSignalFunc or similar */
}
signal_connections[] =
{
	{ "location",        (void *) mozilla_embed_location_changed_cb  },
        { "title",           (void *) mozilla_embed_title_changed_cb     },
        { "net_state_all",   (void *) mozilla_embed_net_state_all_cb     },
        { "progress_all",    (void *) mozilla_embed_progress_cb          },
        { "link_message",    (void *) mozilla_embed_link_message_cb      },
        { "js_status",       (void *) mozilla_embed_js_status_cb         },
        { "visibility",      (void *) mozilla_embed_visibility_cb        },
        { "destroy_browser", (void *) mozilla_embed_destroy_brsr_cb      },
        { "dom_mouse_click", (void *) mozilla_embed_dom_mouse_click_cb   },
	{ "dom_mouse_down",  (void *) mozilla_embed_dom_mouse_down_cb    },
        { "size_to",         (void *) mozilla_embed_size_to_cb           },
        { "new_window",      (void *) mozilla_embed_new_window_cb        },
        { "security_change", (void *) mozilla_embed_security_change_cb   },
	{ "dom_key_down",    (void *) mozilla_embed_dom_key_down_cb      },
	{ "net_stop",        (void *) mozilla_embed_net_stop_cb          },
	{ "net_start",       (void *) mozilla_embed_net_start_cb         },

        /* terminator -- must be last in the list! */
        { NULL, NULL } 
};

struct MozillaEmbedPrivate
{
	MozillaEmbedPrivate() : wrapper(NULL), security_state(-1), 
				loaded_url (FALSE), loading_url(0)
	{ /* nothing */ }

	GaleonWrapper *wrapper;
	gint security_state;

	/* LOAD HACK: When loading a site, either initially, or when
	 * loaded from "about:blank" remember the url that was
	 * attempted to load, and always return that for the
	 * get_location, and also fake it so that refresh loads that
	 * url
	 *
	 * Note that we only do this hack for the "about:blank" case
	 * when the first page was "about:blank"
	 */
	gboolean loaded_url; //!< Has a web page been loaded
	gchar *loading_url;  //!< The URL we are trying to load
};

static GObjectClass *parent_class = NULL;

MAKE_GET_TYPE_IFACE (mozilla_embed, "MozillaEmbed", MozillaEmbed,
		     mozilla_embed_class_init, mozilla_embed_init, GTK_TYPE_MOZ_EMBED,
		     galeon_embed_init, GALEON_EMBED_TYPE);

static void
galeon_embed_init (GaleonEmbedClass *embed_class)
{
	embed_class->get_capabilities = impl_get_capabilities;
	embed_class->load_url = impl_load_url; 
	embed_class->stop_load = impl_stop_load;
	embed_class->can_go_back = impl_can_go_back;
	embed_class->can_go_forward =impl_can_go_forward;
	embed_class->can_go_up = impl_can_go_up;
	embed_class->get_go_up_list = impl_get_go_up_list;
	embed_class->go_back = impl_go_back;
	embed_class->go_forward = impl_go_forward;
	embed_class->go_up = impl_go_up;
	embed_class->render_data = impl_render_data;
	embed_class->open_stream = impl_open_stream;
	embed_class->append_data = impl_append_data;
	embed_class->close_stream = impl_close_stream;
	embed_class->get_title = impl_get_title;
	embed_class->get_location = impl_get_location;
	embed_class->reload = impl_reload;
	embed_class->copy_page = impl_copy_page;
	embed_class->grab_focus = impl_grab_focus;
	embed_class->get_link_tags = impl_get_link_tags;
	embed_class->zoom_set = impl_zoom_set;
	embed_class->zoom_get = impl_zoom_get;
	embed_class->selection_can_cut = impl_selection_can_cut;
	embed_class->selection_can_copy = impl_selection_can_copy;
	embed_class->can_paste = impl_can_paste;
	embed_class->selection_cut = impl_selection_cut;
	embed_class->selection_copy = impl_selection_copy;
	embed_class->paste = impl_paste;
	embed_class->shistory_count = impl_shistory_count;
	embed_class->shistory_get_nth = impl_shistory_get_nth;
	embed_class->shistory_get_pos = impl_shistory_get_pos;
	embed_class->shistory_go_nth = impl_shistory_go_nth;
	embed_class->shistory_copy = impl_shistory_copy;
	embed_class->shistory_clear = impl_shistory_clear;
	embed_class->scroll = impl_scroll;
	embed_class->fine_scroll = impl_fine_scroll;
	embed_class->get_security_level = impl_get_security_level;
	embed_class->find_set_properties = impl_find_set_properties;
	embed_class->find_next = impl_find_next;
	embed_class->set_encoding = impl_set_encoding;
	embed_class->get_encoding_info = impl_get_encoding_info;
	embed_class->has_modified_forms = impl_has_modified_forms;
	embed_class->can_view_source = impl_can_view_source;
	embed_class->select_all = impl_select_all;
	embed_class->print = impl_print;
	embed_class->print_preview_close = impl_print_preview_close;
	embed_class->print_preview_num_pages = impl_print_preview_num_pages;
	embed_class->print_preview_navigate = impl_print_preview_navigate;
	embed_class->get_page_properties = impl_get_page_properties;
	embed_class->show_page_certificate = impl_show_page_certificate;
	embed_class->get_stylesheets = impl_get_stylesheets;
	embed_class->get_selected_stylesheet = impl_get_selected_stylesheet;
	embed_class->set_stylesheet = impl_set_stylesheet;
	embed_class->apply_user_stylesheet = impl_apply_user_stylesheet;
	embed_class->remove_user_stylesheet = impl_remove_user_stylesheet;
}

/****************************************************************
 * Start Dirty Mouse Wheel hack
 *
 * This hack is needed as the mozilla embed object doesn't emit a
 * zoom changed signal, and you can't seem to be able to capture
 * the mouse wheel movements in the parent widgets. This is also
 * abusing the fact that we know that gtkmozembed object is also
 * a GtkContainer containing the real widget.
 ****************************************************************/

static gint before_zoom_val = 0;

/**
 *  This function will get called before the actual scroll-event
 *  has been delivered to the widget, this allows us to get the current
 *  zoom value
 */
static gboolean
mozilla_embed_child_scroll_event_cb (GtkWidget    *widget, 
				     GdkEvent     *event,
				     MozillaEmbed *embed)
{
	galeon_embed_zoom_get (GALEON_EMBED (embed), &before_zoom_val);
	return FALSE;
}


/**
 *  This gets called after the scroll event has been delivered to the
 *  widget. We can get the new zoom value, and emit the appropriate
 *  signal if the zoom factor changed 
 */
static gboolean
mozilla_embed_child_event_after_cb (GtkWidget    *widget, 
				    GdkEvent     *event,
				    MozillaEmbed *embed)
{
	int zoom;
	if (event->type != GDK_SCROLL) return FALSE;

	if (galeon_embed_zoom_get (GALEON_EMBED (embed), &zoom) == G_OK &&
	    zoom != before_zoom_val)
	    
	{
		g_signal_emit_by_name (embed, "ge_zoom_change", zoom);
	}
	return FALSE;
}

/**
 *  Hack: We know that the parent class is really GtkBin object, so
 *  hook onto the "add" signal and connect appropriate signals for the
 *  child
 */
static void
mozilla_embed_add (GtkContainer *container, GtkWidget *widget)
{
	MozillaEmbed *embed = MOZILLA_EMBED (container);

	GTK_CONTAINER_CLASS(parent_class)->add (container, widget);

	g_signal_connect (widget, "scroll-event",
			  G_CALLBACK (mozilla_embed_child_scroll_event_cb), embed);

	g_signal_connect (widget, "event-after",
			  G_CALLBACK (mozilla_embed_child_event_after_cb), embed);
}


/**
 *  If the child is removed, remove the signals that we attached 
 */
static void
mozilla_embed_remove (GtkContainer *container, GtkWidget *widget)
{
	MozillaEmbed *embed = MOZILLA_EMBED (container);

	g_signal_handlers_disconnect_by_func 
		(widget,
		 (gpointer)mozilla_embed_child_scroll_event_cb,
		 embed);

	g_signal_handlers_disconnect_by_func 
		(widget,
		 (gpointer)mozilla_embed_child_event_after_cb,
		 embed);

	GTK_CONTAINER_CLASS(parent_class)->remove (container, widget);
}

/*************************************************************
 * End dirty mouse wheel zoom hack
 *************************************************************/

static void
mozilla_embed_realize (GtkWidget *widget)
{
	MozillaEmbedPrivate *mpriv = MOZILLA_EMBED (widget)->priv;

	GTK_WIDGET_CLASS(parent_class)->realize (widget);

	nsresult result;
	result = mpriv->wrapper->Init (GTK_MOZ_EMBED (widget));

	if (NS_FAILED(result))
	{
               	g_warning ("Browser initialization failed");
	}
}

static void
mozilla_embed_class_init (MozillaEmbedClass *klass)
{
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
	GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
     	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); 

	parent_class = (GObjectClass *) g_type_class_peek_parent (klass);
	
	container_class->add    = mozilla_embed_add;
	container_class->remove = mozilla_embed_remove;
        object_class->finalize = mozilla_embed_finalize;
	widget_class->realize = mozilla_embed_realize;
}

static void
mozilla_embed_init (MozillaEmbed *embed)
{
        embed->priv          = new MozillaEmbedPrivate;
	embed->priv->wrapper = new GaleonWrapper ();

	LOG ("MozillaEmbed ctor (%p)", embed);

	mozilla_embed_connect_signals (embed);
}

static void 
mozilla_embed_connect_signals (MozillaEmbed *embed)
{
        gint i;
	GaleonEmbed *gembed;

	gembed = GALEON_EMBED (embed);
	       
        /* connect signals */
        for (i = 0; signal_connections[i].event != NULL; i++)
        {
                g_signal_connect_object (G_OBJECT(embed),
					 signal_connections[i].event,
					 G_CALLBACK(signal_connections[i].func), 
					 embed, (GConnectFlags)0);
        }
}

static void
mozilla_embed_finalize (GObject *object)
{
        MozillaEmbed *embed;

        g_return_if_fail (object != NULL);
        g_return_if_fail (IS_MOZILLA_EMBED (object));

	embed = MOZILLA_EMBED (object);
	
	g_return_if_fail (embed->priv != NULL);
	
	if (embed->priv->wrapper)
	{
		embed->priv->wrapper->Destroy();
        	delete embed->priv->wrapper;
        	embed->priv->wrapper = NULL;
	}

	g_free (embed->priv->loading_url);

	delete embed->priv;
	
        G_OBJECT_CLASS (parent_class)->finalize (object);

	LOG ("MozillaEmbed dtor (%p)", embed);
}

gpointer
mozilla_embed_get_galeon_wrapper (MozillaEmbed *embed)
{
	return embed->priv->wrapper;
}

static void
impl_get_capabilities (GaleonEmbed *embed,
                       EmbedCapabilities *caps)
{
	EmbedCapabilities mozilla_caps;
	
	mozilla_caps = (EmbedCapabilities) ( 
		EMBED_CLIPBOARD_CAP |
	        EMBED_COOKIES_CAP |
	        EMBED_LINKS_CAP |
	        EMBED_ZOOM_CAP |
	        EMBED_PRINT_CAP |
	        EMBED_FIND_CAP |
	        EMBED_SCROLL_CAP |
		EMBED_FINE_SCROLL_CAP |
	        EMBED_SECURITY_CAP |
	        EMBED_CHARSET_CAP |
	        EMBED_SHISTORY_CAP );
	
	*caps = mozilla_caps;
}

static gresult 
impl_load_url (GaleonEmbed *embed, 
               const char *url)
{
	char *clean_url;

        clean_url = gul_string_remove_outside_whitespace (url);

        gtk_moz_embed_load_url (GTK_MOZ_EMBED(embed),
                                clean_url);

	/* LOAD HACK: If we haven't loaded a url yet, remember this one, unless it
	 * "about:blank", and flag that we are trying to load this url */
	MozillaEmbed *membed = MOZILLA_EMBED (embed);
	if (!membed->priv->loaded_url && strcmp ("about:blank", clean_url))
	{
		g_free (membed->priv->loading_url);
		membed->priv->loading_url = clean_url;

		/* Fake a "location" signal, so that the layer above picks up
		 * this url, and doesn't think there is no current url */
		g_signal_emit_by_name (membed, "ge_location");
		return G_OK;
	}

	g_free (clean_url);

	return G_OK;
}

static gresult 
impl_stop_load (GaleonEmbed *embed)
{
	gtk_moz_embed_stop_load (GTK_MOZ_EMBED(embed));	
	
	return G_OK;
}

static gresult 
impl_can_go_back (GaleonEmbed *embed)
{
	if (gtk_moz_embed_can_go_back (GTK_MOZ_EMBED(embed)))
	{
		return G_OK;
	}
	else
	{
		return G_FAILED;
	}
}

static gresult 
impl_can_go_forward (GaleonEmbed *embed)
{
	if (gtk_moz_embed_can_go_forward (GTK_MOZ_EMBED(embed)))
	{
		return G_OK;
	}
	else
	{
		return G_FAILED;
	}

}

static gresult 
impl_can_go_up (GaleonEmbed *embed)
{
	char *location;
	char *s;
	gresult result;

	if (galeon_embed_get_location (embed, TRUE, FALSE, &location) != G_OK)
		return G_FAILED;
	g_return_val_if_fail (location != NULL, G_FAILED);
	if ((s = mozilla_embed_get_uri_parent (location)) != NULL)
	{
		g_free (s);
		result = G_OK;
	}				
	else
	{
		result = G_FAILED;
	}

	g_free (location);

	return result;
}

static gresult
impl_get_go_up_list (GaleonEmbed *embed, GSList **l)
{
	char *location;
	char *s;
	
	if (galeon_embed_get_location (embed, TRUE, FALSE, &location) != G_OK)
		return G_FAILED;
	g_return_val_if_fail (location != NULL, G_FAILED);
	
	*l = NULL;
	s = location;
	while ((s = mozilla_embed_get_uri_parent (s)) != NULL)
	{
		*l = g_slist_prepend (*l, s);
	}				

	g_free (location);
	*l = g_slist_reverse (*l);

	return G_OK;
}

static gresult 
impl_go_back (GaleonEmbed *embed)
{
	gtk_moz_embed_go_back (GTK_MOZ_EMBED(embed));

	return G_OK;
}
		
static gresult  
impl_go_forward (GaleonEmbed *embed)
{
	gtk_moz_embed_go_forward (GTK_MOZ_EMBED(embed));

	return G_OK;
}

static gresult
impl_go_up (GaleonEmbed *embed)
{
	char *uri;
	char *parent_uri;
	
	if (galeon_embed_get_location (embed, TRUE, FALSE, &uri) != G_OK)
		return G_FAILED;
	g_return_val_if_fail (uri != NULL, G_FAILED);
	
	parent_uri = mozilla_embed_get_uri_parent (uri);
	g_return_val_if_fail (parent_uri != NULL, G_FAILED);
	
	galeon_embed_load_url (embed, parent_uri);
	
	g_free (parent_uri);
	
	return G_OK;
}

static char *
mozilla_embed_get_uri_parent (const char *aUri)
{
        nsresult rv;

        nsCOMPtr<nsIURI> uri;
        rv = NS_NewURI (getter_AddRefs(uri), aUri);
        if (NS_FAILED(rv) || !uri) return NULL;

	/* Don't support going 'up' with chrome url's, mozilla handily
	 * fixes them up for us, so it doesn't work properly, see
	 *    rdf/chrome/src/nsChromeProtocolHandler.cpp::NewURI()
	 * (the Canonify() call) */
	nsCAutoString scheme;
	rv = uri->GetScheme (scheme);
	if (NS_FAILED(rv) || !scheme.Length()) return NULL;
	if (scheme == NS_LITERAL_CSTRING ("chrome")) return NULL;

        nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
        if (NS_FAILED(rv) || !url) return NULL;

        nsCAutoString dirPath;
        rv = url->GetDirectory (dirPath);
        if (NS_FAILED(rv) || !dirPath.Length()) return NULL;

        nsCAutoString filePath;
        rv = url->GetFilePath (filePath);
        if (NS_FAILED(rv) || !filePath.Length()) return NULL;

        PRInt32 pathLength = filePath.Length();
        PRInt32 trailingSlash = filePath.RFind("/");

        if(pathLength < 2 || trailingSlash == -1)
        {
                return NULL;
        }

        if(trailingSlash != (pathLength-1))
        {
                uri->SetPath(dirPath);
        }
        else
        {
                PRInt32 nextSlash = filePath.RFind("/",PR_FALSE,trailingSlash-1);
                nsCAutoString parentPath;
                filePath.Left(parentPath, nextSlash);
                uri->SetPath(parentPath);
        }

        nsCAutoString spec;
        uri->GetSpec(spec);

        return !spec.IsEmpty() ? g_strdup(spec.get()) : NULL;
}

static gresult
impl_render_data (GaleonEmbed *embed, 
                  const char *data,
                  guint32 len,
                  const char *base_uri, 
                  const char *mime_type)
{
	gtk_moz_embed_render_data (GTK_MOZ_EMBED(embed),
				   data,
				   len,
				   base_uri,
				   mime_type);
	
	return G_OK;
}

static gresult
impl_open_stream (GaleonEmbed *embed,
                  const char *base_uri,
                  const char *mime_type)
{
	gtk_moz_embed_open_stream (GTK_MOZ_EMBED(embed),
				   base_uri, mime_type);
	
	return G_OK;
}

static gresult
impl_append_data (GaleonEmbed *embed,
                  const char *data, 
                  guint32 len)
{
	gtk_moz_embed_append_data (GTK_MOZ_EMBED(embed),
				   data, len);
	
	return G_OK;
}

static gresult
impl_close_stream (GaleonEmbed *embed)
{
	gtk_moz_embed_close_stream (GTK_MOZ_EMBED(embed));
	
	return G_OK;
}

static gresult
impl_get_title (GaleonEmbed *embed,
                char **title)
{
	*title = gtk_moz_embed_get_title (GTK_MOZ_EMBED(embed));
	
	return G_OK;
}

/* This can be used to get the real location of the currently
 * loaded page. It bypasses gtkmozembed, and the loading_page
 * hack, to get the actual location mozilla thinks it has loaded
 */
char *
mozilla_embed_get_real_location (MozillaEmbed *embed)
{
	nsCAutoString url;
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult rv = wrapper->GetRealURL (url);
	if (NS_FAILED (rv)) return 0;

	return (!url.IsEmpty()) ? g_strdup (url.get()) : NULL;
}

static gresult 
impl_get_location (GaleonEmbed *embed, 
                   gboolean toplevel,
                   gboolean requested,
                   char **location)
{
	char *l;
	nsresult rv;
	nsCAutoString url;
	GaleonWrapper *wrapper;

	wrapper = MOZILLA_EMBED(embed)->priv->wrapper;

	/* FIXME !toplevel requested not implemented */
	
	if (toplevel)
	{
		l = gtk_moz_embed_get_location 
			(GTK_MOZ_EMBED(embed));
	}
	else if (!toplevel)
	{
		rv = wrapper->GetDocumentUrl (url);
		l = (NS_SUCCEEDED (rv) && !url.IsEmpty()) ?
		     g_strdup (url.get()) : NULL;	   	
	}
	else if (requested)
	{
		l = mozilla_embed_get_real_location (MOZILLA_EMBED (embed));
   	
	}

	/* LOAD HACK: If we are trying to load a url, and we didn't
	 * manage it (the location is still "about:blank"), return the
	 * attempted url to the caller, so that they know the right thing */
	MozillaEmbed *membed = MOZILLA_EMBED (embed);
	if (!membed->priv->loaded_url && membed->priv->loading_url && l &&
	    !strcmp ("about:blank", l))
	{
		g_free (l);
		l = g_strdup (membed->priv->loading_url);
	}

	*location = l;
	
	return ( l == NULL ) ? G_FAILED : G_OK;
}

static gresult 
impl_reload (GaleonEmbed *embed, 
             EmbedReloadFlags flags)
{
	char *location;

	if ((flags & EMBED_RELOAD_FRAME))
	{
		GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;

		nsresult rv = wrapper->ReloadDocument();
		return NS_SUCCEEDED(rv) ? G_OK : G_FAILED;
	}

	guint32 mflags = GTK_MOZ_EMBED_FLAG_RELOADNORMAL;
	
	MozillaEmbed *membed = MOZILLA_EMBED (embed);
	location = mozilla_embed_get_real_location (membed);
	if (!location) return G_FAILED;

	/* LOAD HACK: If we are loading a url, but it didn't load
	 * properly, then load the url that was originally requested */
	if (!membed->priv->loaded_url && membed->priv->loading_url &&
	    !strcmp (location,"about:blank"))
	{
		galeon_embed_load_url (embed, membed->priv->loading_url);
		g_free (location);
		return G_OK;
	} 

	/* NOTE, bypassing the proxy just tells the proxy not to read from
	 * its cache, it doesn't actually talk to the server directly */

	/* If the page is a view-source: url, we need to force the reload */
	if (location && g_str_has_prefix (location, "view-source:"))
	{
		mflags = GTK_MOZ_EMBED_FLAG_RELOADBYPASSPROXYANDCACHE;
		LOG ("Forcing bypass cache and proxy for view-source:");
	}
	else if (flags & EMBED_RELOAD_FORCE)
	{
		mflags = GTK_MOZ_EMBED_FLAG_RELOADBYPASSPROXYANDCACHE;
		LOG ("Should bypass cache and proxy");
	}
	
	g_free (location);

	gtk_moz_embed_reload (GTK_MOZ_EMBED(embed), mflags);
	
	return G_OK;
}

static gresult
impl_copy_page (GaleonEmbed *dest,
		GaleonEmbed *source,
		EmbedDisplayType display_type)
{
	GaleonWrapper *dWrapper = MOZILLA_EMBED(dest)->priv->wrapper;;
	GaleonWrapper *sWrapper = MOZILLA_EMBED(source)->priv->wrapper;;

        nsresult rv;

        nsCOMPtr<nsISupports> pageDescriptor;
        rv = sWrapper->GetPageDescriptor(getter_AddRefs(pageDescriptor));
        if (!pageDescriptor || NS_FAILED(rv)) return G_FAILED;

        rv = dWrapper->LoadDocument(pageDescriptor, static_cast<PRUint32>(display_type));
        if (NS_FAILED(rv)) return G_FAILED;

	/* LOAD HACK: Ensure that we remember the url that we are loading 
	 * (if the source is in the middle of loading the url */
	if (!MOZILLA_EMBED (dest)->priv->loaded_url)
	{
		char *url;
		if (galeon_embed_get_location (source, FALSE, TRUE, &url) == G_OK)
		{
			MOZILLA_EMBED (dest)->priv->loading_url = url;

			/* Fake a "location" signal, so that the layer above picks up
			 * this url, and doesn't think there is no current url */
			g_signal_emit_by_name (dest, "ge_location");
		}
	}

        return G_OK;
}

static gresult 
impl_grab_focus (GaleonEmbed *embed)
{
	gtk_widget_grab_focus (GTK_BIN (embed)->child);
	
	return G_OK;
}

static gresult
impl_get_link_tags (GaleonEmbed *embed,
                    const char *link_type,
                    GList **tags)
{
	return G_NOT_IMPLEMENTED;
}

static gresult
impl_zoom_set (GaleonEmbed *embed, 
               int zoom, 
               gboolean reflow)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult result;

	result = wrapper->SetZoom ((float)(zoom) / 100, reflow);

	if (NS_SUCCEEDED (result))
	{
		g_signal_emit_by_name (embed, "ge_zoom_change", zoom);
	}

	return NS_SUCCEEDED(result) ? G_OK : G_FAILED;
}

static gresult
impl_zoom_get (GaleonEmbed *embed,
               int *zoom)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	float f;

	nsresult result = wrapper->GetZoom (&f);
	if (NS_FAILED (result)) return G_FAILED;

	*zoom = (int) rint (f * 100);

	return G_OK;
}

static gresult 
impl_selection_can_cut (GaleonEmbed *embed)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	gboolean result;
	
	wrapper->CanCutSelection (&result);
	
	return result ? G_OK : G_FAILED;
}

static gresult 
impl_selection_can_copy (GaleonEmbed *embed)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	gboolean result;
	
	wrapper->CanCopySelection (&result);
	
	return result ? G_OK : G_FAILED;
}

static gresult 
impl_can_paste (GaleonEmbed *embed)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	gboolean result;
	
	wrapper->CanPaste (&result);
	
	return result ? G_OK : G_FAILED;
}

static gresult 
impl_select_all (GaleonEmbed *embed)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult result;
	
	result = wrapper->SelectAll ();
	
	return result ? G_OK : G_FAILED;
}

static gresult
impl_selection_cut (GaleonEmbed *embed)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult rv;
	
	rv = wrapper->CutSelection ();
	
	return NS_SUCCEEDED(rv) ? G_OK : G_FAILED;
}

static gresult
impl_selection_copy (GaleonEmbed *embed)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult rv;
	
	rv = wrapper->CopySelection ();
	
	return NS_SUCCEEDED(rv) ? G_OK : G_FAILED;
}

static gresult
impl_paste (GaleonEmbed *embed)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult rv;
	
	rv = wrapper->Paste ();
	
	return NS_SUCCEEDED(rv) ? G_OK : G_FAILED;
}

static gresult
impl_shistory_count  (GaleonEmbed *embed,
                      int *count)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult rv;
	int c, index;
	
	rv = wrapper->GetSHInfo (&c, &index);

	*count = c;
	
	return NS_SUCCEEDED(rv) ? G_OK : G_FAILED;
}

static gresult
impl_shistory_get_nth (GaleonEmbed *embed, 
                       int nth,
                       gboolean is_relative,
                       char **aUrl,
                       char **aTitle)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult rv;

	if (is_relative)
	{
		int pos;

		if (galeon_embed_shistory_get_pos 
		    (GALEON_EMBED(embed), &pos) == G_OK)
		{
			nth += pos;
		}
		else
		{
			return G_FAILED;
		}
	}
	
        nsCAutoString url;
        rv = wrapper->GetSHUrlAtIndex(nth, url);
        *aUrl = (NS_SUCCEEDED (rv) && !url.IsEmpty()) ? g_strdup(url.get()) : NULL;

	PRUnichar *title;
	rv = wrapper->GetSHTitleAtIndex(nth, &title);
	*aTitle = g_strdup (NS_ConvertUCS2toUTF8(title).get());
	nsMemory::Free(title);

	return G_OK;
}

static gresult
impl_shistory_get_pos (GaleonEmbed *embed,
                       int *pos)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult rv;
	int count, index;
	
	rv = wrapper->GetSHInfo (&count, &index);

	*pos = index;
	
	return NS_SUCCEEDED(rv) ? G_OK : G_FAILED;
}

static gresult
impl_shistory_go_nth (GaleonEmbed *embed, 
                      int nth)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult rv;
	
	rv = wrapper->GoToHistoryIndex (nth);

	return NS_SUCCEEDED(rv) ? G_OK : G_FAILED;
}

static gboolean
impl_shistory_copy (GaleonEmbed *source,
		    GaleonEmbed *dest,
		    gboolean back_history,
		    gboolean forward_history,
		    gboolean set_current)
{
	nsresult rv;
	GaleonWrapper *s_wrapper = MOZILLA_EMBED(source)->priv->wrapper;
	GaleonWrapper *d_wrapper = MOZILLA_EMBED(dest)->priv->wrapper;
	
	rv = s_wrapper->CopyHistoryTo (d_wrapper, back_history, forward_history, set_current);

	return NS_SUCCEEDED(rv) ? G_OK : G_FAILED;
}

static gresult
impl_shistory_clear (GaleonEmbed *embed)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult rv;
	
	rv = wrapper->ClearHistory();

	return NS_SUCCEEDED(rv) ? G_OK : G_FAILED;
}


static gresult
impl_scroll (GaleonEmbed *embed, 
             EmbedScrollDirection direction)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;

	switch (direction)
	{
	case EMBED_SCROLL_UP:
		wrapper->ScrollUp ();
		break;
	case EMBED_SCROLL_DOWN:
		wrapper->ScrollDown ();
		break;
	case EMBED_SCROLL_LEFT:
		wrapper->ScrollLeft ();
		break;
	case EMBED_SCROLL_RIGHT:
		wrapper->ScrollRight ();
		break;
	}
	
	return G_OK;
}

static gresult
impl_fine_scroll (GaleonEmbed *embed, int horiz, int vert)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;

	wrapper->FineScroll (horiz, vert);

	return G_OK;
}

static gresult
impl_get_security_level (GaleonEmbed *embed, 
                         EmbedSecurityLevel *level,
                         char **description)
{
	nsresult result;

	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	result = wrapper->GetSecurityTooltip (description);
        if (NS_FAILED (result)) return G_FAILED;

	*level = mozilla_embed_security_level (MOZILLA_EMBED (embed));
	return G_OK;
}

static gresult
impl_print (GaleonEmbed *embed,
            EmbedPrintInfo *info)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult result;

        nsCOMPtr<nsIPrintSettings> options;
        result = wrapper->GetPrintSettings(getter_AddRefs(options));
	if (NS_FAILED (result)) return G_FAILED;

	MozillaCollatePrintSettings(info, options);

        options->SetPrintSilent (PR_TRUE);

	result = wrapper->Print(options, info->preview);

	options->SetPrintSilent (PR_FALSE);

	return NS_SUCCEEDED (result) ? G_OK : G_FAILED;
}

static gresult
impl_print_preview_close (GaleonEmbed *embed)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult result;

	result = wrapper->PrintPreviewClose();
	return NS_SUCCEEDED(result) ? G_OK : G_FAILED;
}

static gresult
impl_print_preview_num_pages (GaleonEmbed *embed,
			      gint *retNum)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult result;

	result = wrapper->PrintPreviewNumPages(retNum);
	return NS_SUCCEEDED(result) ? G_OK : G_FAILED;
}

static gresult
impl_print_preview_navigate (GaleonEmbed *embed,
			     EmbedPrintPreviewNavType navType,
			     gint pageNum)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult result;

	result = wrapper->PrintPreviewNavigate(navType, pageNum);
	return NS_SUCCEEDED(result) ? G_OK : G_FAILED;
}

static gresult
impl_find_next (GaleonEmbed *embed, 
                gboolean backwards)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult result;
        PRBool didFind;

        result = wrapper->Find (backwards, &didFind);
	
	return didFind ? G_OK : G_FAILED;
}


static gresult
impl_find_set_properties (GaleonEmbed *embed, 
                          char *search_string,
	                  gboolean case_sensitive,
			  gboolean wrap_around)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult result;

        result = wrapper->FindSetProperties
		((NS_ConvertUTF8toUCS2(search_string)).get(),
		 case_sensitive, wrap_around);
	
	return NS_SUCCEEDED (result) ? G_OK : G_FAILED;
}

static gresult
impl_set_encoding (GaleonEmbed *embed,
		   const char *charset)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	nsresult rv;

	rv = wrapper->ForceEncoding (charset);
	if (NS_FAILED (rv)) return G_FAILED;

	gtk_moz_embed_reload (GTK_MOZ_EMBED (embed),
			      GTK_MOZ_EMBED_FLAG_RELOADCHARSETCHANGE);

	return G_OK;
}

static gresult
impl_get_encoding_info (GaleonEmbed *embed,
			GaleonEncodingPageInfo **info)
{
	MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
	nsresult result;

	*info = NULL;
	result = mpriv->wrapper->GetEncodingInfo (info);

	if (NS_FAILED (result))
	{
		galeon_encoding_page_info_free (*info);
		return G_FAILED;
	}

	return G_OK;
}


static gresult
impl_has_modified_forms (GaleonEmbed *embed)
{
	MozillaEmbedPrivate *mpriv = MOZILLA_EMBED(embed)->priv;
	nsresult result;

	PRBool modified;
	result = mpriv->wrapper->GetHasModifiedForms (&modified);

	return modified == PR_TRUE ? G_OK : G_FAILED;
}

static gresult
impl_can_view_source (GaleonEmbed *embed)
{
	char *location;
	gresult ret = G_OK;

	if (galeon_embed_get_location (embed, TRUE, FALSE, &location) != G_OK)
		return G_FAILED;
	g_return_val_if_fail (location != NULL, G_FAILED);

	if (g_str_has_prefix (location, "view-source:") ||
	    g_str_has_prefix (location, "wyciwyg:"))
	{
		ret = G_FAILED;
	}

	g_free (location);

	return ret;
}


static void
mozilla_embed_location_changed_cb (GtkMozEmbed *embed, 
				   MozillaEmbed *membed)
{
	g_signal_emit_by_name (membed, "ge_location");
}

static void
mozilla_embed_title_changed_cb (GtkMozEmbed *embed, 
				MozillaEmbed *membed)
{
	g_return_if_fail (IS_MOZILLA_EMBED (membed));
	g_return_if_fail (GTK_IS_WIDGET (embed));
	g_signal_emit_by_name (membed, "ge_title");
}

static void
mozilla_embed_net_state_all_cb (GtkMozEmbed *embed, const char *aURI,
                                gint state, guint status, 
				MozillaEmbed *membed)
{
	EmbedState estate = EMBED_STATE_UNKNOWN;
	int i;
	
	struct
	{
		guint state;
		EmbedState embed_state;
	}
	conversion_map [] =
	{
		{ GTK_MOZ_EMBED_FLAG_START, EMBED_STATE_START },
		{ GTK_MOZ_EMBED_FLAG_STOP, EMBED_STATE_STOP },
		{ GTK_MOZ_EMBED_FLAG_REDIRECTING, EMBED_STATE_REDIRECTING },
		{ GTK_MOZ_EMBED_FLAG_TRANSFERRING, EMBED_STATE_TRANSFERRING },
		{ GTK_MOZ_EMBED_FLAG_NEGOTIATING, EMBED_STATE_NEGOTIATING },
		{ GTK_MOZ_EMBED_FLAG_IS_REQUEST, EMBED_STATE_IS_REQUEST },
		{ GTK_MOZ_EMBED_FLAG_IS_DOCUMENT, EMBED_STATE_IS_DOCUMENT },
		{ GTK_MOZ_EMBED_FLAG_IS_NETWORK, EMBED_STATE_IS_NETWORK },
		{ 0, EMBED_STATE_UNKNOWN }
	};

	for (i = 0; conversion_map[i].state != 0; i++)
	{
		if (state & conversion_map[i].state)
		{
			estate = (EmbedState) (estate | conversion_map[i].embed_state);	
		}
	}
	
	g_signal_emit_by_name (membed, "ge_net_state", aURI, estate);
}

static void
mozilla_embed_net_stop_cb (GtkMozEmbed *embed, MozillaEmbed *membed)
{
	/* LOAD HACK: Check to see if a real url is currently loaded.
	 * If it is, then forget the "loading_url", and mark the embed
	 * as having had a url loaded */
	if (!membed->priv->loaded_url)
	{
		char *l = mozilla_embed_get_real_location (MOZILLA_EMBED(embed));
		if (strcmp ("about:blank", l))
		{
			/* We have loaded a real page */
			g_free (membed->priv->loading_url);
			membed->priv->loading_url = 0;
			membed->priv->loaded_url = TRUE;
		}
		g_free (l);
	}
	g_signal_emit_by_name (membed, "ge_net_stop" );

}

static void
mozilla_embed_net_start_cb (GtkMozEmbed *embed, MozillaEmbed *membed)
{
	g_signal_emit_by_name (membed, "ge_net_start" );

}

static void
mozilla_embed_progress_cb (GtkMozEmbed *embed, const char *aURI,
                           gint curprogress, gint maxprogress, 
			   MozillaEmbed *membed)
{
	g_signal_emit_by_name (membed, "ge_progress", aURI, 
			       curprogress, maxprogress);
}

static void
mozilla_embed_link_message_cb (GtkMozEmbed *embed, 
			       MozillaEmbed *membed)
{
	char *msg = gtk_moz_embed_get_link_message (embed);

	g_signal_emit_by_name (membed, "ge_link_message", msg );

	g_free (msg);
}

static void
mozilla_embed_js_status_cb (GtkMozEmbed *embed, 
			    MozillaEmbed *membed)
{
	char *msg = gtk_moz_embed_get_js_status (embed);

	g_signal_emit_by_name (membed, "ge_js_status", msg);

	g_free (msg);
}

static void
mozilla_embed_visibility_cb (GtkMozEmbed *embed, gboolean visibility, 
			     MozillaEmbed *membed)
{
	g_signal_emit_by_name (membed, "ge_visibility", visibility); 

	GaleonWrapper *wrapper = MOZILLA_EMBED(membed)->priv->wrapper;

	nsresult rv;
	nsCOMPtr<nsIWindowWatcher> wwatch
		(do_GetService(WINDOWWATCHER_CONTRACTID, &rv));
	if (NS_FAILED(rv) || !wwatch) return;

	nsCOMPtr<nsIDOMWindow> domWindow;
	rv = wrapper->GetDOMWindow(getter_AddRefs(domWindow));
	if(NS_FAILED(rv) || !domWindow) return;

	rv = wwatch->SetActiveWindow(domWindow);
}

static void
mozilla_embed_destroy_brsr_cb (GtkMozEmbed *embed, 
			       MozillaEmbed *membed)
{
	g_signal_emit_by_name (membed, "ge_destroy_brsr"); 
}

static gint
mozilla_embed_dom_mouse_click_cb (GtkMozEmbed *embed, gpointer dom_event, 
				  MozillaEmbed *membed)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	GaleonEmbedEvent *info;
	gint return_value = 0;
	nsresult result;

	info = galeon_embed_event_new();
	
	EventContext ctx;
	ctx.Init(wrapper);
        result = ctx.GetMouseEventInfo(static_cast<nsIDOMMouseEvent*>(dom_event), info);
	if (NS_SUCCEEDED(result))
	{
		nsCOMPtr<nsIDOMDocument> domDoc;
		result = ctx.GetTargetDocument(getter_AddRefs(domDoc));
		if (NS_SUCCEEDED(result))
		{
			result = wrapper->PushTargetDocument(domDoc);
			if (NS_SUCCEEDED(result))
			{
				g_signal_emit_by_name(membed, "ge_dom_mouse_click", 
						      info, &return_value); 
				wrapper->PopTargetDocument();
			}
		}

	}

	g_object_unref(info);
	
	return return_value;
}

static gint
mozilla_embed_dom_mouse_down_cb (GtkMozEmbed *embed, gpointer dom_event, 
				 MozillaEmbed *membed)
{
	GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	GaleonEmbedEvent *info;
	gint return_value = 0;
	nsresult result;
	
	info = galeon_embed_event_new ();
	
	EventContext ctx;
	ctx.Init (wrapper);
        result = ctx.GetMouseEventInfo (static_cast<nsIDOMMouseEvent*>(dom_event), info);
	if (NS_SUCCEEDED(result))
	{
		nsCOMPtr<nsIDOMDocument> domDoc;
		result = ctx.GetTargetDocument (getter_AddRefs(domDoc));
		if (NS_SUCCEEDED(result))
		{
			result = wrapper->PushTargetDocument (domDoc);
			if (NS_SUCCEEDED(result))
			{
				g_signal_emit_by_name (membed, "ge_dom_mouse_down", 
						       info, &return_value); 
				wrapper->PopTargetDocument ();
			}
		}

	}
	
	g_object_unref (info);
	
	return return_value;
}

static gint
mozilla_embed_dom_key_down_cb (GtkMozEmbed *embed, gpointer dom_event,
		               MozillaEmbed *membed)
{
	nsCOMPtr<nsIDOMKeyEvent> ev = static_cast<nsIDOMKeyEvent*>(dom_event);
	if (!ev)
	{
		return FALSE;
	}

	GaleonWrapper *wrapper = MOZILLA_EMBED(membed)->priv->wrapper;

	GaleonEmbedEvent *info;
	info = galeon_embed_event_new ();

	gboolean ret = FALSE;

	// Just check for Shift-F10 and the Contect menu key
	// so that we know to popup the context menu.
	//
	// The DOM_VK_* keycodes are not compatible with the keycodes defined
	// in GDK, so making a generic dom_key_down signal is probably not
	// worth the trouble.

	nsresult rv;
	EventContext ctx;
	ctx.Init (wrapper);
	rv = ctx.GetKeyEventInfo (ev, info);
	if (NS_SUCCEEDED(rv) &&
	    ((info->keycode == nsIDOMKeyEvent::DOM_VK_F10 &&
	      info->modifier == GDK_SHIFT_MASK) 
#if MOZILLA_SNAPSHOT > 14
	     || (info->keycode == nsIDOMKeyEvent::DOM_VK_CONTEXT_MENU &&
		 !info->modifier)
#endif
		    ))
	{
		// Translate relative coordinates to absolute values, and try
		// to avoid covering links by adding a little offset.

		int x, y;
		gdk_window_get_origin (GTK_WIDGET(membed)->window, &x, &y);
		info->x += x + 6;	
		info->y += y + 6;

		nsCOMPtr<nsIDOMDocument> doc;
		rv = ctx.GetTargetDocument (getter_AddRefs(doc));
		if (NS_SUCCEEDED(rv))
		{
			rv = wrapper->PushTargetDocument (doc);
			if (NS_SUCCEEDED(rv))
			{
				g_signal_emit_by_name (membed, "ge_contextmenu", info);
				wrapper->PopTargetDocument ();

				// return TRUE to stop further event handling
				ret = TRUE;
			}
		}
	}

	g_object_unref (info);
	return ret;
}

static void
mozilla_embed_size_to_cb (GtkMozEmbed *embed, gint width, gint height, 
			  MozillaEmbed *membed)
{
	g_signal_emit_by_name (membed, "ge_size_to", width, height);
}

static void
mozilla_embed_new_window_cb (GtkMozEmbed *embed, 
			     GtkMozEmbed **newEmbed,
                             guint chromemask, 
			     MozillaEmbed *membed)
{
	int i;
	EmbedChromeMask mask = EMBED_CHROME_OPENASPOPUP;
	GaleonEmbed *new_embed = NULL;

	struct
	{
		guint chromemask;
		EmbedChromeMask embed_mask;
	}
	conversion_map [] =
	{
		{ GTK_MOZ_EMBED_FLAG_DEFAULTCHROME, EMBED_CHROME_DEFAULT },
		{ GTK_MOZ_EMBED_FLAG_MENUBARON, EMBED_CHROME_MENUBARON },
		{ GTK_MOZ_EMBED_FLAG_TOOLBARON, EMBED_CHROME_TOOLBARON },
		/* Map the bookmarks bar to the default, as otherwise _blank
		 * pages don't honour the gconf settings, and always have the
		 * bookmarks bar, which just confuses people (bug 122410) */
		{ GTK_MOZ_EMBED_FLAG_PERSONALTOOLBARON, EMBED_CHROME_DEFAULT },
		{ GTK_MOZ_EMBED_FLAG_STATUSBARON, EMBED_CHROME_STATUSBARON },
		{ GTK_MOZ_EMBED_FLAG_WINDOWRAISED, EMBED_CHROME_WINDOWRAISED },
		{ GTK_MOZ_EMBED_FLAG_WINDOWLOWERED, EMBED_CHROME_WINDOWLOWERED },
		{ GTK_MOZ_EMBED_FLAG_CENTERSCREEN, EMBED_CHROME_CENTERSCREEN },
		{ GTK_MOZ_EMBED_FLAG_OPENASDIALOG, EMBED_CHROME_OPENASDIALOG },
		{ GTK_MOZ_EMBED_FLAG_OPENASCHROME, EMBED_CHROME_OPENASCHROME },
		{ 0, EMBED_CHROME_NONE }
	};

	for (i = 0; conversion_map[i].chromemask != 0; i++)
	{
		if (chromemask & conversion_map[i].chromemask)
		{
			mask = (EmbedChromeMask) (mask | conversion_map[i].embed_mask);	
		}
	}
	
	g_signal_emit_by_name (membed, "ge_new_window", &new_embed, mask);

	g_assert (new_embed != NULL);
	
	gtk_moz_embed_set_chrome_mask (GTK_MOZ_EMBED(new_embed), chromemask);

	*newEmbed = GTK_MOZ_EMBED(new_embed);
}

static void
mozilla_embed_security_change_cb (GtkMozEmbed *embed, 
				  gpointer request,
                                  guint state, 
				  MozillaEmbed *membed)
{
	EmbedSecurityLevel level;
	nsresult result;

        nsCOMPtr<nsIChannel> channel;
	channel = do_QueryInterface ((nsIRequest*)request, 
				     &result);

	GaleonWrapper *wrapper = MOZILLA_EMBED(membed)->priv->wrapper;
	wrapper->SetChannel (channel);	

	membed->priv->security_state = state;
	level = mozilla_embed_security_level (membed);

	g_signal_emit_by_name (membed, "ge_security_change", level);
}

static EmbedSecurityLevel
mozilla_embed_security_level (MozillaEmbed *membed)
{
	EmbedSecurityLevel level;

	switch (membed->priv->security_state)
        {
        case nsIWebProgressListener::STATE_IS_INSECURE:
                level = STATE_IS_INSECURE;
                break;
        case nsIWebProgressListener::STATE_IS_BROKEN:
                level = STATE_IS_BROKEN;
                break;
        case nsIWebProgressListener::STATE_IS_SECURE|
             nsIWebProgressListener::STATE_SECURE_HIGH:
                level = STATE_IS_SECURE_HIGH;
                break;
        case nsIWebProgressListener::STATE_IS_SECURE|
             nsIWebProgressListener::STATE_SECURE_MED:
                level = STATE_IS_SECURE_MED;
                break;
        case nsIWebProgressListener::STATE_IS_SECURE|
             nsIWebProgressListener::STATE_SECURE_LOW:
                level = STATE_IS_SECURE_LOW;
                break;
        default:
                level = STATE_IS_UNKNOWN;
                break;
        }
	return level;
}

static gint
stylesheet_find_func (gconstpointer a, gconstpointer b)
{
	const EmbedStyleSheet *sheet = (const EmbedStyleSheet*)a;
	const char * name = (const char*)b;

	return strcmp (sheet->name, name);;
}

static gresult
impl_get_page_properties(GaleonEmbed *embed,
			 EmbedPageProperties **retProperties)
{
	nsresult result;

        GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;

	EmbedPageProperties *props = g_new0(EmbedPageProperties, 1);

	result = wrapper->GetPageProperties (props);

	(*retProperties) = props;

	return NS_FAILED (result) ? G_FAILED : G_OK;
}


static gresult
impl_show_page_certificate (GaleonEmbed *embed)
{
        GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	wrapper->ShowPageCertificate();
	return G_OK;
}

static gresult
impl_get_stylesheets(GaleonEmbed *embed,
		     GList **retSheets)
{
	nsresult result;

        GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;

	nsCOMPtr<nsIDOMStyleSheetList> list;
	result = wrapper->GetStyleSheets(getter_AddRefs(list));
	if (NS_FAILED(result) || !list) return G_FAILED;

	PRUint32 count(0); 
	result = list->GetLength(&count);
	if (NS_FAILED(result)) return G_FAILED;

	//Sometimes Mozilla wants to return -1 as an unsigned value...
	if ((count+1) == 0) count = 0;

	gint found(0);
	gint found_named(0);
	GList *csslist(NULL);
	for (PRUint32 i = 0; i < count; i++)
	{
		nsCOMPtr<nsIDOMStyleSheet> item;
		result = list->Item(i, getter_AddRefs(item));
		if (NS_FAILED(result) || !item) continue;

		found++;

		nsAutoString string;
		result = item->GetTitle(string);
		if (NS_FAILED(result)) continue;

		if (string.IsEmpty()) continue;

		const char * name = NS_ConvertUCS2toUTF8(string).get();

		if (g_list_find_custom (csslist, name, stylesheet_find_func))
		{
			continue;
		}

		if (!stylesheet_is_alternate (item))
		{
			found_named++;
		}

		EmbedStyleSheet *sheet = g_new0(EmbedStyleSheet, 1);
		sheet->name  = g_strdup(name);
		sheet->sheet = item;
		sheet->type  = STYLESHEET_NAMED;

		csslist = g_list_append(csslist, sheet);
	}

	if (found > 0 && found_named == 0)
	{
		/* Add in the "Default" style if we found stylesheets but
		 * we didn't find any (non-alternate) named ones) */
		EmbedStyleSheet *sheet = g_new0(EmbedStyleSheet, 1);
		sheet->name  = g_strdup(_("Default"));
		sheet->sheet = NULL;
		sheet->type  = STYLESHEET_BASIC;

		csslist = g_list_append(csslist, sheet);
	}

	if (found > 0)
	{
		/* prepend None item if any sheets were found */
		EmbedStyleSheet *sheet = g_new0(EmbedStyleSheet, 1);
		sheet->name = g_strdup(_("None"));
		sheet->sheet = NULL;
		sheet->type = STYLESHEET_NONE;

		csslist = g_list_prepend(csslist, sheet);
	}

	(*retSheets) = csslist;
	return G_OK;
}

static gresult
impl_get_selected_stylesheet(GaleonEmbed *embed,
			     EmbedStyleSheet **retSheet)
{
	gboolean found = FALSE;
	nsresult result;

        GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	
	nsCOMPtr<nsIDOMStyleSheetList> list;
	result = wrapper->GetStyleSheets(getter_AddRefs(list));
	if (NS_FAILED(result) || !list) return G_FAILED;
	
	PRUint32 count, i; 
	result = list->GetLength(&count);
	if (NS_FAILED(result)) return G_FAILED;

        //Sometimes Mozilla wants to return -1 as an unsigned value...
        if ((count+1) == 0) count = 0;

	for (i = 0; i < count; i++)
	{
		nsCOMPtr<nsIDOMStyleSheet> item;
		result = list->Item(i, getter_AddRefs(item));
		if (NS_FAILED(result) || !item) continue;

		nsAutoString string;
		result = item->GetTitle(string);
		if (NS_FAILED(result)) continue;

		PRBool disabled;
		item->GetDisabled(&disabled);
		
		if (string.IsEmpty())
		{
			/* fine, but let's try to get something more sensible */
			if (disabled == PR_FALSE) found = TRUE;
			continue;
		}
		
		if (disabled == PR_FALSE)
		{
			EmbedStyleSheet *sheet =
				g_new0(EmbedStyleSheet, 1);
			sheet->name =
				g_strdup(NS_ConvertUCS2toUTF8(string).get());
			sheet->sheet = item;
			sheet->type = STYLESHEET_NAMED;

			(*retSheet) = sheet;
			return G_OK;
		}
	}

	if (found)
	{
		EmbedStyleSheet *sheet = g_new0(EmbedStyleSheet, 1);
		sheet->type = STYLESHEET_BASIC;
		(*retSheet) = sheet;
		return G_OK;
	}
	else
	{
		(*retSheet) =  NULL;
		return G_FAILED;
	}
}

static gresult
impl_set_stylesheet(GaleonEmbed *embed,
		    EmbedStyleSheet *sheet)
{
	nsresult result;

        GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;
	
	nsCOMPtr<nsIDOMStyleSheetList> list;
	result = wrapper->GetStyleSheets(getter_AddRefs(list));
	if (NS_FAILED(result) || !list) return G_FAILED;
	
	PRUint32 count, i; 
	result = list->GetLength(&count);
	if (NS_FAILED(result)) return G_FAILED;

        //Sometimes Mozilla wants to return -1 as an unsigned value...
        if ((count+1) == 0) count = 0;

	for (i = 0; i < count; i++)
	{
		nsCOMPtr<nsIDOMStyleSheet> item;
		result = list->Item(i, getter_AddRefs(item));
		if (NS_FAILED(result) || !item) continue;
		
		nsAutoString string;
		result = item->GetTitle(string);
		if (NS_FAILED(result)) continue;

		const char* title = NS_ConvertUCS2toUTF8(string).get();

		/*
		 * if STYLESHEET_NONE is requtested, disable all.
		 * if STYLESHEET_BASIC is requested, enable only unnamed sheets
		 * if STYLESHEET_NAMED is requested load all sheets that have
		 *    that name, and all unnamed ones
		 */
		if      (item == sheet->sheet || strcmp (title, sheet->name) == 0)
		{
			item->SetDisabled(FALSE);
		}
		else if (sheet->type != STYLESHEET_NONE && string.IsEmpty())
		{
			item->SetDisabled(FALSE);
		}
		else
		{
			item->SetDisabled(TRUE);
		}
	}

	return G_OK;
}

static gresult
impl_apply_user_stylesheet(GaleonEmbed *embed,
			   const gchar *sheetfile,
			   EmbedStyleSheet **retSheet)
{
        GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;

	nsIStyleSheet *item = nsnull;

	gchar *path = galeon_embed_utils_get_user_sheet_path(sheetfile);

	gchar *tmp = g_strconcat("file://", path, NULL);
	g_free(path);

	wrapper->LoadOverrideStyleSheet(tmp, &item);
	g_free (tmp);

	EmbedStyleSheet *sheet = g_new0(EmbedStyleSheet, 1);
	sheet->name = g_strdup(sheetfile);
	sheet->sheet = item;
	sheet->type = STYLESHEET_NAMED;

	(*retSheet) = sheet;
	return G_OK;
}

static gresult
impl_remove_user_stylesheet(GaleonEmbed *embed,
			    EmbedStyleSheet *sheet)
{
	if (!sheet) return G_OK;

        GaleonWrapper *wrapper = MOZILLA_EMBED(embed)->priv->wrapper;

	nsIStyleSheet *item = NS_STATIC_CAST(nsIStyleSheet*, sheet->sheet);

	nsresult rv = NS_OK;
	rv = wrapper->RemoveOverrideStyleSheet(item);

	return NS_SUCCEEDED(rv) ? G_OK : G_FAILED;
}

static gboolean
stylesheet_is_alternate(nsIDOMStyleSheet *item)
{
	nsresult result;
	gboolean ret = FALSE;

	nsCOMPtr<nsIDOMNode> node;
	result = item->GetOwnerNode(getter_AddRefs(node));
	if (NS_FAILED(result)) return FALSE;

	nsCOMPtr<nsIDOMHTMLLinkElement> link = do_QueryInterface(node);
	
	if (link)
	{
		nsAutoString str;
		link->GetRel(str);
		
		gchar *tmp = g_ascii_strdown(NS_ConvertUCS2toUTF8(str).get(), -1);
		
		if (strstr (tmp, "alternate") != NULL) ret = TRUE;

		g_free(tmp);
	}

	return ret;
}

