// -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-

//
//  Copyright (C) 2002-2003 Hiroyuki Ikezoe
//  Copyright (C) 2003 Takuro Ashie
//
//  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.
//

#include "kz-mozembed.h"

#include <string.h>
#include <gtkmozembed.h>
#include <gtkmozembed_internal.h>

#include "kazehakase.h"
#include "kz-mozwrapper.h"
#include "kz-mozprogresslistener.h"
#include "gobject-utils.h"
#include "intl.h"
#include "mozilla.h"
#include "mozilla-prefs.h"
#include "bookmarks/kz-bookmark.h"
#if USE_MIGEMO
#include "migemo.h"
#endif
#include "utils.h"

#include <nsCOMPtr.h>
#include <nsIDOMDocument.h>
#include <content/nsIDocumentViewer.h>
#include <content/nsIContent.h>
#include <nsIWebBrowser.h>
#include <content/nsIDocument.h>
#include <nsIDOMMouseEvent.h>
#include <dom/nsIDOMKeyEvent.h>
#include <nsIDOMEventTarget.h>
#include <dom/nsIDOMNSHTMLElement.h>  
#include <nsIDOMHTMLElement.h>  
#include <nsIDOMHTMLTextAreaElement.h>  
#include <nsIDOMNamedNodeMap.h>
#include <webbrowserpersist/nsIWebBrowserPersist.h>
#include <necko/nsNetUtil.h>
#include <nsIWebBrowserFind.h>
#include <dom/nsIDOMNSDocument.h>
#include <dom/nsIDOMNSEvent.h>
#include <docshell/nsIDocShell.h>
#include <docshell/nsIDocShellTreeItem.h>
#include <docshell/nsIDocShellTreeOwner.h>
#include <nsIDOMNodeList.h>
#include <nsIDOMWindow.h>
#include <nsISelection.h>
#include <nsIDOMRange.h>
#include <nsIWebBrowserFind.h>
#include <necko/nsNetUtil.h>
#include <uconv/nsICharsetConverterManager.h>
#if MOZILLA_SNAPSHOT < 10
#	include <uconv/nsICharsetConverterManager2.h>
#endif
#include <nsIDOMWindow.h>
#include <nsISelection.h>
#include <nsISHistory.h>
#include <nsIHistoryEntry.h>
#include <nsISHEntry.h>
#include <nsIWebNavigation.h>
#include <nsCWebBrowserPersist.h>
#include <widget/nsIBaseWindow.h>
#include <nsIWebPageDescriptor.h>
#include <nsIPresContext.h>
#include <nsIEventStateManager.h>

struct _KzMozEmbedPriv
{
	KzMozWrapper *wrapper;
	gint size_inited;
	gint cur_requests;
	gint total_requests;
	
	gboolean lock;

	/* for navigation link */
	gchar *link_next;
	gchar *link_prev;
	gchar *link_index;
	gchar *link_contents;
	gchar *link_start;
	
	/* for alternate rss */
	gchar *link_rss;
#if USE_MIGEMO
	/* for migemo */
	gchar *migemo_keyword;
#endif
};

enum {
	FAVICON_SIGNAL,
	DESTROY_EMBED_SIGNAL,
	LAST_SIGNAL
};

static void kz_moz_embed_class_init    (KzMozEmbedClass *klass);
static void kz_moz_embed_init          (KzMozEmbed *kzembed);
static void kz_moz_embed_destroy       (GtkObject *object);
static void kz_moz_embed_finalize      (GObject    *object);
static void kz_moz_embed_realize       (GtkWidget *widget);
static void kz_moz_embed_unrealize     (GtkWidget *widget);
static void kz_moz_embed_size_allocate (GtkWidget *widget,
					GtkAllocation *allocation);
static void kz_moz_embed_destroy_brsr  (GtkMozEmbed *embed);
static void kz_moz_embed_title         (GtkMozEmbed *embed);
static void kz_moz_embed_location      (GtkMozEmbed *embed);
static void kz_moz_embed_net_start     (GtkMozEmbed *embed);
static gint kz_moz_embed_open_uri      (GtkMozEmbed *embed, const char *uri);
static void kz_moz_embed_size_to       (GtkMozEmbed *embed,
					gint width, gint height);
static void kz_moz_embed_visibility    (GtkMozEmbed *embed,
					gboolean visibility);
static void kz_moz_embed_net_stop      (GtkMozEmbed *embed);
static void kz_moz_embed_net_state_all (GtkMozEmbed *embed,
					const char  *aURI,
					gint         state,
					guint        status);

static void kz_moz_embed_get_head_link (KzMozEmbed *kzembed);

static void kz_moz_embed_navigation_link_free(KzMozEmbed *kzembed);

static gint kz_moz_embed_dom_key_down  (GtkMozEmbed *embed, gpointer event);
static gint kz_moz_embed_dom_key_up    (GtkMozEmbed *embed, gpointer event);
static gint kz_moz_embed_dom_key_press (GtkMozEmbed *embed, gpointer event);

static gchar    *mozilla_get_attribute      (nsIDOMNode *node,
					     gchar *attribute);
static gboolean  mozilla_get_link_from_node (nsIDOMDocument *domDoc,
					     nsIDOMNode *node,
					     gchar **url,
					     gchar **inner_html);
static glong     mozilla_set_event_context  (KzMozEmbed *kzembed,
					     nsIDOMEventTarget *target,
					     KzEmbedEvent *info);

static gboolean  mozilla_get_head_link_from_node (nsIDOMDocument *domDoc,
						  nsIDOMNode *node,
						  gchar **url);

static GtkMozEmbedClass *parent_class = NULL;

static gint kz_moz_embed_signals[LAST_SIGNAL] = {0};

KZ_OBJECT_GET_TYPE(kz_moz_embed, "KzMozEmbed", KzMozEmbed,
		   kz_moz_embed_class_init, kz_moz_embed_init,
		   GTK_TYPE_MOZ_EMBED)


static void
kz_moz_embed_class_init (KzMozEmbedClass *klass)
{
	GObjectClass *gobject_class;
	GtkObjectClass *object_class;
	GtkWidgetClass *widget_class;
	GtkMozEmbedClass *moz_embed_class;

	parent_class    = (GtkMozEmbedClass *) g_type_class_peek_parent (klass);

	gobject_class   = (GObjectClass *) klass;
	object_class    = (GtkObjectClass *) klass;
	widget_class    = (GtkWidgetClass *) klass;
	moz_embed_class = (GtkMozEmbedClass *) klass;

	// GObject
	gobject_class->finalize = kz_moz_embed_finalize;

	// GtkObject signals
	object_class->destroy = kz_moz_embed_destroy;
 
	// widget class
	widget_class->realize         = kz_moz_embed_realize;
	widget_class->unrealize       = kz_moz_embed_unrealize;
	widget_class->size_allocate   = kz_moz_embed_size_allocate;
 
	// GtkMozEmbedSignals
	moz_embed_class->net_state_all   = kz_moz_embed_net_state_all;
#if 0
	moz_embed_class->dom_mouse_over  = kz_moz_embed_dom_mouse_over;
	moz_embed_class->dom_mouse_out   = kz_moz_embed_dom_mouse_out;
	moz_embed_class->net_state       = kz_moz_embed_net_state;
	moz_embed_class->progress        = kz_moz_embed_progress;
	moz_embed_class->progress_all    = kz_moz_embed_progress_all;
	moz_embed_class->js_status       = kz_moz_embed_js_status;
#endif
	moz_embed_class->destroy_brsr    = kz_moz_embed_destroy_brsr;

	moz_embed_class->title           = kz_moz_embed_title;
	moz_embed_class->location        = kz_moz_embed_location;
	moz_embed_class->net_start       = kz_moz_embed_net_start;
	moz_embed_class->net_stop        = kz_moz_embed_net_stop;
	moz_embed_class->open_uri        = kz_moz_embed_open_uri;
	moz_embed_class->size_to         = kz_moz_embed_size_to;
	moz_embed_class->visibility      = kz_moz_embed_visibility;
	moz_embed_class->dom_key_press   = kz_moz_embed_dom_key_press;
	moz_embed_class->dom_key_down    = kz_moz_embed_dom_key_down;
	moz_embed_class->dom_key_up      = kz_moz_embed_dom_key_up;

	// our own signals
	klass->favicon       = NULL;
	klass->destroy_embed = NULL;

	kz_moz_embed_signals[FAVICON_SIGNAL]
		= g_signal_new ("favicon",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (KzMozEmbedClass, favicon),
				NULL, NULL,
				g_cclosure_marshal_VOID__STRING,
				G_TYPE_NONE, 1,
				G_TYPE_STRING);
	kz_moz_embed_signals[DESTROY_EMBED_SIGNAL]
		= g_signal_new ("destroy_embed",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (KzMozEmbedClass, destroy_embed),
				NULL, NULL,
				g_cclosure_marshal_VOID__VOID,
				G_TYPE_NONE, 0);
}


static void
kz_moz_embed_init (KzMozEmbed *kzembed)
{
	// widgets
	kzembed->popup_window          = NULL;

	// status
	kzembed->location              = NULL;
	kzembed->title                 = NULL;
	kzembed->load_started          = 0;
	kzembed->load_percent          = 0;
	kzembed->bytes_loaded          = 0;
	kzembed->max_bytes_loaded      = 0;
	kzembed->is_loading            = FALSE;
	kzembed->load_status_message   = NULL;

	// priv
	kzembed->priv = g_new0(KzMozEmbedPriv, 1);
	kzembed->priv->wrapper        = NULL;
	kzembed->priv->size_inited    = FALSE;
	kzembed->priv->total_requests = 0;
	kzembed->priv->cur_requests   = 0;
	kzembed->priv->link_index     = NULL;
	kzembed->priv->link_contents  = NULL;
	kzembed->priv->link_start     = NULL;
	kzembed->priv->link_next      = NULL;
	kzembed->priv->link_prev      = NULL;
	kzembed->priv->link_rss       = NULL;
#if USE_MIGEMO
	kzembed->priv->migemo_keyword = NULL;
#endif
}

GtkWidget *
kz_moz_embed_new (const gchar *url)
{
	KzMozEmbed *kzembed = KZ_MOZ_EMBED(g_object_new(KZ_TYPE_MOZ_EMBED, NULL));

#if 0
	//
	// set the chrome type so it's stored in the object
	//
	g_moz_embed_set_chrome_mask(GTK_MOZ_EMBED(kzembed->mozembed),
				    actualChromeMask);
#endif
	kz_moz_embed_load_url(kzembed, url);

	return GTK_WIDGET(kzembed);
}

static void
kz_moz_embed_destroy (GtkObject *object)
{
	KzMozEmbed *kzembed = KZ_MOZ_EMBED(object);

	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

	g_free(kzembed->location);
	kzembed->location = NULL;
	g_free(kzembed->title);
	kzembed->title = NULL;
#if USE_MIGEMO
	if (kzembed->priv->migemo_keyword)
	{
		g_free(kzembed->priv->migemo_keyword);
		kzembed->priv->migemo_keyword = NULL;
	}
#endif
	if (GTK_OBJECT_CLASS(parent_class)->destroy)
		GTK_OBJECT_CLASS(parent_class)->destroy(object);
}


KZ_OBJECT_FINALIZE (kz_moz_embed, KzMozEmbed)


static void
kz_moz_embed_realize (GtkWidget *widget)
{
	if (GTK_WIDGET_CLASS(parent_class)->realize)
		GTK_WIDGET_CLASS(parent_class)->realize(widget);

	KzMozEmbed *kzembed = KZ_MOZ_EMBED(widget);

	if (!kzembed->priv->wrapper)
	{
		kzembed->priv->wrapper = new KzMozWrapper;
		nsresult rv = kzembed->priv->wrapper->Init(kzembed);
		if (NS_FAILED(rv))
		{
			g_error("KzMozEmbed: Faild to init KzMozWrapper!");
		}
	}
}


static void
kz_moz_embed_unrealize (GtkWidget *widget)
{
	KzMozEmbed *kzembed = KZ_MOZ_EMBED(widget);

	kzembed->priv->size_inited = FALSE;

	if (kzembed->priv->wrapper)
	{
		kzembed->priv->wrapper->Destroy();
		delete kzembed->priv->wrapper;
		kzembed->priv->wrapper = NULL;
	}
	
	kz_moz_embed_navigation_link_free(kzembed);
	
	if (GTK_WIDGET_CLASS(parent_class)->unrealize)
		GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
}


static void
kz_moz_embed_navigation_link_free(KzMozEmbed *kzembed)
{	
	if (kzembed->priv->link_index)
		g_free(kzembed->priv->link_index);
	if (kzembed->priv->link_contents)
		g_free(kzembed->priv->link_contents);
	if (kzembed->priv->link_start)
		g_free(kzembed->priv->link_start);
	if (kzembed->priv->link_prev)
		g_free(kzembed->priv->link_prev);
	if (kzembed->priv->link_next)
		g_free(kzembed->priv->link_next);
	if (kzembed->priv->link_rss)
		g_free(kzembed->priv->link_rss);
	
	kzembed->priv->link_index     = NULL;
	kzembed->priv->link_contents  = NULL;
	kzembed->priv->link_start     = NULL;
	kzembed->priv->link_prev      = NULL;
	kzembed->priv->link_next      = NULL;
	kzembed->priv->link_rss       = NULL;
}


static void
kz_moz_embed_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
{
	KzMozEmbed *kzembed = KZ_MOZ_EMBED(widget);

	g_return_if_fail(GTK_IS_WIDGET(widget));

	if (!GTK_WIDGET_REALIZED(widget)) return;

	if (!kzembed->priv->size_inited) 
	{
		// for preventing invalid scroll position 
		// when new tab background opens with anchor (html#xx)  
		nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(kzembed->priv->wrapper->mWebBrowser);
		baseWindow->SetPositionAndSize(0, 0,
					       allocation->width,
					       allocation->height,
					       PR_FALSE);
	}
	if (!GTK_WIDGET_MAPPED(widget))
		return;
	
	if (GTK_WIDGET_CLASS(parent_class)->size_allocate)
		GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);

	kzembed->priv->size_inited = TRUE;
}


void
kz_moz_embed_load_url (KzMozEmbed *kzembed, const gchar *url)
{
	gchar *start_page = NULL, *str;

	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

	str = KZ_CONF_GET_STR("Global", "startup_page");

	if (url && *url)
	{
		start_page = g_strdup(url);
	}
	else if (str)
	{
		start_page = g_strdup(str);
	}
	else
	{
		start_page = g_strdup("about:blank");
	}

	if (kz_moz_embed_get_lock(KZ_MOZ_EMBED(kzembed)))
	{
		GtkMozEmbed *newembed = NULL;
		g_signal_emit_by_name(G_OBJECT(kzembed), 
				      "new-window",
				      &newembed, 0);
		gtk_moz_embed_load_url(newembed, start_page);
	}
	else
		gtk_moz_embed_load_url(GTK_MOZ_EMBED(kzembed), start_page);

	g_free(str);
	g_free(start_page);
}


void
kz_moz_embed_view_source (KzMozEmbed *kzembed, const gchar *url)
{
	KzMozWrapper *wrapper = KZ_MOZ_EMBED(kzembed)->priv->wrapper;
	nsresult rv;

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

        rv = wrapper->LoadDocument(pageDescriptor, 
				   nsIWebPageDescriptor::DISPLAY_AS_SOURCE);
}


const gchar *
kz_moz_embed_get_title (KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), NULL);
	return kzembed->title;
}

gchar *
kz_moz_embed_ensure_title (KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), NULL);

	if (kzembed->title && *kzembed->title)
		return g_strdup(kzembed->title);

	if (kzembed->location && *kzembed->location)
	{
		if (kz_moz_embed_is_loading(kzembed))
		{
			return g_strdup_printf(_("Loading %s ..."),
					       kzembed->location);
		}
		else
		{
			return g_strdup(kzembed->location);
		}
	}
	else
	{
		if (kz_moz_embed_is_loading(kzembed))
			return g_strdup(_("Loading..."));
	}

	return g_strdup(_("No title"));
}

gchar * 
kz_moz_embed_get_link_message (KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_MOZ_EMBED(kzembed), NULL);

	gchar *message;
	nsXPIDLString  uMessage;

	*getter_Copies(uMessage) = gtk_moz_embed_get_link_message_unichar
						(GTK_MOZ_EMBED(kzembed));
	message = g_strdup(NS_ConvertUCS2toUTF8(uMessage).get());

	return message;
}


gdouble
kz_moz_embed_get_progress (KzMozEmbed *kzembed)
{
	gdouble progress;

	g_return_val_if_fail(KZ_MOZ_EMBED(kzembed), 0.0);

	if (kzembed->priv->total_requests <= 0 ||
	    kzembed->priv->cur_requests <= 0)
	{
		return 0.0;
	}

	progress = (gdouble) kzembed->priv->cur_requests
		/ (gdouble) kzembed->priv->total_requests;

	if (progress > 1.0)
		return 1.0;

	return progress;
}


const gchar *
kz_moz_embed_get_location (KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), NULL);
	if (kzembed->location != NULL && !strncmp(kzembed->location, "about:blank", 11))
		return "";

	return kzembed->location;
}

static void
kz_moz_embed_destroy_brsr (GtkMozEmbed *embed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(embed));

	KzMozEmbed *kzembed = KZ_MOZ_EMBED(embed);

	g_signal_emit(G_OBJECT(kzembed), 
		      kz_moz_embed_signals[DESTROY_EMBED_SIGNAL],
		      0, NULL);

}


static void
kz_moz_embed_title (GtkMozEmbed *embed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(embed));

	KzMozEmbed *kzembed = KZ_MOZ_EMBED(embed);

	g_free(kzembed->title);

	nsXPIDLString  uTitle;
	*getter_Copies(uTitle) = gtk_moz_embed_get_title_unichar(embed);
	kzembed->title = g_strdup (NS_ConvertUCS2toUTF8(uTitle).get());
}

static void
kz_moz_embed_location (GtkMozEmbed *embed)
{
	KzMozEmbed *kzembed = KZ_MOZ_EMBED(embed);

	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

	g_free(kzembed->location);
	kzembed->location = gtk_moz_embed_get_location(embed);
}


static void
kz_moz_embed_net_start (GtkMozEmbed *embed)
{
	KzMozEmbed *kzembed = KZ_MOZ_EMBED(embed);

	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

	if (parent_class->net_start)
		parent_class->net_start(embed);

	kzembed->is_loading = TRUE;
}

static void
kz_moz_embed_net_stop (GtkMozEmbed *embed)
{
	KzMozEmbed *kzembed = KZ_MOZ_EMBED(embed);

	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

	if (parent_class->net_stop)
		parent_class->net_stop(embed);

	kzembed->is_loading = FALSE;

	/* First free previous link */
	kz_moz_embed_navigation_link_free(kzembed);

	kz_moz_embed_get_head_link(kzembed);
}

static void
kz_moz_embed_net_state_all (GtkMozEmbed *embed, const char *aURI,
			    gint state, guint status)
{
	KzMozEmbed *kzembed = KZ_MOZ_EMBED(embed);

	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

	if (state & GTK_MOZ_EMBED_FLAG_IS_NETWORK)
	{
		kzembed->priv->total_requests = 0;
		kzembed->priv->cur_requests = 0;
#if 0
                if (state & GTK_MOZ_EMBED_FLAG_START)
                {
		}
                else if (state & EMBED_STATE_STOP)
                {
                        kzembed->priv->total_requests = 0;
                        kzembed->priv->cur_requests = 0;
		} 
#endif
	}
        if (state & GTK_MOZ_EMBED_FLAG_IS_REQUEST)
        {
                if (state & GTK_MOZ_EMBED_FLAG_START)
                        kzembed->priv->total_requests ++;
                else if (state & GTK_MOZ_EMBED_FLAG_STOP)
                        kzembed->priv->cur_requests ++;
        }
	if (parent_class->net_state_all)
		parent_class->net_state_all(embed, aURI, state, status);
}

static gint
kz_moz_embed_open_uri (GtkMozEmbed *embed, const char *uri)
{
	if (parent_class->open_uri)
		parent_class->open_uri(embed, uri);

	if (!strncmp(uri, "mailto:", 7))
	{
		return TRUE;
	}

	return FALSE;
}

static void
kz_moz_embed_size_to (GtkMozEmbed *embed, gint width, gint height)
{
	gtk_widget_set_size_request(GTK_WIDGET(embed), width, height);
	gtk_widget_queue_resize(GTK_WIDGET(embed));
}

static gint
kz_moz_embed_dom_key_down (GtkMozEmbed *embed, gpointer event)
{
	return FALSE;
}

static gint
kz_moz_embed_dom_key_up (GtkMozEmbed *embed, gpointer event)
{
	return FALSE;
}

static gint
kz_moz_embed_dom_key_press (GtkMozEmbed *embed, gpointer event)
{
	return FALSE;
}

static void
kz_moz_embed_visibility (GtkMozEmbed *embed, gboolean visibility)
{
	GtkWidget *parent = NULL;

	parent = gtk_widget_get_parent(GTK_WIDGET(embed));
	g_return_if_fail(parent != NULL);
	
	if (visibility) 
	{
		gtk_widget_show(GTK_WIDGET(embed));
		gtk_widget_show(parent);
	}
	else
	{
		gtk_widget_hide(GTK_WIDGET(embed));
		gtk_widget_hide(parent);
	}
}

gboolean
kz_moz_embed_can_cut_selection (KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);

	if (!kzembed->priv->wrapper) return TRUE;

	PRBool retval;
	nsresult rv = kzembed->priv->wrapper->CanCutSelection(&retval);

	if (NS_FAILED(rv)) return FALSE;

	return retval;
}

gboolean
kz_moz_embed_can_copy_selection (KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);

	if (!kzembed->priv->wrapper) return TRUE;

	PRBool retval;
	nsresult rv = kzembed->priv->wrapper->CanCopySelection(&retval);

	if (NS_FAILED(rv)) return FALSE;

	return retval;
}

gboolean
kz_moz_embed_can_paste (KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);

	if (!kzembed->priv->wrapper) return TRUE;

	PRBool retval;
	nsresult rv = kzembed->priv->wrapper->CanPaste(&retval);

	if (NS_FAILED(rv)) return FALSE;

	return retval;
}

void
kz_moz_embed_cut_selection (KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	g_return_if_fail(kzembed->priv->wrapper);

	kzembed->priv->wrapper->CutSelection();
}

void
kz_moz_embed_copy_selection (KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	g_return_if_fail(kzembed->priv->wrapper);

	kzembed->priv->wrapper->CopySelection();
}

void
kz_moz_embed_paste (KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	g_return_if_fail(kzembed->priv->wrapper);

	kzembed->priv->wrapper->Paste();
}

void
kz_moz_embed_select_all (KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	g_return_if_fail(kzembed->priv->wrapper);

	kzembed->priv->wrapper->SelectAll();
}

gchar *
kz_moz_embed_get_selection_string(KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), NULL);

	if (!kzembed->priv->wrapper) return NULL;

	nsresult rv;

	nsCOMPtr<nsISelection> selection;
	rv = kzembed->priv->wrapper->GetSelection(getter_AddRefs(selection));
	if (!selection) return NULL;

	PRUnichar *string;
	rv = selection->ToString(&string);
	
	if (NS_FAILED(rv)) return NULL;

	return g_strdup (NS_ConvertUCS2toUTF8(string).get());
}

gchar *
kz_moz_embed_get_selection_source(KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), NULL);

	if (!kzembed->priv->wrapper) return NULL;

	nsresult rv;

	nsCOMPtr<nsISelection> selection;
	rv = kzembed->priv->wrapper->GetSelection(getter_AddRefs(selection));
	if (!selection) return NULL;

	nsAutoString string;
	rv = kzembed->priv->wrapper->GetSelectionSource(selection, PR_TRUE, string);
	if (NS_FAILED(rv)) return NULL;

	return g_strdup(NS_ConvertUCS2toUTF8(string).get());
}

gboolean
kz_moz_embed_find (KzMozEmbed *embed, const char *keyword,
		   gboolean backward)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(embed), FALSE);
	g_return_val_if_fail(keyword, FALSE);

	nsCOMPtr<nsIWebBrowser> web;
	gtk_moz_embed_get_nsIWebBrowser(GTK_MOZ_EMBED(embed),
					getter_AddRefs(web));
	if (!web) return FALSE;

	nsresult rv;
	nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(web));
#if USE_MIGEMO
	if (migemo && embed->priv->migemo_keyword)
		finder->SetSearchString(NS_ConvertUTF8toUCS2(embed->priv->migemo_keyword).get());
	else
		finder->SetSearchString(NS_ConvertUTF8toUCS2(keyword).get());
#else
	finder->SetSearchString(NS_ConvertUTF8toUCS2(keyword).get());
#endif
	finder->SetFindBackwards(backward);
	finder->SetWrapFind(TRUE);
	finder->SetEntireWord(TRUE);
	finder->SetSearchFrames(TRUE);
	finder->SetMatchCase(FALSE);
	PRBool did_find;
	rv = finder->FindNext(&did_find);

	return NS_SUCCEEDED(rv) && did_find ? TRUE : FALSE;
}

gboolean
kz_moz_embed_incremental_search (KzMozEmbed *embed, const char *keyword,
				 gboolean backward)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(embed), FALSE);
	g_return_val_if_fail(keyword, FALSE);
	
	if (strlen(keyword) ==0)
		return FALSE;

	nsCOMPtr<nsIWebBrowser> web;
	gtk_moz_embed_get_nsIWebBrowser(GTK_MOZ_EMBED(embed),
					getter_AddRefs(web));
	if (!web) return FALSE;

	nsCOMPtr<nsISelection> selection;
	nsresult rv;

	rv = embed->priv->wrapper->GetSelection(getter_AddRefs(selection));
	if (selection)
	{
		selection->RemoveAllRanges();
	}

	nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(web));
#if USE_MIGEMO
	gboolean use_migemo;
	KZ_CONF_GET("Global", "use_migemo", use_migemo, BOOL);

	if (use_migemo)
	{
		nsAutoString text;
		gchar *body_string;

		embed->priv->wrapper->GetBodyString(text);
		body_string = g_strdup(NS_ConvertUCS2toUTF8(text).get());

		if (!migemo)
		{
			if (!migemo_init())
			{
				finder->SetSearchString(NS_ConvertUTF8toUCS2(keyword).get());
				goto START_SEARCH;
			}
		}

		gchar *word = migemo_get_word(body_string, keyword);

		if (word) {
			if (embed->priv->migemo_keyword)
				g_free(embed->priv->migemo_keyword);

			embed->priv->migemo_keyword = word;

			finder->SetSearchString(NS_ConvertUTF8toUCS2(embed->priv->migemo_keyword).get());
		}
		g_free(body_string);
	}
	else
	{
		finder->SetSearchString(NS_ConvertUTF8toUCS2(keyword).get());
	}
#else
	finder->SetSearchString(NS_ConvertUTF8toUCS2(keyword).get());
#endif
START_SEARCH:
	finder->SetFindBackwards(backward);
	finder->SetWrapFind(TRUE);
	finder->SetEntireWord(TRUE);
	finder->SetSearchFrames(TRUE);
	finder->SetMatchCase(FALSE);
	PRBool did_find;
	rv = finder->FindNext(&did_find);

	return NS_SUCCEEDED(rv) && did_find ? TRUE : FALSE;
}

gboolean
kz_moz_embed_selection_is_collapsed (KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), TRUE);

	if (!kzembed->priv->wrapper) return TRUE;

	nsresult rv;

	nsCOMPtr<nsISelection> selection;
	rv = kzembed->priv->wrapper->GetSelection(getter_AddRefs(selection));
	if (!selection) return TRUE;

	PRBool collapsed;
	rv = selection->GetIsCollapsed(&collapsed);
	if (NS_FAILED(rv)) return TRUE;

	return collapsed;
}


gboolean
kz_moz_embed_get_links (KzMozEmbed *kzembed, GList **list,
			gboolean selected_only)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);
	g_return_val_if_fail(kzembed->priv->wrapper, FALSE);
	g_return_val_if_fail(list, FALSE);

	// get selection
	nsresult rv;
	nsCOMPtr<nsISelection> selection;
	rv = kzembed->priv->wrapper->GetSelection(getter_AddRefs(selection));
	if (NS_FAILED(rv)) return FALSE;

	// get all anchor nodes in the document.
	nsCOMPtr<nsIDOMDocument> domDoc;
        rv = kzembed->priv->wrapper->GetMainDomDocument(getter_AddRefs(domDoc));
        if (NS_FAILED(rv) || !domDoc) return FALSE;

	nsAutoString tagname;
	tagname.AssignWithConversion("a");
	nsCOMPtr<nsIDOMNodeList> nodeList;
	rv = domDoc->GetElementsByTagName(tagname,
					  getter_AddRefs(nodeList));
        if (NS_FAILED(rv) || !domDoc) return FALSE;

	PRUint32 num;
	rv = nodeList->GetLength(&num);
	if (NS_FAILED(rv) || num < 1) return FALSE;

	// store links to GList
	nsCOMPtr<nsIDOMNode> node;
	for (PRUint32 i = 0; i < num; i++)
	{
		rv = nodeList->Item(i, getter_AddRefs(node));
		if (NS_FAILED(rv) || !node) continue;

		// check whether the selection contains these nodes or not.
		if (selected_only)
		{
			PRBool contains;
			selection->ContainsNode(node, PR_TRUE, &contains);
			if (!contains) continue;
		}

		gchar *uri = NULL, *title = NULL;
		mozilla_get_link_from_node(domDoc, node,
					   &uri, &title);
		if (uri && *uri)
		{
			KzBookmark *link;
			link = kz_bookmark_new_with_attrs(title, uri, NULL);
			*list = g_list_append(*list, link);
		}

		g_free(uri);
		g_free(title);
	}

	return TRUE;
}


void
kz_moz_embed_add_ui (KzMozEmbed *kzembed, GtkUIManager *ui, KzUILevel level)
{
	g_warning("kz_moz_embed_add_ui() is not implemented yet");
}


void
kz_moz_embed_remove_ui (KzMozEmbed *kzembed)
{
	g_warning("kz_moz_embed_remove_ui() is not implemented yet");
}


glong
kz_moz_embed_get_key_event_info(KzMozEmbed *kzembed, gpointer event,
				KzEmbedEventKey **info_ret)
{
	KzEmbedEventKey *info;
	info = (KzEmbedEventKey *) kz_embed_event_new(KZ_EMBED_EVENT_KEY);
	*info_ret = info;

	nsresult result;

	nsIDOMKeyEvent *aKeyEvent = (nsIDOMKeyEvent*) event;

     	nsCOMPtr<nsIDOMEventTarget> OriginalTarget;

     	nsCOMPtr<nsIDOMNSEvent> aEvent = do_QueryInterface(aKeyEvent);
     	if (!aEvent) return KZ_CONTEXT_NONE;

	PRUint32 code;
	aKeyEvent->GetKeyCode(&code);
	code = info->key;

	aKeyEvent->GetCharCode(&code);
	code = info->char_code;

	PRBool mod_key;
	info->modifier = 0;
	aKeyEvent->GetAltKey(&mod_key);
        if (mod_key) info->modifier |= KZ_ALT_KEY;

	aKeyEvent->GetShiftKey(&mod_key);
        if (mod_key) info->modifier |= KZ_SHIFT_KEY;

	aKeyEvent->GetMetaKey(&mod_key);
        if (mod_key) info->modifier |= KZ_META_KEY;

	aKeyEvent->GetCtrlKey(&mod_key);
        if (mod_key) info->modifier |= KZ_CTRL_KEY;

     	result = aEvent->GetOriginalTarget(getter_AddRefs(OriginalTarget));

     	if (NS_FAILED(result) || !OriginalTarget) return KZ_CONTEXT_NONE;

     	nsCOMPtr<nsIDOMNode> OriginalNode = do_QueryInterface(OriginalTarget);
     	if (!OriginalNode) return KZ_CONTEXT_NONE;

     	nsString nodename;
     	OriginalNode->GetNodeName(nodename);

     	if (nodename.EqualsWithConversion("xul:thumb") ||
	    nodename.EqualsWithConversion("xul:slider"))
	{
       		return KZ_CONTEXT_NONE;
	}

     	nsCOMPtr<nsIDOMEventTarget> target;
     	result = aKeyEvent->GetTarget(getter_AddRefs(target));
     	if (NS_FAILED(result) || !target) return KZ_CONTEXT_NONE;

	return mozilla_set_event_context(kzembed, target, (KzEmbedEvent *) info);
}


glong
kz_moz_embed_get_mouse_event_info(KzMozEmbed *kzembed, gpointer event,
				  KzEmbedEventMouse **info_ret)
{
	KzEmbedEventMouse *info;
	info = (KzEmbedEventMouse *) kz_embed_event_new(KZ_EMBED_EVENT_MOUSE);
	*info_ret = info;

	nsresult result;

	nsIDOMMouseEvent *aMouseEvent = (nsIDOMMouseEvent*)event;

     	nsCOMPtr<nsIDOMEventTarget> OriginalTarget;

     	nsCOMPtr<nsIDOMNSEvent> aEvent = do_QueryInterface(aMouseEvent);
     	if (!aEvent) return KZ_CONTEXT_NONE;

	PRUint16 button;
	aMouseEvent->GetButton(&button);
	info->button = button;

	PRBool mod_key;
	info->modifier = 0;
	aMouseEvent->GetAltKey(&mod_key);
        if (mod_key) info->modifier |= KZ_ALT_KEY;

	aMouseEvent->GetShiftKey(&mod_key);
        if (mod_key) info->modifier |= KZ_SHIFT_KEY;

	aMouseEvent->GetMetaKey(&mod_key);
        if (mod_key) info->modifier |= KZ_META_KEY;

	aMouseEvent->GetCtrlKey(&mod_key);
        if (mod_key) info->modifier |= KZ_CTRL_KEY;

	PRInt32 pos;
	aMouseEvent->GetClientX(&pos);
	info->x = pos;
	aMouseEvent->GetClientY(&pos);
	info->y = pos;

     	result = aEvent->GetOriginalTarget(getter_AddRefs(OriginalTarget));

     	if (NS_FAILED(result) || !OriginalTarget) return KZ_CONTEXT_NONE;

     	nsCOMPtr<nsIDOMNode> OriginalNode = do_QueryInterface(OriginalTarget);
     	if (!OriginalNode) return KZ_CONTEXT_NONE;

     	nsString nodename;
     	OriginalNode->GetNodeName(nodename);

     	if (nodename.EqualsWithConversion ("xul:thumb") ||
	    nodename.EqualsWithConversion ("xul:slider"))
	{
       		return KZ_CONTEXT_NONE;
	}

     	nsCOMPtr<nsIDOMEventTarget> target;
     	result = aMouseEvent->GetTarget(getter_AddRefs(target));
     	if (NS_FAILED(result) || !target) return KZ_CONTEXT_NONE;

	return mozilla_set_event_context(kzembed, target, (KzEmbedEvent *) info);
}


static void
kz_moz_embed_get_head_link (KzMozEmbed *kzembed)
{
	nsresult rv;
	nsCOMPtr<nsIDOMDocument> domDoc;
        rv = kzembed->priv->wrapper->GetMainDomDocument(getter_AddRefs(domDoc));
        if (NS_FAILED(rv) || !domDoc) return;

	nsAutoString tagname;
	tagname.AssignWithConversion("link");
	nsCOMPtr<nsIDOMNodeList> nodeList;
	rv = domDoc->GetElementsByTagName(tagname,
					  getter_AddRefs(nodeList));
        if (NS_FAILED(rv) || !domDoc) return;
	PRUint32 num;
	rv = nodeList->GetLength(&num);
	if (NS_FAILED(rv) || num < 1) return;

	nsCOMPtr<nsIDOMNode> node;
	for (PRUint32 i = 0; i < num; i++)
	{
		gchar *url = NULL;
		rv = nodeList->Item(i, getter_AddRefs(node));
		if (NS_FAILED(rv) || !node) continue;
		
		char *relattr =  mozilla_get_attribute(node, "rel");
		if (!relattr) continue;

		mozilla_get_head_link_from_node(domDoc, node, &url);

		nsAutoString rela;
		rela.AssignWithConversion(relattr);
		nsCString relc;
		relc.AssignWithConversion(rela);
		char *rel = ToNewCString(relc);
		if (!strcasecmp(rel, "SHORTCUT ICON") ||
		    !strcasecmp(rel, "ICON"))
		{
			g_signal_emit(G_OBJECT(kzembed), 
				      kz_moz_embed_signals[FAVICON_SIGNAL],
				      0, url);
		}
		else if(!strcasecmp(rel, "ALTERNATE"))
		{
			kzembed->priv->link_rss = g_strdup(url);
		}
		else if(!strcasecmp(rel, "INDEX"))
		{
			kzembed->priv->link_index = g_strdup(url);
		} 
		else if(!strcasecmp(rel, "CONTENTS"))
		{
			kzembed->priv->link_contents = g_strdup(url);
		}
		else if (!strcasecmp(rel, "PREV"))
		{
			kzembed->priv->link_prev = g_strdup(url);
		}
		else if (!strcasecmp(rel, "NEXT"))
		{
			kzembed->priv->link_next = g_strdup(url);
		}
		else if (!strcasecmp(rel, "START"))
		{
			kzembed->priv->link_start = g_strdup(url);
		}
		g_free(url);
		g_free(rel);
		g_free(relattr);
	}
}


// Nautilus CREDITS here
static gchar *
mozilla_get_attribute (nsIDOMNode *node, gchar *attribute)
{
     	nsresult result;

	nsCOMPtr<nsIDOMNamedNodeMap> attributes;
	result = node->GetAttributes(getter_AddRefs(attributes));
	if (!NS_SUCCEEDED (result) || !attributes) return NULL;

	nsAutoString attr; 

	attr.AssignWithConversion(attribute);

	nsCOMPtr<nsIDOMNode> attrNode;
	result = attributes->GetNamedItem (attr, getter_AddRefs(attrNode));
	if (!NS_SUCCEEDED(result) || !attrNode)  return NULL;

	nsAutoString nodeValue;

	result = attrNode->GetNodeValue(nodeValue);
	if (!NS_SUCCEEDED(result))  return NULL;

	return ToNewCString(nodeValue);
}


static gboolean
mozilla_get_link_from_node (nsIDOMDocument *domDoc, nsIDOMNode *node,
			    gchar **url, gchar **inner_html)
{
	if (inner_html) *inner_html = NULL;
	if (url) *url = NULL;

	// get url
	char *hrefattr =  mozilla_get_attribute(node, "href");
	if (!hrefattr) return FALSE;

	nsAutoString hrefa;
	hrefa.AssignWithConversion(hrefattr);

	nsCString hrefc,linkc;
	hrefc.AssignWithConversion(hrefa);

	nsIURI *baseURI;
	nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
	nsresult rv;
#if MOZILLA_SNAPSHOT > 12
	baseURI = doc->GetBaseURI();
#elif MOZILLA_SNAPSHOT > 11
	baseURI = doc->GetBaseURL();
#elif MOZILLA_SNAPSHOT > 9
	rv = doc->GetBaseURL(&baseURI);
#else
	rv = doc->GetBaseURL(baseURI);
#endif	
	rv = baseURI->Resolve(hrefc,linkc);
	*url = ToNewCString(linkc);

	g_free(hrefattr);

	// get title
	nsAutoString nodename;
	node->GetNodeName(nodename);
	nsCOMPtr<nsIDOMNamedNodeMap> attributes;
	node->GetAttributes(getter_AddRefs(attributes));

	nsCOMPtr<nsIDOMNode> hrefNode;
	nsAutoString href; 
	href.AssignWithConversion("href");
	attributes->GetNamedItem(href, getter_AddRefs(hrefNode));
	if (!hrefNode) return FALSE;

	nsAutoString linkhtml;
	nsCOMPtr<nsIDOMNSHTMLElement> nsElement;

	nsCOMPtr<nsIDOMHTMLElement> element;
	element = do_QueryInterface(node);

	nsElement = do_QueryInterface(element);
	if (!nsElement) return FALSE;

	rv = nsElement->GetInnerHTML(linkhtml);
	if (NS_SUCCEEDED(rv) &&
	    NS_ConvertUCS2toUTF8(linkhtml).get()) 
	{
		*inner_html = g_strdup(NS_ConvertUCS2toUTF8(linkhtml).get());
	}

	return TRUE;
}


static glong
mozilla_set_event_context (KzMozEmbed *kzembed,
			   nsIDOMEventTarget *target,
			   KzEmbedEvent *info)
{
	nsresult result;

	nsCOMPtr<nsIDOMNode> node = do_QueryInterface(target);
	if (!node) return KZ_CONTEXT_NONE;

     	nsCOMPtr<nsIDOMDocument> domDoc;
     	result = node->GetOwnerDocument(getter_AddRefs(domDoc));
     	if (!NS_SUCCEEDED (result) || !domDoc) return KZ_CONTEXT_NONE;

     	nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
     	if(!doc) return KZ_CONTEXT_NONE;

     	nsCOMPtr<nsIDOMNSDocument> nsDoc = do_QueryInterface(domDoc);
     	if (!nsDoc) return KZ_CONTEXT_NONE;

	nsIURI *baseURI;
#if MOZILLA_SNAPSHOT > 12
	baseURI = doc->GetBaseURI();
#elif MOZILLA_SNAPSHOT > 11
	baseURI = doc->GetBaseURL();
#elif MOZILLA_SNAPSHOT > 9
	result = doc->GetBaseURL(&baseURI);
#else
	result = doc->GetBaseURL(baseURI);
#endif
	if (NS_FAILED(result) || !baseURI) return KZ_CONTEXT_NONE;

     	nsString mime;
     	nsDoc->GetContentType(mime);
     	if (mime.EqualsWithConversion("text/xul"))  return KZ_CONTEXT_NONE;

	PRUint32 flags = KZ_CONTEXT_NONE;

	// check framed page
	nsCOMPtr<nsIDOMDocument> mainDocument;
	result = kzembed->priv->wrapper->GetMainDomDocument (getter_AddRefs(mainDocument));
	if (domDoc != mainDocument)
	{
		flags |= KZ_CONTEXT_FRAME;
		nsCAutoString url;
		baseURI->GetSpec(url);		
		info->frame_src = g_strdup(url.get());
	}
	
	// check whether the node is in the selection or not
	nsCOMPtr<nsISelection> selection;
	result = kzembed->priv->wrapper->GetSelection(getter_AddRefs(selection));
	if (selection)
	{
		PRBool contains;
		selection->ContainsNode(node, PR_TRUE, &contains);
		if (contains)
			flags |= KZ_CONTEXT_SELECTION;
	}

	// Get other context
	nsCOMPtr<nsIDOMHTMLElement> element;

     	do {
		PRUint16 type;
		node->GetNodeType(&type);

		element = do_QueryInterface(node);
		if (element)
		{
			nsAutoString tag;
			element->GetTagName(tag);

			if (tag.EqualsWithConversion("input", PR_TRUE))
			{
				flags |= KZ_CONTEXT_INPUT;
			}
			else if (tag.EqualsWithConversion("textarea", PR_TRUE))
			{
				flags |= KZ_CONTEXT_INPUT | KZ_CONTEXT_TEXTAREA;
				info->element = (void*)element;
			}
			else if (tag.EqualsWithConversion("img", PR_TRUE))
			{
				flags |= KZ_CONTEXT_IMAGE;

				char *src = mozilla_get_attribute(node, "src");
				if (!src)  return KZ_CONTEXT_NONE;

			     	nsAutoString srca;
			     	srca.AssignWithConversion(src);

			     	nsCString srcc,imgc;
			     	srcc.AssignWithConversion(srca);

			     	result = baseURI->Resolve(srcc, imgc);
			     	g_free(src);

			     	info->img = ToNewCString(imgc);

			     	if (!info->img) return KZ_CONTEXT_NONE;
			}
			else
			{
				flags |= KZ_CONTEXT_OTHER;
			}

			nsCOMPtr<nsIDOMNamedNodeMap> attributes;
			node->GetAttributes(getter_AddRefs(attributes));
			if (attributes)
			{
				nsCOMPtr<nsIDOMNode> hrefNode;
				nsAutoString href; 
			     	href.AssignWithConversion("href");
				attributes->GetNamedItem(href, getter_AddRefs(hrefNode));
				if (hrefNode)
				{
					flags |= KZ_CONTEXT_LINK;

					mozilla_get_link_from_node(domDoc, node,
								   &info->link,
								   &info->linktext);
					if (!info->link)
					{
						g_free(info->linktext);
						return KZ_CONTEXT_NONE;
					}
					break;
				}
			}
		}

		nsCOMPtr<nsIDOMNode> parentNode;
		node->GetParentNode(getter_AddRefs(parentNode));

		if (!parentNode)
		{
			node = nsnull;
			flags |= KZ_CONTEXT_DOCUMENT;
			break;
		}
		node = parentNode;
	} while (node);

	info->context = flags;

	return flags;
}


static gboolean
mozilla_get_head_link_from_node (nsIDOMDocument *domDoc, nsIDOMNode *node,
				 gchar **url)
{
	if (url) *url = NULL;

	// get url
	char *hrefattr =  mozilla_get_attribute(node, "href");
	if (!hrefattr)
		return FALSE;	

	
	nsAutoString hrefa;
	hrefa.AssignWithConversion(hrefattr);

	nsCString hrefc,linkc;
	hrefc.AssignWithConversion(hrefa);

	nsIURI *baseURI;
	nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
	nsresult rv;
#if MOZILLA_SNAPSHOT > 12
	baseURI = doc->GetBaseURI();
#elif MOZILLA_SNAPSHOT > 11
	baseURI = doc->GetBaseURL();
#elif MOZILLA_SNAPSHOT > 9
	rv = doc->GetBaseURL(&baseURI);
#else
	rv = doc->GetBaseURL(baseURI);
#endif	
	rv = baseURI->Resolve(hrefc,linkc);
	*url = ToNewCString(linkc);

	g_free(hrefattr);
	return TRUE;
}

static gchar *
kz_moz_embed_get_up_location(KzMozEmbed *kzembed)
{
	const gchar *location;
	gchar *up_location = NULL;
	gchar *pos, *dummy;
	int len; 

	location = kz_moz_embed_get_location(kzembed);
	if (!location)
		return NULL;

	len = strlen(location);
	if (location[len - 1] == '/')
		dummy = g_strndup(location, len - 1);
	else 
		dummy = g_strndup(location, len);
	pos =  strrchr(dummy, '/');
	if (pos)
		up_location = g_strndup(dummy, pos - dummy + 1);
	g_free(dummy);
	return up_location;
}

void
kz_moz_embed_reload(KzMozEmbed *kzembed, gint32 flags)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	gtk_moz_embed_reload(GTK_MOZ_EMBED(kzembed), flags);
}


void
kz_moz_embed_stop_load(KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	gtk_moz_embed_stop_load(GTK_MOZ_EMBED(kzembed));
}


void
kz_moz_embed_go_back(KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	gtk_moz_embed_go_back(GTK_MOZ_EMBED(kzembed));
}


void
kz_moz_embed_go_up(KzMozEmbed *kzembed)
{
	gchar *location;
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

	location = kz_moz_embed_get_up_location(kzembed);
	kz_moz_embed_load_url(kzembed, location);
	g_free(location);
}

void
kz_moz_embed_go_forward(KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	gtk_moz_embed_go_forward(GTK_MOZ_EMBED(kzembed));
}


gboolean
kz_moz_embed_can_go_back(KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);
	return gtk_moz_embed_can_go_back(GTK_MOZ_EMBED(kzembed));
}

gboolean
kz_moz_embed_can_go_forward(KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);
	return gtk_moz_embed_can_go_forward(GTK_MOZ_EMBED(kzembed));
}

gboolean
kz_moz_embed_can_go_up(KzMozEmbed *kzembed)
{
	gboolean ret;
	gchar *location;
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);
	
	location = kz_moz_embed_get_up_location(kzembed);

	if (!location)
		return FALSE;

	/* stupid! */
	if (strcmp(location, "http://") &&
	    strcmp(location, "ftp://")  &&
	    strcmp(location, "file://"))
		ret = TRUE;
	else
		ret = FALSE;
	g_free(location);

	return ret;
}

gboolean
kz_moz_embed_can_go_index(KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);
	if(kzembed->priv->link_index)
		return TRUE;
	return FALSE;
}

gboolean
kz_moz_embed_can_go_contents(KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);
	if(kzembed->priv->link_contents)
		return TRUE;
	return FALSE;
}

gboolean
kz_moz_embed_can_go_start(KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);
	if(kzembed->priv->link_start)
		return TRUE;
	return FALSE;
}
gboolean
kz_moz_embed_can_go_prev(KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);
	if(kzembed->priv->link_prev)
		return TRUE;
	return FALSE;
}

gboolean
kz_moz_embed_can_go_next(KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);
	if(kzembed->priv->link_next)
		return TRUE;
	return FALSE;
}

void
kz_moz_embed_go_index (KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	g_return_if_fail(kzembed->priv->link_index);

	kz_moz_embed_load_url(kzembed,
			      kzembed->priv->link_index);
}

void
kz_moz_embed_go_contents (KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	g_return_if_fail(kzembed->priv->link_contents);

	kz_moz_embed_load_url(kzembed,
			      kzembed->priv->link_contents);
}

void
kz_moz_embed_go_start (KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	g_return_if_fail(kzembed->priv->link_start);

	kz_moz_embed_load_url(kzembed,
			      kzembed->priv->link_start);
}

void
kz_moz_embed_go_next (KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	g_return_if_fail(kzembed->priv->link_next);

	kz_moz_embed_load_url(kzembed,
			      kzembed->priv->link_next);
}

void
kz_moz_embed_go_prev (KzMozEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	g_return_if_fail(kzembed->priv->link_prev);

	kz_moz_embed_load_url(kzembed,
			      kzembed->priv->link_prev);
}


void
kz_moz_embed_copy_page (KzMozEmbed   *kzembed, KzMozEmbed   *dkzembed)
{
	KzMozWrapper *dWrapper = KZ_MOZ_EMBED(dkzembed)->priv->wrapper;;
	KzMozWrapper *sWrapper = KZ_MOZ_EMBED(kzembed)->priv->wrapper;;

        nsresult rv;

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

        rv = dWrapper->LoadDocument(pageDescriptor, 0);
}


gboolean
kz_moz_embed_shistory_copy (KzMozEmbed *source,
			    KzMozEmbed *dest,
			    gboolean back_history,
			    gboolean forward_history,
			    gboolean set_current)
{
	nsresult rv;
	KzMozWrapper *s_wrapper = KZ_MOZ_EMBED(source)->priv->wrapper;
	KzMozWrapper *d_wrapper = KZ_MOZ_EMBED(dest)->priv->wrapper;
	
	rv = s_wrapper->CopyHistoryTo (d_wrapper, back_history, forward_history, set_current);

	return NS_SUCCEEDED(rv) ? TRUE : FALSE;
}


/* picked from galeon-1.3.11a */
gboolean
kz_moz_embed_shistory_get_pos (KzMozEmbed *kzembed,
			       int *pos, int *count)
{
	KzMozWrapper *wrapper = kzembed->priv->wrapper;
	nsresult rv;
	int total, index;

	rv = wrapper->GetSHInfo (&total, &index);

	*pos = index;
	*count = total;

	return NS_SUCCEEDED(rv) ? TRUE : FALSE;
}

/* picked from galeon-1.3.11a */
void
kz_moz_embed_shistory_get_nth (KzMozEmbed *kzembed, 
			       int nth,
			       gboolean is_relative,
			       char **aUrl,
			       char **aTitle)
{
	KzMozWrapper *wrapper = kzembed->priv->wrapper;
	nsresult rv;

	if (is_relative)
	{
		int pos, count;
		if(kz_moz_embed_shistory_get_pos(kzembed, &pos, &count))
			pos += nth;
		else
			return;
		nth = pos;
	}
	
        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);
}


gboolean
kz_moz_embed_get_lock (KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);
	return kzembed->priv->lock;
}


void
kz_moz_embed_set_lock (KzMozEmbed *kzembed, gboolean lock)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	kzembed->priv->lock = lock;
}


gchar *
kz_moz_embed_get_body_text(KzMozEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);
	g_return_val_if_fail(kzembed->priv->wrapper, FALSE);

	nsAutoString text;
	kzembed->priv->wrapper->GetBodyString(text);
	return g_strdup(NS_ConvertUCS2toUTF8(text).get());
}


void
kz_moz_embed_set_encoding (KzMozEmbed *kzembed,
			   const char *encoding)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	g_return_if_fail(kzembed->priv->wrapper);

	kzembed->priv->wrapper->ForceEncoding(encoding);
}

void
kz_moz_embed_get_encoding (KzMozEmbed *kzembed,
			   char **encoding,
			   gboolean *forced)
{
	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));
	g_return_if_fail(kzembed->priv->wrapper);

	kzembed->priv->wrapper->GetEncoding(encoding, *forced);
}

gboolean 
kz_moz_embed_save_with_content (KzMozEmbed *kzembed, const char *rawfilename)
{
	nsresult rv;
	KzMozWrapper *wrapper = NULL;
	PRUint32 persistFlags = 0;
	
	nsCOMPtr<nsIWebBrowserPersist> bpersist = 
		do_CreateInstance(NS_WEBBROWSERPERSIST_CONTRACTID);
	if (!bpersist) return FALSE;

	nsCOMPtr<nsIURI> linkURI;
	linkURI = nsnull;

	nsCAutoString filename(rawfilename);
        nsCOMPtr<nsILocalFile> file;
       	NS_NewNativeLocalFile(filename, PR_TRUE, getter_AddRefs(file)); 
        if (!file) return FALSE;

	nsCOMPtr<nsILocalFile> path;
	char *datapath;
	datapath = g_strconcat (rawfilename, ".content", NULL);
	NS_NewLocalFile(NS_ConvertUTF8toUCS2(datapath), PR_TRUE, getter_AddRefs(path));
	g_free (datapath);
	
	wrapper = kzembed->priv->wrapper;
	g_return_val_if_fail (wrapper != NULL, FALSE);	

	persistFlags = nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES;

	size_t len = strlen(rawfilename);
	if((rawfilename[len-1] == 'z' && rawfilename[len-2] == 'g') ||
	   (rawfilename[len-1] == 'Z' && rawfilename[len-2] == 'G'))
	{
                persistFlags |= nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION;
	}

	persistFlags |= nsIWebBrowserPersist::PERSIST_FLAGS_BYPASS_CACHE;
	bpersist->SetPersistFlags (persistFlags);

	g_return_val_if_fail (wrapper != NULL, FALSE);
		
	nsCOMPtr<nsIDOMDocument> DOMDocument;

	rv = wrapper->GetMainDomDocument (getter_AddRefs(DOMDocument));
	if (NS_FAILED(rv) || !DOMDocument) return FALSE;

	nsCOMPtr<nsIDocument> document =
		do_QueryInterface (DOMDocument, &rv);
	if (NS_FAILED(rv) || !document) return FALSE;

	nsCOMPtr<nsIURI> inURI;
	nsCAutoString sURI;

	rv = wrapper->GetDocumentUrl(sURI);
      	rv = NS_NewURI(getter_AddRefs(inURI), sURI);
	
	// FIXME! Use ProgressListener. 
	// KzMozProgressListener *aProgress = new KzMozProgressListener ();
	// aProgress->Init(inURI, path, 
	//		nsnull, nsnull, 0, bpersist);

	rv = bpersist->SaveDocument (DOMDocument, file, path, nsnull, 0, 0);
	if (NS_FAILED(rv)) return FALSE;

	return TRUE;
}


void
kz_moz_embed_print (KzMozEmbed *kzembed)
{
	KzMozWrapper *wrapper = NULL;

	wrapper = kzembed->priv->wrapper;
	g_return_if_fail (wrapper != NULL);

	wrapper->Print();
}


void
kz_moz_embed_print_preview (KzMozEmbed *kzembed)
{
	KzMozWrapper *wrapper = NULL;

	wrapper = kzembed->priv->wrapper;
	g_return_if_fail (wrapper != NULL);
	
	wrapper->PrintPreview();
}


gchar *
kz_moz_embed_get_text_from_textarea (KzMozEmbed *kzembed,
				     gpointer element)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), NULL);
	
	nsISupports *s = (nsISupports*)element;

	nsCOMPtr<nsIDOMHTMLTextAreaElement> tElement = do_QueryInterface(s);

	g_return_val_if_fail(tElement, NULL);

	nsAutoString string;
	tElement->GetValue(string);
	
	return g_strdup(NS_ConvertUCS2toUTF8(string).get());
}


gboolean
kz_moz_embed_set_text_into_textarea (KzMozEmbed *kzembed,
				     gpointer element,
				     const gchar *text)
{
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), FALSE);

	nsISupports *s = (nsISupports*)element;

	nsCOMPtr<nsIDOMHTMLTextAreaElement> tElement = do_QueryInterface(s);

	if (!tElement) return FALSE;

	nsAutoString string = NS_ConvertUTF8toUCS2(text);
	tElement->SetValue(string);
	
	return TRUE;
}


gchar *
kz_moz_embed_store_history_file(KzMozEmbed *kzembed)
{
	nsresult rv;
	KzMozWrapper *wrapper = NULL;
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), NULL);

	wrapper = kzembed->priv->wrapper;
	g_return_val_if_fail (wrapper != NULL, NULL);

	PRUint32 persistFlags = 0;
	
	nsCOMPtr<nsIWebBrowserPersist> bpersist = 
		do_CreateInstance(NS_WEBBROWSERPERSIST_CONTRACTID);
	if (!bpersist) return NULL;

	persistFlags = nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES;

	persistFlags |= nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE;
	bpersist->SetPersistFlags (persistFlags);

	nsCOMPtr<nsIURI> inURI;
	nsCAutoString sURI;

	rv = wrapper->GetDocumentUrl(sURI);
      	rv = NS_NewURI(getter_AddRefs(inURI), sURI);

	gchar *dir, *filename;
	gchar *uri = ToNewCString(sURI);

	filename = create_filename_with_path_from_uri(uri);
		
	if (!filename)
		filename = g_strdup("index.html");

	dir = g_strconcat(g_get_home_dir(),
			  "/.kazehakase/history/",
			  filename,
			  NULL);
	g_free(filename);
	g_free(uri);

	nsCOMPtr<nsILocalFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
	rv = localFile->InitWithPath(NS_ConvertUTF8toUCS2(dir));

	if (NS_FAILED(rv))
		return NULL;

	PRBool exists;
	localFile->Exists(&exists);
	if (!exists)
	{
		rv = localFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
		if (NS_FAILED(rv))
			return NULL;
	}

	nsCOMPtr<nsISupports> pageDescriptor;

	wrapper->GetPageDescriptor(getter_AddRefs(pageDescriptor));
	
	rv = bpersist->SaveURI (inURI, pageDescriptor, nsnull,
				nsnull, nsnull, localFile);
	
	return dir;
}

