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

//
//  Copyright (C) 2007 Hiroyuki Ikezoe
//
//  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-embed-module-impl.h"

#include "kz-gecko-single.h"

#define KZ_REGRESSION_BUG4474 1

#include <string.h>
#include <math.h>
#include <gtkmozembed.h>
#include <gtkmozembed_internal.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <sys/stat.h>

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

#include "kz-search.h"

#include <nsCOMPtr.h>
#include <nsIDOMDocument.h>
#include <nsIDocumentViewer.h>
#include <nsIWebBrowser.h>
#include <nsIDOMMouseEvent.h>
#include <dom/nsIDOMKeyEvent.h>
#include <dom/nsIDOMNSHTMLElement.h>
#include <nsIDOMHTMLElement.h>
#include <nsIDOMHTMLDocument.h>
#include <nsIDOMHTMLTextAreaElement.h>
#include <nsIDOMNamedNodeMap.h>
#include <nsIDOMDocumentRange.h>
#include <nsIDOMDocumentFragment.h>
#include <nsIDOMSerializer.h>
#include <nsIDOMText.h>
#include <webbrowserpersist/nsIWebBrowserPersist.h>
#include <nsIWebBrowserFind.h>
#include <nsIFind.h>
#include <dom/nsIDOMNSDocument.h>
#include <dom/nsIDOMNSEvent.h>
#include <nsIDOMNodeList.h>
#include <nsIDOMWindow.h>
#include <nsISelection.h>
#include <nsIDOMRange.h>
#include <nsIDOMWindow.h>
#include <nsISelection.h>
#include <nsISHistory.h>
#include <nsIHistoryEntry.h>
#include <nsISHEntry.h>
#include <nsISHistoryInternal.h>
#include <nsIWebNavigation.h>
#include <nsCWebBrowserPersist.h>
#include <widget/nsIBaseWindow.h>
#include <nsIWebPageDescriptor.h>
#include <nsICommandManager.h>
#include <nsTime.h>
#include <nsRect.h>
#define MOZILLA_STRICT_API
#include <nsEmbedString.h>
#undef MOZILLA_STRICT_API
#include <nsIServiceManager.h>
#include <nsIInterfaceRequestorUtils.h>
#include <nsMemory.h>
#include <nsILocalFile.h>
#include <nsIDOM3Node.h>

#define KZ_GECKO_EMBED_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_GECKO_EMBED, KzGeckoEmbedPrivate))

typedef struct _KzGeckoEmbedPrivate	KzGeckoEmbedPrivate;
struct _KzGeckoEmbedPrivate
{
	KzMozWrapper *wrapper;
	gint size_inited;
	gint cur_requests;
	gint total_requests;

	/* location and title */
	gchar *location;
	gchar *title;

	gint load_started; /* count of currently active connections */
	gint load_percent;
	gint bytes_loaded;
	gint max_bytes_loaded;
	gboolean  is_loading;
	const gchar *load_status_message;

	gboolean lock;

	/* for navigation link */
	GList *nav_links[KZ_EMBED_LINK_GUARD];

#ifdef USE_MIGEMO
	gchar *migemo_keyword;
#endif
	gchar *last_highlight;
	gchar *first_url;
};

typedef struct _KzGeckoEmbedClass	KzGeckoEmbedClass;
struct _KzGeckoEmbedClass
{
	GtkMozEmbedClass parent_class;
};

static GType kz_type_gecko_embed = 0;
static GtkMozEmbedClass *kz_gecko_embed_parent_class;

static GtkWidget *kz_gecko_embed_new     (const gchar *url);

static void kz_gecko_embed_iface_init    (KzEmbedIFace *iface);

static void kz_gecko_embed_destroy       (GtkObject   *object);
static void kz_gecko_embed_realize       (GtkWidget   *widget);
static void kz_gecko_embed_unrealize     (GtkWidget   *widget);
static void kz_gecko_embed_size_allocate (GtkWidget   *widget,
					  GtkAllocation *allocation);
static void kz_gecko_embed_destroy_brsr  (GtkMozEmbed *embed);
static void kz_gecko_embed_link_message  (GtkMozEmbed *embed);
static void kz_gecko_embed_js_status     (GtkMozEmbed *embed);
static void kz_gecko_embed_title         (GtkMozEmbed *embed);
static void kz_gecko_embed_location      (GtkMozEmbed *embed);
static void kz_gecko_embed_net_start     (GtkMozEmbed *embed);
static void kz_gecko_embed_new_window    (GtkMozEmbed *embed,
					  GtkMozEmbed **newembed,
					  guint        chromemask);
static gint kz_gecko_embed_open_uri      (GtkMozEmbed *embed,
					  const char  *uri);
static void kz_gecko_embed_size_to       (GtkMozEmbed *embed,
					  gint         width,
					  gint         height);
static void kz_gecko_embed_visibility    (GtkMozEmbed *embed,
					  gboolean     visibility);
static void kz_gecko_embed_net_stop      (GtkMozEmbed *embed);
static void kz_gecko_embed_net_state_all (GtkMozEmbed *embed,
					  const char  *aURI,
					  gint         state,
					  guint        status);
#if 0
static void kz_gecko_embed_security_change (GtkMozEmbed *embed,
					    gpointer     request,
					    guint        state);
#endif
static gint kz_gecko_embed_dom_key_down        (GtkMozEmbed *embed, gpointer event);
static gint kz_gecko_embed_dom_key_up          (GtkMozEmbed *embed, gpointer event);
static gint kz_gecko_embed_dom_key_press       (GtkMozEmbed *embed, gpointer event);
static gint kz_gecko_embed_dom_mouse_down      (GtkMozEmbed *embed, gpointer event);
static gint kz_gecko_embed_dom_mouse_up        (GtkMozEmbed *embed, gpointer event);
static gint kz_gecko_embed_dom_mouse_click     (GtkMozEmbed *embed, gpointer event);
static gint kz_gecko_embed_dom_mouse_dbl_click (GtkMozEmbed *embed, gpointer event);
static gint kz_gecko_embed_dom_mouse_over      (GtkMozEmbed *embed, gpointer event);


static void  kz_gecko_embed_navigation_link_free(KzGeckoEmbed *kzembed);
static glong kz_gecko_embed_get_key_event_info  (KzGeckoEmbed *kzembed,
						 gpointer      event,
						 KzEmbedEventKey **info_ret);
static glong kz_gecko_embed_get_mouse_event_info(KzGeckoEmbed *kzembed,
						 gpointer      event,
						 KzEmbedEventMouse **info_ret);
static glong kz_gecko_embed_set_event_context   (KzGeckoEmbed *kzembed,
						 nsIDOMEventTarget *target,
						 KzEmbedEvent *info);
static gchar *kz_gecko_embed_store_history_file (KzGeckoEmbed *kzembed);

static void         kz_gecko_embed_load_url              (KzEmbed      *kzembed,
							  const gchar  *url);
static void         kz_gecko_embed_view_source           (KzEmbed      *kzembed,
							  const gchar  *url);

static gboolean     kz_gecko_embed_is_loading            (KzEmbed      *kzembed);

static const gchar *kz_gecko_embed_get_title             (KzEmbed      *kzembed);
static const gchar *kz_gecko_embed_get_location          (KzEmbed      *kzembed);
static gchar       *kz_gecko_embed_ensure_title          (KzEmbed      *kzembed);
static gchar       *kz_gecko_embed_get_link_message      (KzEmbed      *kzembed);

static gdouble      kz_gecko_embed_get_progress          (KzEmbed      *kzembed);

static gboolean     kz_gecko_embed_can_cut_selection     (KzEmbed      *kzembed);
static gboolean     kz_gecko_embed_can_copy_selection    (KzEmbed      *kzembed);
static gboolean     kz_gecko_embed_can_paste             (KzEmbed      *kzembed);
static void         kz_gecko_embed_cut_selection         (KzEmbed      *kzembed);
static void         kz_gecko_embed_copy_selection        (KzEmbed      *kzembed);
static void         kz_gecko_embed_paste                 (KzEmbed      *kzembed);
static void         kz_gecko_embed_select_all            (KzEmbed      *kzembed);

static gchar       *kz_gecko_embed_get_selection_string  (KzEmbed      *kzembed);

static gboolean     kz_gecko_embed_find                  (KzEmbed      *kzembed,
							  const char   *keyword,
							  gboolean      backward);
static gboolean     kz_gecko_embed_incremental_search    (KzEmbed      *kzembed,
							  const char   *keyword,
							  gboolean      backward);

static gboolean     kz_gecko_embed_selection_is_collapsed(KzEmbed      *kzembed);

static gboolean     kz_gecko_embed_get_links             (KzEmbed      *kzembed,
							  GList       **list,
							  gboolean      selected_only);

static gboolean     kz_gecko_embed_get_dest_anchors      (KzEmbed      *kzembed,
							  GList       **list);

static void         kz_gecko_embed_copy_page             (KzEmbed      *kzembed,
							  KzEmbed      *dkzembed,
							  KzEmbedCopyType type);
static gboolean     kz_gecko_embed_shistory_copy         (KzEmbed      *source,
							  KzEmbed      *dest,
							  gboolean      back_history,
							  gboolean      forward_history,
							  gboolean      set_current);
static gboolean     kz_gecko_embed_shistory_get_pos      (KzEmbed      *kzembed,
							  int          *pos,
							  int          *count);
static void         kz_gecko_embed_shistory_get_nth      (KzEmbed      *kzembed, 
							  int           nth,
							  gboolean      is_relative,
							  char        **aUrl,
							  char        **aTitle);

static void         kz_gecko_embed_reload                (KzEmbed      *kzembed,
							  KzEmbedReloadFlag flags);
static void         kz_gecko_embed_stop_load             (KzEmbed      *kzembed);
static void         kz_gecko_embed_go_back               (KzEmbed      *kzembed);
static void         kz_gecko_embed_go_forward            (KzEmbed      *kzembed);

static gboolean     kz_gecko_embed_can_go_back           (KzEmbed      *kzembed);
static gboolean     kz_gecko_embed_can_go_forward        (KzEmbed      *kzembed);
static gboolean     kz_gecko_embed_can_go_nav_link       (KzEmbed      *kzembed,
							  KzEmbedNavLink link);
static void         kz_gecko_embed_go_nav_link           (KzEmbed      *kzembed,
							  KzEmbedNavLink link);
static void         kz_gecko_embed_append_nav_link       (KzEmbed      *kzembed,
							  KzEmbedNavLink link,
							  KzNavi       *navi);
static void         kz_gecko_embed_set_nth_nav_link      (KzEmbed      *kzembed,
							  KzEmbedNavLink link,
							  KzNavi       *navi,
							  guint         n);
static KzNavi      *kz_gecko_embed_get_nth_nav_link      (KzEmbed      *kzembed,
							  KzEmbedNavLink link,
							  guint         n);
static GList       *kz_gecko_embed_get_nav_links         (KzEmbed      *kzembed,
							  KzEmbedNavLink link);
static void         kz_gecko_embed_go_history_index      (KzEmbed      *kzembed,
							  gint          index);

static void         kz_gecko_embed_do_command            (KzEmbed      *kzembed,
							  const char   *command);
static gboolean     kz_gecko_embed_can_do_command        (KzEmbed      *kzembed,
							  const char   *command);

static gboolean     kz_gecko_embed_get_lock              (KzEmbed      *kzembed);
static void         kz_gecko_embed_set_lock              (KzEmbed      *kzembed,
							  gboolean      lock);

static gchar       *kz_gecko_embed_get_body_text         (KzEmbed      *kzembed);
#if 0
static gchar       *kz_gecko_embed_get_selection_source  (KzEmbed      *kzembed);
#endif
static void         kz_gecko_embed_set_encoding          (KzEmbed      *kzembed,
							  const char   *encoding);
static void         kz_gecko_embed_get_encoding          (KzEmbed      *kzembed,
							  char        **encoding,
							  gboolean     *forced);
static void         kz_gecko_embed_print                 (KzEmbed      *kzembed);
static void         kz_gecko_embed_print_preview         (KzEmbed      *kzembed);
static GList       *kz_gecko_embed_get_printer_list      (KzEmbed      *kzembed);
static void         kz_gecko_embed_create_thumbnail      (KzEmbed      *kzembed);


static gboolean     kz_gecko_embed_save_with_content     (KzEmbed      *kzembed,
							  const char   *rawfilename);

static gboolean     kz_gecko_embed_set_text_into_textarea(KzEmbed      *kzembed,
							  gpointer      element,
							  const gchar  *text);
static gchar       *kz_gecko_embed_get_text_from_textarea(KzEmbed      *kzembed,
							  gpointer      element);


static void         kz_gecko_embed_zoom_set              (KzEmbed      *kzembed, 
							  int           zoom, 
							  gboolean      reflow);
static int          kz_gecko_embed_zoom_get              (KzEmbed      *kzembed);
static void         kz_gecko_embed_set_text_size         (KzEmbed      *kzembed, 
							  int           zoom, 
							  gboolean      reflow);
static int          kz_gecko_embed_get_text_size         (KzEmbed      *kzembed);

static gchar       *kz_gecko_embed_get_html_with_contents(KzEmbed      *kzembed,
							  const gchar  *storedir);

static void	     kz_gecko_embed_set_history           (KzEmbed      *kzembed,
							   KzBookmark   *history);
static void	     kz_gecko_embed_get_history           (KzEmbed      *kzembed,
							   KzBookmark   *history);
static guint	     kz_gecko_embed_get_last_modified     (KzEmbed      *kzembed);


#if 0
static void         kz_gecko_embed_set_edit_mode         (KzEmbed      *kzembed);
static void         kz_gecko_embed_set_view_mode         (KzEmbed      *kzembed);
#endif
static void         kz_gecko_embed_fine_scroll            (KzEmbed      *kzembed,
							   int           horiz, 
							   int           vert);
static void	     kz_gecko_embed_page_up		  (KzEmbed      *kzembed);
static void	     kz_gecko_embed_page_down		  (KzEmbed      *kzembed);

static gboolean	     kz_gecko_embed_get_allow_javascript  (KzEmbed      *kzembed);
static void	     kz_gecko_embed_set_allow_javascript  (KzEmbed      *kzembed,
    							   gboolean      allow);
static gboolean	     kz_gecko_embed_get_allow_images	  (KzEmbed      *kzembed);
static void	     kz_gecko_embed_set_allow_images	  (KzEmbed      *kzembed,
							   gboolean      allow);
static void          kz_gecko_embed_show_page_certificate (KzEmbed       *kzembed);

/* KzGeckoEmbed Class */
static void   kz_gecko_embed_class_init (KzGeckoEmbedClass *klass);
static void   kz_gecko_embed_init       (KzGeckoEmbed *embed);

static void
kz_gecko_embed_register_type (GTypeModule *module)
{
	static const GTypeInfo kz_gecko_embed_info =
	{
		sizeof (KzGeckoEmbedClass),
		NULL,		/* base_init */
		NULL,		/* base_finalize */
		(GClassInitFunc) kz_gecko_embed_class_init,
		NULL,		/* class_finalize */
		NULL,		/* class_data */
		sizeof (KzGeckoEmbed),
		0,		/* n_preallocs */
		(GInstanceInitFunc) kz_gecko_embed_init,
	};

	const GInterfaceInfo kz_embed_info =
	{
		(GInterfaceInitFunc) kz_gecko_embed_iface_init,
		NULL,
		NULL
	};

	kz_type_gecko_embed = g_type_module_register_type(module,
							  GTK_TYPE_MOZ_EMBED,
							  "KzGeckoEmbed",
							  &kz_gecko_embed_info,
							  (GTypeFlags)0);

	g_type_module_add_interface(module,
				    KZ_TYPE_GECKO_EMBED,
				    KZ_TYPE_EMBED,
				    &kz_embed_info);
}

G_MODULE_EXPORT void
KZ_EMBED_MODULE_IMPL_INIT (GTypeModule *module)
{
	kz_gecko_embed_register_type(module);
}

G_MODULE_EXPORT void
KZ_EMBED_MODULE_IMPL_EXIT (void)
{
}

G_MODULE_EXPORT GtkWidget *
KZ_EMBED_MODULE_IMPL_CREATE (const gchar *url)
{
	return kz_gecko_embed_new(url);
}

GType
kz_gecko_embed_get_type (void)
{
       return kz_type_gecko_embed;
}

static void
kz_gecko_embed_iface_init (KzEmbedIFace *iface)
{
	iface->load_url               = kz_gecko_embed_load_url;
	iface->view_source            = kz_gecko_embed_view_source;
	iface->is_loading             = kz_gecko_embed_is_loading;
	iface->get_title              = kz_gecko_embed_get_title;
	iface->get_location           = kz_gecko_embed_get_location;
	iface->ensure_title           = kz_gecko_embed_ensure_title;
	iface->get_link_message       = kz_gecko_embed_get_link_message;
	iface->get_progress           = kz_gecko_embed_get_progress;
	iface->can_cut_selection      = kz_gecko_embed_can_cut_selection;
	iface->can_copy_selection     = kz_gecko_embed_can_copy_selection;
	iface->can_paste              = kz_gecko_embed_can_paste;
	iface->cut_selection          = kz_gecko_embed_cut_selection;
	iface->copy_selection         = kz_gecko_embed_copy_selection;
	iface->paste                  = kz_gecko_embed_paste;
	iface->select_all             = kz_gecko_embed_select_all;
	iface->get_selection_string   = kz_gecko_embed_get_selection_string;
	iface->find                   = kz_gecko_embed_find;
	iface->incremental_search     = kz_gecko_embed_incremental_search;
	iface->selection_is_collapsed = kz_gecko_embed_selection_is_collapsed;
	iface->get_links              = kz_gecko_embed_get_links;
	iface->get_dest_anchors       = kz_gecko_embed_get_dest_anchors;
	iface->copy_page              = kz_gecko_embed_copy_page;
	iface->shistory_copy          = kz_gecko_embed_shistory_copy;
	iface->shistory_get_pos       = kz_gecko_embed_shistory_get_pos;
	iface->shistory_get_nth       = kz_gecko_embed_shistory_get_nth;
	iface->reload                 = kz_gecko_embed_reload;
	iface->stop_load              = kz_gecko_embed_stop_load;
	iface->go_back                = kz_gecko_embed_go_back;
	iface->go_forward             = kz_gecko_embed_go_forward;
	iface->can_go_back            = kz_gecko_embed_can_go_back;
	iface->can_go_forward         = kz_gecko_embed_can_go_forward;
	iface->can_go_nav_link        = kz_gecko_embed_can_go_nav_link;
	iface->go_nav_link            = kz_gecko_embed_go_nav_link;
	iface->append_nav_link        = kz_gecko_embed_append_nav_link;
	iface->set_nav_link           = NULL;
	iface->set_nth_nav_link       = kz_gecko_embed_set_nth_nav_link;
	iface->get_nav_link           = NULL;
	iface->get_nth_nav_link       = kz_gecko_embed_get_nth_nav_link;
	iface->get_nav_links          = kz_gecko_embed_get_nav_links;
	iface->go_history_index       = kz_gecko_embed_go_history_index;
	iface->do_command             = kz_gecko_embed_do_command;
	iface->can_do_command         = kz_gecko_embed_can_do_command;
	iface->get_lock               = kz_gecko_embed_get_lock;
	iface->set_lock               = kz_gecko_embed_set_lock;
	iface->get_body_text          = kz_gecko_embed_get_body_text;
#if 0
	iface->get_selection_source   = kz_gecko_embed_get_selection_source;
#endif
	iface->set_encoding           = kz_gecko_embed_set_encoding;
	iface->get_encoding           = kz_gecko_embed_get_encoding;
	iface->print                  = kz_gecko_embed_print;
	iface->print_preview          = kz_gecko_embed_print_preview;
	iface->get_printer_list       = kz_gecko_embed_get_printer_list;
	iface->create_thumbnail       = kz_gecko_embed_create_thumbnail;
	iface->save_with_content      = kz_gecko_embed_save_with_content;
	iface->set_text_into_textarea = kz_gecko_embed_set_text_into_textarea;
	iface->get_text_from_textarea = kz_gecko_embed_get_text_from_textarea;
	iface->zoom_set               = kz_gecko_embed_zoom_set;
	iface->zoom_get               = kz_gecko_embed_zoom_get;
	iface->set_text_size          = kz_gecko_embed_set_text_size;
	iface->get_text_size          = kz_gecko_embed_get_text_size;
	iface->get_html_with_contents = kz_gecko_embed_get_html_with_contents;
	iface->set_history            = kz_gecko_embed_set_history;
	iface->get_history            = kz_gecko_embed_get_history;
	iface->get_last_modified      = kz_gecko_embed_get_last_modified;
	iface->fine_scroll            = kz_gecko_embed_fine_scroll;
	iface->page_up                = kz_gecko_embed_page_up;
	iface->page_down              = kz_gecko_embed_page_down;
	iface->get_allow_javascript   = kz_gecko_embed_get_allow_javascript;
	iface->set_allow_javascript   = kz_gecko_embed_set_allow_javascript;
	iface->get_allow_images       = kz_gecko_embed_get_allow_images;
	iface->set_allow_images       = kz_gecko_embed_set_allow_images;
	iface->show_page_certificate  = kz_gecko_embed_show_page_certificate;


#if 0
	iface->set_edit_mode          = set_edit_mode;
	iface->set_view_mode          = set_view_mode;
#endif
	iface->link_message        = NULL;
	iface->js_status           = NULL;
	iface->location            = NULL;
	iface->title               = NULL;
	iface->progress            = NULL;
	iface->net_start           = NULL;
	iface->net_stop            = NULL;
	iface->new_window          = NULL;
	iface->open_uri            = NULL;
	iface->size_to             = NULL;
	iface->dom_key_down        = NULL;
	iface->dom_key_press       = NULL;
	iface->dom_key_up          = NULL;
	iface->dom_mouse_down      = NULL;
	iface->dom_mouse_up        = NULL;
	iface->dom_mouse_click     = NULL;
	iface->dom_mouse_dbl_click = NULL;
	iface->dom_mouse_over      = NULL;
	iface->dom_mouse_out       = NULL;
	iface->security_change     = NULL;
	iface->status_change       = NULL;
}

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

	kz_gecko_embed_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;

	// GtkObject signals
	object_class->destroy = kz_gecko_embed_destroy;
 
	// widget class
	widget_class->realize         = kz_gecko_embed_realize;
	widget_class->unrealize       = kz_gecko_embed_unrealize;
	widget_class->size_allocate   = kz_gecko_embed_size_allocate;
 
	// GtkMozEmbedSignals
	moz_embed_class->net_state_all   = kz_gecko_embed_net_state_all;
#if 0
	moz_embed_class->security_change     = kz_gecko_embed_security_change;
	moz_embed_class->dom_mouse_over      = kz_gecko_embed_dom_mouse_over;
	moz_embed_class->dom_mouse_out       = kz_gecko_embed_dom_mouse_out;
	moz_embed_class->net_state           = kz_gecko_embed_net_state;
	moz_embed_class->progress            = kz_gecko_embed_progress;
	moz_embed_class->progress_all        = kz_gecko_embed_progress_all;
#endif
	moz_embed_class->destroy_brsr        = kz_gecko_embed_destroy_brsr;

	moz_embed_class->link_message        = kz_gecko_embed_link_message;
	moz_embed_class->js_status           = kz_gecko_embed_js_status;
	moz_embed_class->title               = kz_gecko_embed_title;
	moz_embed_class->location            = kz_gecko_embed_location;
	moz_embed_class->net_start           = kz_gecko_embed_net_start;
	moz_embed_class->net_stop            = kz_gecko_embed_net_stop;
	moz_embed_class->new_window          = kz_gecko_embed_new_window;
	moz_embed_class->open_uri            = kz_gecko_embed_open_uri;
	moz_embed_class->size_to             = kz_gecko_embed_size_to;
	moz_embed_class->visibility          = kz_gecko_embed_visibility;
	moz_embed_class->dom_key_press       = kz_gecko_embed_dom_key_press;
	moz_embed_class->dom_key_down        = kz_gecko_embed_dom_key_down;
	moz_embed_class->dom_key_up          = kz_gecko_embed_dom_key_up;
	moz_embed_class->dom_mouse_down      = kz_gecko_embed_dom_mouse_down;
	moz_embed_class->dom_mouse_up        = kz_gecko_embed_dom_mouse_up;
	moz_embed_class->dom_mouse_click     = kz_gecko_embed_dom_mouse_click;
	moz_embed_class->dom_mouse_dbl_click = kz_gecko_embed_dom_mouse_dbl_click;
	moz_embed_class->dom_mouse_over      = kz_gecko_embed_dom_mouse_over;

	g_type_class_add_private (gobject_class, sizeof(KzGeckoEmbedPrivate));
}

static void
kz_gecko_embed_init (KzGeckoEmbed *kzembed)
{
	gint i;
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	priv->location              = NULL;
	priv->title                 = NULL;
	priv->load_started          = 0;
	priv->load_percent          = 0;
	priv->bytes_loaded          = 0;
	priv->max_bytes_loaded      = 0;
	priv->is_loading            = FALSE;
	priv->load_status_message   = NULL;
	priv->wrapper               = NULL;
	priv->size_inited           = FALSE;
	priv->total_requests        = 0;
	priv->cur_requests          = 0;
	
	for (i = 0; i < KZ_EMBED_LINK_GUARD; i++)
	{
		priv->nav_links[i] = NULL;
	}
#ifdef USE_MIGEMO
	priv->migemo_keyword = NULL;
#endif
	priv->last_highlight = NULL;
	priv->first_url      = NULL;

#ifndef KZ_REGRESSION_BUG4474
#ifndef HAVE_GECKO_1_8
	kz_moz_embed_load_url(KZ_EMBED(kzembed), "about:blank");
#endif
#endif
}

GtkWidget *
kz_gecko_embed_new (const gchar *url)
{
	KzGeckoEmbed *kzembed = KZ_GECKO_EMBED(g_object_new(KZ_TYPE_GECKO_EMBED, NULL));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
#ifndef KZ_REGRESSION_BUG4474
	// To use the gesture in empty tab, 
	// After "about:balnk" was loaded, the first url start to load.  
	if (url)
		priv->first_url = g_strdup(url);
#ifdef HAVE_GECKO_1_8
	kz_gecko_embed_load_url(KZ_EMBED(kzembed), url);
#endif
#else
	kz_gecko_embed_load_url(KZ_EMBED(kzembed), url);
#endif
	return GTK_WIDGET(kzembed);
}

static void
kz_gecko_embed_destroy (GtkObject *object)
{
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE(object);

	if (priv->location)
		g_free(priv->location);
	priv->location = NULL;
	
	if (priv->title)
		g_free(priv->title);
	priv->title = NULL;

	if (priv->first_url)
		g_free(priv->first_url);
	priv->first_url = NULL;

	if (GTK_OBJECT_CLASS(kz_gecko_embed_parent_class)->destroy)
		GTK_OBJECT_CLASS(kz_gecko_embed_parent_class)->destroy(object);
}


static void
kz_gecko_embed_realize (GtkWidget *widget)
{
	gboolean java = TRUE;
	if (GTK_WIDGET_CLASS(kz_gecko_embed_parent_class)->realize)
		GTK_WIDGET_CLASS(kz_gecko_embed_parent_class)->realize(widget);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE(widget);

	if (!priv->wrapper)
	{
		priv->wrapper = new KzMozWrapper();
		nsresult rv = priv->wrapper->Init(KZ_GECKO_EMBED(widget));
		if (NS_FAILED(rv))
		{
			g_error("KzGeckoEmbed: Faild to init KzMozWrapper!");
		}
		else
		{
		}
	}
	KZ_CONF_GET("Global", "use_javascript", java, BOOL);
	kz_gecko_embed_set_allow_javascript(KZ_EMBED(widget), java);
}


static void
kz_gecko_embed_unrealize (GtkWidget *widget)
{
	KzGeckoEmbed *kzembed = KZ_GECKO_EMBED(widget);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	priv->size_inited = FALSE;

	kz_gecko_embed_navigation_link_free(kzembed);
	
	if (GTK_WIDGET_CLASS(kz_gecko_embed_parent_class)->unrealize)
		GTK_WIDGET_CLASS(kz_gecko_embed_parent_class)->unrealize(widget);

	if (priv->wrapper)
	{
		priv->wrapper->Destroy();
		delete priv->wrapper;
		priv->wrapper = NULL;
	}
	
}


static void
kz_gecko_embed_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
{
	g_return_if_fail(GTK_IS_WIDGET(widget));

	if (!GTK_WIDGET_REALIZED(widget)) return;

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

	priv->size_inited = TRUE;
}


static void
kz_gecko_embed_load_url (KzEmbed *kzembed, const gchar *url)
{
	gchar *start_page = NULL;

	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbed *mozembed = KZ_GECKO_EMBED(kzembed);
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

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

	if (!priv->first_url &&
	    kz_gecko_embed_get_lock(kzembed))
	{
		GtkMozEmbed *newembed = NULL;
		g_signal_emit_by_name(kzembed, 
				      "new-window",
				      &newembed, 0);
		gtk_moz_embed_load_url(newembed, start_page);
		return;
	}
	else
	{
		gtk_moz_embed_load_url(GTK_MOZ_EMBED(kzembed), start_page);
	}
	if (priv->location)
		g_free(priv->location);
	priv->location = start_page;
}


static void
kz_gecko_embed_view_source (KzEmbed *kzembed, const gchar *url)
{
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	KzMozWrapper *wrapper = 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);
	if (NS_FAILED(rv)) return;

	// set history explicitly
	nsCOMPtr<nsISHistory> sHistory;
	rv = wrapper->GetSHistory(getter_AddRefs(sHistory));
	if (NS_FAILED(rv) || !sHistory) return;

	nsCOMPtr<nsISHistoryInternal> sHistoryInternal;
	sHistoryInternal = do_QueryInterface(sHistory);

	gchar *uri = g_strdup_printf("view-source:%s", url);

	nsCOMPtr<nsISHEntry> entry;
	entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);

	nsCOMPtr<nsIURI> aURI;
	NewURI(getter_AddRefs(aURI), uri);
	/* FIXME! set correct contentType */
	nsEmbedCString contentType;
	entry->SetURI(aURI);
	sHistoryInternal->AddEntry(entry, PR_TRUE);
	g_free(uri);
}


static gboolean
kz_gecko_embed_is_loading (KzEmbed *kzembed)
{
	return KZ_IS_GECKO_EMBED(kzembed)
		? KZ_GECKO_EMBED_GET_PRIVATE(kzembed)->is_loading
		: FALSE;
}


static const gchar *
kz_gecko_embed_get_title (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), NULL);
	return KZ_GECKO_EMBED_GET_PRIVATE(kzembed)->title;
}

static gchar *
kz_gecko_embed_ensure_title (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), NULL);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

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

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

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

static gchar * 
kz_gecko_embed_get_link_message (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_GECKO_EMBED(kzembed), NULL);

	gchar *message;

	message = gtk_moz_embed_get_link_message(GTK_MOZ_EMBED(kzembed));

	return message;
}


static gdouble
kz_gecko_embed_get_progress (KzEmbed *kzembed)
{
	gdouble progress;

	g_return_val_if_fail(KZ_GECKO_EMBED(kzembed), 0.0);

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

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

	if (progress > 1.0)
		return 1.0;

	return progress;
}


static const gchar *
kz_gecko_embed_get_location (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), NULL);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (priv->location != NULL &&
	    !strncmp(priv->location, "about:blank", 11))
	{
		return "";
	}

	return priv->location;
}

static void
kz_gecko_embed_destroy_brsr (GtkMozEmbed *embed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(embed));

	gtk_widget_destroy(GTK_WIDGET(embed));
}


static void
kz_gecko_embed_link_message (GtkMozEmbed *embed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(embed));

	g_signal_emit_by_name(embed, "kz-link-message");

	if (((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->link_message)
		((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->link_message(embed);
}

static void
kz_gecko_embed_js_status (GtkMozEmbed *embed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(embed));

	g_signal_emit_by_name(embed, "kz-js-status");

	if (((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->js_status)
		((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->js_status(embed);
}

static void
kz_gecko_embed_title (GtkMozEmbed *embed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(embed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE(embed);

	g_free(priv->title);

	priv->title = gtk_moz_embed_get_title(embed);

	g_signal_emit_by_name(embed, "kz-title");

	if (((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->title)
		((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->title(embed);
}

static void
kz_gecko_embed_location (GtkMozEmbed *embed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(embed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE(embed);

#ifndef KZ_REGRESSION_BUG4474
	if (priv->first_url)
	{
		gchar *tmp = g_strdup(priv->first_url);
		g_free(priv->first_url);
		priv->first_url = NULL;

		kz_gecko_embed_load_url(KZ_EMBED(embed), tmp);
		g_free(tmp);

		return;
	}
#endif
	if (priv->location)
		g_free(priv->location);
	priv->location = gtk_moz_embed_get_location(embed);

	g_signal_emit_by_name(embed, "kz-location");

	if (((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->location)
		((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->location(embed);
}


static void
kz_gecko_embed_net_start (GtkMozEmbed *embed)
{
	KzGeckoEmbed *kzembed = KZ_GECKO_EMBED(embed);

	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (priv->first_url)
		return;

	priv->is_loading = TRUE;

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

	g_signal_emit_by_name(embed, "kz-net-start");

	if (((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->net_start)
		((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->net_start(embed);
}

static void
net_stop_proccess (KzGeckoEmbed *kzembed)
{
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (!priv->wrapper) return;
	
	gboolean create_thumbnail = FALSE, store_cache = TRUE;
	KZ_CONF_GET("Global", "create_thumbnail", create_thumbnail, BOOL);
	KZ_CONF_GET("History", "store_cache", store_cache, BOOL);

	gchar *cache = g_strconcat("file://", g_get_home_dir(),
			    	   HISTORY_DIR, NULL);
	const gchar *location = kz_gecko_embed_get_location(KZ_EMBED(kzembed));
	if (location && 
	    (g_str_has_prefix(location, "http:") || 
	     g_str_has_prefix(location, "https:") || 
	     g_str_has_prefix(location, "history-search:") || 
	     g_str_has_prefix(location, "file:")) &&
	    !g_str_has_prefix(location, cache))
	{
		//get the last modification time
		nsCOMPtr<nsIDOMDocument> domDoc;
        	priv->wrapper->GetMainDomDocument(getter_AddRefs(domDoc));
		nsCOMPtr<nsIDOMNSDocument> doc = do_QueryInterface(domDoc);
		nsEmbedString value;
		doc->GetLastModified(value);

		nsEmbedCString cValue;
		NS_UTF16ToCString(value,
				  NS_CSTRING_ENCODING_UTF8, cValue);
		nsTime lm (cValue.get(), PR_TRUE);
		GTime last_modified;
		LL_DIV(last_modified,
		       NS_STATIC_CAST(PRTime, lm), PR_USEC_PER_SEC);

		nsCOMPtr<nsIURI> inURI;
		nsEmbedCString sURI;
		nsresult rv;

		rv = priv->wrapper->GetDocumentUrl(sURI);
		const gchar *uri = sURI.get();
      		rv = NewURI(getter_AddRefs(inURI), uri);

		if (create_thumbnail && 
		    (!last_modified || (thumbnail_get_last_modified(uri) < last_modified)))
		{
			kz_gecko_embed_create_thumbnail(KZ_EMBED(kzembed));
		}
		
		if ((!last_modified || (history_get_last_modified(uri) < last_modified)) &&
		    !g_str_has_prefix(location, "history-search:"))
		{
			if (store_cache)
			{
				gchar *filename;
				filename = kz_gecko_embed_store_history_file(kzembed);
				if (filename)
					g_free(filename);
			}

			if (KZ_GET_SEARCH)
			{
				const gchar *title = kz_gecko_embed_get_title(KZ_EMBED(kzembed));
	
				nsCOMPtr<nsIDOMNode> node = do_QueryInterface(domDoc);
				nsCOMPtr<nsIDOMSerializer> serializer;
				serializer = do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
				if (serializer)
				{
					serializer->SerializeToString(node, value);
					NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cValue);
				}
				kz_search_register_document(KZ_GET_SEARCH, uri, title, cValue.get(), last_modified);
#ifdef WITH_ANTHY_TRAINER
				KzSearch *search = kz_search_new("anthy-trainer");
				if (search)
				{
					kz_search_register_document(search, uri, title, cValue.get(), last_modified);
					g_object_unref(search);
				}
#endif
			}
		}
	}
	
	g_free(cache);
}

static void
kz_gecko_embed_net_stop (GtkMozEmbed *embed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(embed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (embed);
	priv->is_loading = FALSE;

	if (((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->net_stop)
		((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->net_stop(embed);

	net_stop_proccess(KZ_GECKO_EMBED(embed));

	g_signal_emit_by_name(embed, "kz-net-stop");
}

static void
kz_gecko_embed_net_state_all (GtkMozEmbed *embed, const char *aURI,
			    gint state, guint status)
{
	KzGeckoEmbed *kzembed = KZ_GECKO_EMBED(embed);

	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (state & GTK_MOZ_EMBED_FLAG_IS_NETWORK)
	{
		priv->total_requests = 0;
		priv->cur_requests = 0;
#if 0
                if (state & GTK_MOZ_EMBED_FLAG_START)
                {
		}
                else if (state & EMBED_STATE_STOP)
                {
                        priv->total_requests = 0;
                        priv->cur_requests = 0;
		} 
#endif
	}
        if (state & GTK_MOZ_EMBED_FLAG_IS_REQUEST)
        {
                if (state & GTK_MOZ_EMBED_FLAG_START)
                        priv->total_requests ++;
                else if (state & GTK_MOZ_EMBED_FLAG_STOP)
                        priv->cur_requests ++;
        }

	g_signal_emit_by_name(embed, "kz-progress");

	if (((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->net_state_all)
		((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->net_state_all(embed, aURI, state, status);
}

static void 
cb_embed_destroy_browser (GtkMozEmbed *embed, GtkWidget *transient_window)
{
	gtk_widget_destroy(GTK_WIDGET(transient_window));
}

static void
kz_gecko_embed_new_window (GtkMozEmbed *embed, GtkMozEmbed **newEmbed,
			 guint chromemask)
{
	if ((chromemask & GTK_MOZ_EMBED_FLAG_OPENASCHROME) != 0)
	{
		/* FIXME! this is ad hoc. */
		GtkWidget *newWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);

		gtk_window_set_transient_for(GTK_WINDOW(newWindow),
					     GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(embed))));
		*newEmbed = GTK_MOZ_EMBED(kz_gecko_embed_new(NULL));
		g_signal_connect(*newEmbed,"destroy",
				 G_CALLBACK(cb_embed_destroy_browser),
				 newWindow);
		gtk_container_add(GTK_CONTAINER(newWindow),
				  GTK_WIDGET(*newEmbed));
	}
	else
	{
		g_signal_emit_by_name(embed, "kz-new-window", newEmbed);
	}
}

static gint
kz_gecko_embed_open_uri (GtkMozEmbed *embed, const char *uri)
{
	gint ret = FALSE;

	g_signal_emit_by_name(embed, "kz-open-uri", uri, &ret);

	if (((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->open_uri)
		ret = ((GtkMozEmbedClass*)kz_gecko_embed_parent_class)->open_uri(embed, uri);

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

	return ret;
}

static void
kz_gecko_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));

	g_signal_emit_by_name(embed, "kz-size-to", width, height);
}

static gint
kz_gecko_embed_dom_key_down (GtkMozEmbed *embed, gpointer event)
{
	KzEmbedEventKey *kzevent;
	gint ret = FALSE;

	kz_gecko_embed_get_key_event_info(KZ_GECKO_EMBED(embed), event, &kzevent);
	g_signal_emit_by_name(embed, "kz-dom-key-down",
			      kzevent, &ret);
	kz_embed_event_free((KzEmbedEvent *) kzevent);

	return ret;
}

static gint
kz_gecko_embed_dom_key_up (GtkMozEmbed *embed, gpointer event)
{
	KzEmbedEventKey *kzevent;
	gint ret = FALSE;

	kz_gecko_embed_get_key_event_info(KZ_GECKO_EMBED(embed), event, &kzevent);
	g_signal_emit_by_name(embed, "kz-dom-key-up",
			      kzevent, &ret);
	kz_embed_event_free((KzEmbedEvent *) kzevent);

	return ret;
}

static gint
kz_gecko_embed_dom_key_press (GtkMozEmbed *embed, gpointer event)
{
	KzEmbedEventKey *kzevent;
	gint ret = FALSE;

	kz_gecko_embed_get_key_event_info(KZ_GECKO_EMBED(embed), event, &kzevent);
	g_signal_emit_by_name(embed, "kz-dom-key-press",
			      kzevent, &ret);
	kz_embed_event_free((KzEmbedEvent *) kzevent);

	return ret;
}

static gint
kz_gecko_embed_dom_mouse_down (GtkMozEmbed *embed, gpointer event)
{
	KzEmbedEventMouse *kzevent;
	gint ret = FALSE;

	kz_gecko_embed_get_mouse_event_info(KZ_GECKO_EMBED(embed), event, &kzevent);

	g_signal_emit_by_name(embed, "kz-dom-mouse-down",
			      kzevent, &ret);
	kz_embed_event_free((KzEmbedEvent *) kzevent);

	return ret;
}

static gint
kz_gecko_embed_dom_mouse_up (GtkMozEmbed *embed, gpointer event)
{
	KzEmbedEventMouse *kzevent;
	gint ret = FALSE;

	kz_gecko_embed_get_mouse_event_info(KZ_GECKO_EMBED(embed), event, &kzevent);
	g_signal_emit_by_name(embed, "kz-dom-mouse-up",
			      kzevent, &ret);
	kz_embed_event_free((KzEmbedEvent *) kzevent);

	return ret;
}

static gint
kz_gecko_embed_dom_mouse_click (GtkMozEmbed *embed, gpointer event)
{
	KzEmbedEventMouse *kzevent;
	gint ret = FALSE;

	kz_gecko_embed_get_mouse_event_info(KZ_GECKO_EMBED(embed), event, &kzevent);
	g_signal_emit_by_name(embed, "kz-dom-mouse-click",
			      kzevent, &ret);
	kz_embed_event_free((KzEmbedEvent *) kzevent);

	return ret;
}

static gint
kz_gecko_embed_dom_mouse_dbl_click (GtkMozEmbed *embed, gpointer event)
{
	KzEmbedEventMouse *kzevent;
	gint ret = FALSE;

	kz_gecko_embed_get_mouse_event_info(KZ_GECKO_EMBED(embed), event, &kzevent);
	g_signal_emit_by_name(embed, "kz-dom-mouse-dbl-click",
			      kzevent, &ret);
	kz_embed_event_free((KzEmbedEvent *) kzevent);

	return ret;
}

static
gint kz_gecko_embed_dom_mouse_over (GtkMozEmbed *embed, gpointer event)
{
	KzEmbedEventMouse *kzevent;
	gint ret = FALSE;

	kz_gecko_embed_get_mouse_event_info(KZ_GECKO_EMBED(embed), event, &kzevent);
	g_signal_emit_by_name(embed, "kz-dom-mouse-over",
			      kzevent, &ret);
	kz_embed_event_free((KzEmbedEvent *) kzevent);

	return ret;
}

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

	parent = gtk_widget_get_parent(GTK_WIDGET(embed));
	g_return_if_fail(parent != NULL);
	
	g_object_set(embed, "visible", visibility, NULL);
	g_object_set(parent, "visible", visibility, NULL);
}

#if 0
static void
kz_gecko_embed_security_change (GtkMozEmbed *embed,
			      gpointer request,
			      guint state)
{
	if (kz_gecko_embed_parent_class->security_change)
		kz_gecko_embed_parent_class->security_change(embed, request, state);
}
#endif

static gboolean
kz_gecko_embed_can_cut_selection (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (!priv->wrapper) return TRUE;

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

	if (NS_FAILED(rv)) return FALSE;

	return retval;
}

static gboolean
kz_gecko_embed_can_copy_selection (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (!priv->wrapper) return TRUE;

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

	if (NS_FAILED(rv)) return FALSE;

	return retval;
}

static gboolean
kz_gecko_embed_can_paste (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (!priv->wrapper) return TRUE;

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

	if (NS_FAILED(rv)) return FALSE;

	return retval;
}

static void
kz_gecko_embed_cut_selection (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	g_return_if_fail(priv->wrapper);

	priv->wrapper->CutSelection();
}

static void
kz_gecko_embed_copy_selection (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	g_return_if_fail(priv->wrapper);

	priv->wrapper->CopySelection();
}

static void
kz_gecko_embed_paste (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	g_return_if_fail(priv->wrapper);

	priv->wrapper->Paste();
}

static void
kz_gecko_embed_select_all (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	g_return_if_fail(priv->wrapper);

	priv->wrapper->SelectAll();
}

static gchar *
kz_gecko_embed_get_selection_string(KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), NULL);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (!priv->wrapper) return NULL;

	nsresult rv;

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

	PRUnichar *string;
	rv = selection->ToString(&string);
	
	nsEmbedCString str;
	NS_UTF16ToCString(nsEmbedString(string),
			  NS_CSTRING_ENCODING_UTF8, str);

	return g_strdup (str.get());
}

static gchar *
kz_gecko_embed_get_html_with_contents (KzEmbed *kzembed, const gchar *storedir)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), NULL);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (!priv->wrapper) return NULL;

	nsresult rv;

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

	nsEmbedString string;
	rv = priv->wrapper->GetHtmlWithContents(selection, 
						storedir,
						string);
	if (NS_FAILED(rv)) return NULL;

	nsEmbedCString str;
	NS_UTF16ToCString(nsEmbedString(string),
			  NS_CSTRING_ENCODING_UTF8, str);
	return g_strdup(str.get());
}
#if 0
static gchar *
kz_gecko_embed_get_selection_source(KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), NULL);

	KzGeckoEmbed *mozembed = KZ_GECKO_EMBED(kzembed);
	if (!priv->wrapper) return NULL;

	nsresult rv;

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

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

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

#if USE_MIGEMO
static gboolean
kz_gecko_embed_get_body_string(KzGeckoEmbed *mozembed, gchar **body_string,
			     gboolean backward, gboolean whole)
{
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (mozembed);
	nsEmbedString text;
	nsresult rv;

	if (whole)
		rv = priv->wrapper->GetBodyString(text);
	else
		rv = priv->wrapper->GetStringSelection(text, backward);
	if (NS_FAILED(rv))
		return FALSE;
	
	nsEmbedCString str;
	NS_UTF16ToCString(text,
			  NS_CSTRING_ENCODING_UTF8, str);
	*body_string = g_strdup(str.get());
	return TRUE;
}

static void
set_migemo_keyword(KzGeckoEmbed *mozembed, const char *keyword,
		   gchar *body_string, gboolean backward)
{
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (mozembed);
	g_free(priv->migemo_keyword);
	priv->migemo_keyword =
		kz_migemo_get_matched_text(KZ_GET_MIGEMO, body_string,
					   keyword, backward);
}
#endif

static gboolean
kz_gecko_embed_unhighlight_word (KzEmbed *kzembed, const char *word)
{
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (!priv->wrapper) return FALSE;

	KzMozWrapper *wrapper = priv->wrapper;

	nsresult rv;
	nsCOMPtr<nsIFind> finder;
	finder = do_CreateInstance("@mozilla.org/embedcomp/rangefind;1", &rv);

	if (NS_FAILED(rv)) return FALSE;

	nsEmbedString string;
	NS_CStringToUTF16(nsEmbedCString(word),
			  NS_CSTRING_ENCODING_UTF8, string);
	const PRUnichar *u_word;
	NS_StringGetData(string, &u_word);
	
	nsCOMPtr<nsIDOMDocument> domDoc;
       	rv = wrapper->GetMainDomDocument(getter_AddRefs(domDoc));
        if (NS_FAILED(rv) || !domDoc) return FALSE;
	
	nsCOMPtr<nsIDOMDocumentRange> docRange = do_QueryInterface(domDoc);
	if (!docRange) return FALSE;

	nsCOMPtr<nsIDOMRange> searchRange;
	nsCOMPtr<nsIDOMRange> startRange;
	nsCOMPtr<nsIDOMRange> endRange;
	rv = wrapper->SetHighlightRange(getter_AddRefs(searchRange),
	    				getter_AddRefs(startRange),
					getter_AddRefs(endRange));
	nsCOMPtr<nsIDOMRange> retRange;

	while (finder->Find(u_word, searchRange, startRange, endRange, getter_AddRefs(retRange)) == NS_OK)
	{
		if (!retRange) break;

		nsCOMPtr<nsIDOMNode> startContainer;
		retRange->GetStartContainer(getter_AddRefs(startContainer));

		nsCOMPtr<nsIDOMNode> elm;
		startContainer->GetParentNode(getter_AddRefs(elm));

		char *attr = NULL;
		wrapper->GetAttributeFromNode(elm, "id", &attr);
		if (elm && attr && !g_ascii_strcasecmp(attr, "kazehakase-search"))
		{
			nsCOMPtr<nsIDOMDocumentFragment> flag;
			nsCOMPtr<nsIDOMNode> next;
			nsCOMPtr<nsIDOMNode> parent;

			domDoc->CreateDocumentFragment(getter_AddRefs(flag));
			nsCOMPtr<nsIDOMNode> flagNode;
			flagNode = do_QueryInterface(flag);

			elm->GetNextSibling(getter_AddRefs(next));
			elm->GetParentNode(getter_AddRefs(parent));

			nsCOMPtr<nsIDOMNode> child;
			while (elm->GetFirstChild(getter_AddRefs(child)) == NS_OK)
			{
				if (!child) break;
				nsCOMPtr<nsIDOMNode> newNode;
				flagNode->AppendChild(child, getter_AddRefs(newNode));
			}

			docRange->CreateRange(getter_AddRefs(startRange));
			startRange->SetStartAfter(elm);

			nsCOMPtr<nsIDOMNode> tmp;
			parent->RemoveChild(elm, getter_AddRefs(tmp));
			parent->InsertBefore(flagNode, next, getter_AddRefs(tmp));
		}
		else
		{
			nsCOMPtr<nsIDOMNode> endContainer;
			retRange->GetEndContainer(getter_AddRefs(endContainer));
			PRInt32 endOffset;
			retRange->GetEndOffset(&endOffset);
			docRange->CreateRange(getter_AddRefs(startRange));
			startRange->SetStart(endContainer, endOffset);
		}
		startRange->Collapse(PR_TRUE);
	}

	return TRUE;
}


// this function is drawn upon 
//   toolkit/components/typeaheadfind/content/findBar.js in firefox-1.0+.
static gboolean
kz_gecko_embed_highlight_word (KzEmbed *kzembed, const char *word)
{
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (priv->last_highlight)
	{
		if (!strcmp(priv->last_highlight, word))
			return TRUE;
		kz_gecko_embed_unhighlight_word (kzembed, priv->last_highlight);
		g_free(priv->last_highlight);
	}
	
	priv->last_highlight = g_strdup(word);

	nsresult rv;
	nsCOMPtr<nsIFind> finder;
	finder = do_CreateInstance("@mozilla.org/embedcomp/rangefind;1", &rv);

	if (NS_FAILED(rv)) return FALSE;

	nsEmbedString string;
	NS_CStringToUTF16(nsEmbedCString(word),
			  NS_CSTRING_ENCODING_UTF8, string);
	const PRUnichar *u_word;
	NS_StringGetData(string, &u_word);
	
	nsCOMPtr<nsIDOMDocument> domDoc;
       	rv = priv->wrapper->GetMainDomDocument(getter_AddRefs(domDoc));
        if (NS_FAILED(rv) || !domDoc) return FALSE;


	nsCOMPtr<nsIDOMRange> searchRange;
	nsCOMPtr<nsIDOMRange> startRange;
	nsCOMPtr<nsIDOMRange> endRange;
	rv = priv->wrapper->SetHighlightRange(getter_AddRefs(searchRange),
	    						     getter_AddRefs(startRange),
							     getter_AddRefs(endRange));

	const PRUnichar span[]          = { 's', 'p', 'a', 'n' ,'\0' };
	const PRUnichar style[]         = { 's', 't', 'y', 'l' ,'e', '\0' };
	const PRUnichar u_color[]       = { 's', 'p', 'a', 'n' ,'\0' };
	const PRUnichar id[]            = { 'i', 'd', '\0' };
	const PRUnichar sc[]            = { ';', '\0' };
	const PRUnichar bg_color[]      = { 'b', 'a', 'c', 'k' ,'g', 'r', 
			 		    'o', 'u', 'n', 'd', '-', 'c',
			 		    'o', 'l', 'o', 'r', ':', '\0' };
	const PRUnichar kazehakase_id[] = { 'k', 'a', 'z', 'e' ,'h', 'a', 
					    'k', 'a', 's', 'e', '-', 's',
					    'e', 'a', 'r', 'c', 'h', '\0' };

	nsEmbedCString cColor("#ffff00");
	nsEmbedString uColor;
	NS_CStringToUTF16(cColor, NS_CSTRING_ENCODING_UTF8, uColor);
	nsEmbedString color(bg_color);
	color += uColor;
	color += sc;
	nsCOMPtr<nsIDOMElement> baseElm;
	domDoc->CreateElement(nsEmbedString(span), getter_AddRefs(baseElm));
	baseElm->SetAttribute(nsEmbedString(style), color);
	baseElm->SetAttribute(nsEmbedString(id), nsEmbedString(kazehakase_id));
	nsCOMPtr<nsIDOMNode> baseNode = do_QueryInterface(baseElm);

	nsCOMPtr<nsIDOMRange> retRange;

	while (finder->Find(u_word, searchRange, startRange, endRange, getter_AddRefs(retRange)) == NS_OK)
	{
		if (!retRange) break;

		nsCOMPtr<nsIDOMNode> node;
		baseNode->CloneNode(PR_TRUE, getter_AddRefs(node));

		nsCOMPtr<nsIDOMNode> startContainer;
		retRange->GetStartContainer(getter_AddRefs(startContainer));

		PRInt32 startOffset, endOffset;
		retRange->GetStartOffset(&startOffset);
		retRange->GetEndOffset(&endOffset);

		nsCOMPtr<nsIDOMDocumentFragment> flag;
		retRange->ExtractContents(getter_AddRefs(flag));
		if (!flag) continue;

		nsCOMPtr<nsIDOMNode> flagNode;
		flagNode = do_QueryInterface(flag);

		nsCOMPtr<nsIDOMText> text;
		text = do_QueryInterface(startContainer);

		nsCOMPtr<nsIDOMText> before;
		text->SplitText(startOffset, getter_AddRefs(before));
		nsCOMPtr<nsIDOMNode> beforeNode;
		beforeNode = do_QueryInterface(before);
		
		nsCOMPtr<nsIDOMNode> parent;
		beforeNode->GetParentNode(getter_AddRefs(parent));

		nsCOMPtr<nsIDOMNode> newNode;
		node->AppendChild(flagNode, getter_AddRefs(newNode));
		nsCOMPtr<nsIDOMNode> newParent;
		parent->InsertBefore(node, beforeNode, getter_AddRefs(newParent));

		nsCOMPtr<nsIDOMDocument> doc;
		node->GetOwnerDocument(getter_AddRefs(doc));
		nsCOMPtr<nsIDOMDocumentRange> range;
		range = do_QueryInterface(doc);

		range->CreateRange(getter_AddRefs(startRange));

		nsCOMPtr<nsIDOMNodeList> list;
		node->GetChildNodes(getter_AddRefs(list));

		PRUint32 length;
		list->GetLength(&length);
		startRange->SetStart(node, length);
		startRange->SetEnd(node, length);
	}

	return TRUE;
}

  
static gboolean
kz_gecko_embed_find (KzEmbed *kzembed, const char *keyword,
		   gboolean backward)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);
	g_return_val_if_fail(keyword, FALSE);

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


	nsresult rv;
	nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(web));
#if USE_MIGEMO
	KzGeckoEmbed *mozembed = KZ_GECKO_EMBED(kzembed);
	gboolean use_migemo;
	KZ_CONF_GET("Global", "use_migemo", use_migemo, BOOL);

	nsEmbedString str;
	if (use_migemo)
	{
		gchar *body_string;
		KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

		if (!kz_gecko_embed_get_body_string(mozembed, &body_string,
						  backward, FALSE))
			goto START_SEARCH;
		set_migemo_keyword(mozembed, keyword, body_string, backward);

		if (!priv->migemo_keyword)
		{
			g_free(body_string);
			if (!kz_gecko_embed_get_body_string(mozembed,
							  &body_string,
							  backward,
							  TRUE))
				goto START_SEARCH;
			set_migemo_keyword(mozembed, keyword,
					   body_string, backward);
		}
		
		if (priv->migemo_keyword)
		{
			NS_CStringToUTF16(nsEmbedCString(priv->migemo_keyword),
					  NS_CSTRING_ENCODING_UTF8, str);
		}
		else
		{
			NS_CStringToUTF16(nsEmbedCString(keyword),
					  NS_CSTRING_ENCODING_UTF8, str);
		}
		g_free(body_string);
	}
	else
	{
		NS_CStringToUTF16(nsEmbedCString(keyword),
				  NS_CSTRING_ENCODING_UTF8, str);
	}
	finder->SetSearchString(str.get());
START_SEARCH:
#else	
	nsEmbedString str;
	NS_CStringToUTF16(nsEmbedCString(keyword),
			  NS_CSTRING_ENCODING_UTF8, str);
	finder->SetSearchString(str.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;
}

static gboolean
kz_gecko_embed_incremental_search (KzEmbed *kzembed, const char *keyword,
				 gboolean backward)
{
	nsresult rv;
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), 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(kzembed),
					getter_AddRefs(web));
	if (!web) return FALSE;

	nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(web));
#if USE_MIGEMO
	KzGeckoEmbed *mozembed = KZ_GECKO_EMBED(kzembed);
	gboolean use_migemo;
	KZ_CONF_GET("Global", "use_migemo", use_migemo, BOOL);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	nsCOMPtr<nsISelection> selection;
	rv = priv->wrapper->GetSelection(getter_AddRefs(selection));
	if (!selection) return FALSE;

	selection->RemoveAllRanges();

	nsEmbedString str;
	if (use_migemo)
	{
		gchar *body_string;

		if (!kz_gecko_embed_get_body_string(mozembed, &body_string,
						  backward, TRUE))
			goto START_SEARCH;
		set_migemo_keyword(mozembed, keyword, body_string, backward);

		if (priv->migemo_keyword) 
		{
			NS_CStringToUTF16(nsEmbedCString(priv->migemo_keyword),
					  NS_CSTRING_ENCODING_UTF8, str);
		}
		else 
		{
			NS_CStringToUTF16(nsEmbedCString(keyword),
					  NS_CSTRING_ENCODING_UTF8, str);
		}
		g_free(body_string);
	}
	else
	{
		NS_CStringToUTF16(nsEmbedCString(keyword),
				  NS_CSTRING_ENCODING_UTF8, str);
	}
	finder->SetSearchString(str.get());
START_SEARCH:
#else
	nsEmbedString str;
	NS_CStringToUTF16(nsEmbedCString(keyword),
			  NS_CSTRING_ENCODING_UTF8, str);
	finder->SetSearchString(str.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);

	// highlight search word
	
	gboolean use_highlight = FALSE;
	KZ_CONF_GET("Global", "use_highlight", use_highlight, BOOL);
	if (use_highlight)
	{
		if (NS_SUCCEEDED(rv))
		{
			nsEmbedCString c_str;
			NS_UTF16ToCString(str, NS_CSTRING_ENCODING_UTF8, c_str);
			kz_gecko_embed_highlight_word(kzembed, c_str.get());
		}
	}

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

static gboolean
kz_gecko_embed_selection_is_collapsed (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), TRUE);

	KzGeckoEmbed *mozembed = KZ_GECKO_EMBED(kzembed);
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (!priv->wrapper) return TRUE;

	nsresult rv;

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

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

	return collapsed;
}


static gboolean
kz_gecko_embed_get_links (KzEmbed *kzembed, GList **list,
			gboolean selected_only)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);
	KzGeckoEmbed *mozembed = KZ_GECKO_EMBED(kzembed);
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	g_return_val_if_fail(priv->wrapper, FALSE);
	g_return_val_if_fail(list, FALSE);

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

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

        // get main DOMWindow
	nsCOMPtr<nsIDOMWindow> mainDOMWindow;
	rv = priv->wrapper->GetDOMWindow(getter_AddRefs(mainDOMWindow));
	if (NS_FAILED(rv)) return FALSE;

	rv = priv->wrapper->GetLinksFromWindow(mainDOMWindow,
							 list,
							 selection,
							 selected_only);

	return NS_FAILED(rv) ? FALSE : TRUE;
}


static gboolean
kz_gecko_embed_get_dest_anchors (KzEmbed *kzembed, GList **list)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);
	KzGeckoEmbed *mozembed = KZ_GECKO_EMBED(kzembed);
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	g_return_val_if_fail(priv->wrapper, FALSE);
	g_return_val_if_fail(list, FALSE);

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

        // get main DOMWindow
	nsCOMPtr<nsIDOMWindow> mainDOMWindow;
	rv = priv->wrapper->GetDOMWindow(getter_AddRefs(mainDOMWindow));
	if (NS_FAILED(rv)) return FALSE;

	rv = priv->wrapper->GetDestAnchorsFromWindow(mainDOMWindow,
						     list);

	return NS_FAILED(rv) ? FALSE : TRUE;
}


glong
kz_gecko_embed_get_key_event_info(KzGeckoEmbed *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);
	info->key = code;

	aKeyEvent->GetCharCode(&code);
	info->char_code = 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;

     	nsEmbedString nodename;
     	OriginalNode->GetNodeName(nodename);

	nsEmbedCString cNodename;
	NS_UTF16ToCString(nodename, NS_CSTRING_ENCODING_UTF8, cNodename);

     	if (!g_ascii_strcasecmp(cNodename.get(), "xul:thumb") ||
	    !g_ascii_strcasecmp(cNodename.get(), "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 kz_gecko_embed_set_event_context(kzembed, target, (KzEmbedEvent *) info);
}


glong
kz_gecko_embed_get_mouse_event_info(KzGeckoEmbed *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;

     	nsEmbedString nodename;
     	OriginalNode->GetNodeName(nodename);

	nsEmbedCString cNodename;
	NS_UTF16ToCString(nodename, NS_CSTRING_ENCODING_UTF8, cNodename);

     	if (!g_ascii_strcasecmp(cNodename.get(), "xul:thumb") ||
	    !g_ascii_strcasecmp(cNodename.get(), "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 kz_gecko_embed_set_event_context(kzembed, target, (KzEmbedEvent *) info);
}


static glong
kz_gecko_embed_set_event_context (KzGeckoEmbed *kzembed,
			          nsIDOMEventTarget *target,
			          KzEmbedEvent *info)
{
	nsresult result;
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	KzMozWrapper *wrapper = priv->wrapper;

	g_return_val_if_fail(priv->wrapper, KZ_CONTEXT_NONE);

	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<nsIDOM3Node> domnode = do_QueryInterface(domDoc);
     	if(!domnode) return KZ_CONTEXT_NONE;

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

	nsEmbedString spec;
	domnode->GetBaseURI(spec);

	nsEmbedCString cSpec;
	NS_UTF16ToCString(spec,
			  NS_CSTRING_ENCODING_UTF8, cSpec);

	nsCOMPtr<nsIURI> baseURI;
	NewURI(getter_AddRefs(baseURI), cSpec.get());
	if (!baseURI) return KZ_CONTEXT_NONE;

     	nsEmbedString mime;
     	nsDoc->GetContentType(mime);

	nsEmbedCString cMime;
	NS_UTF16ToCString(mime, NS_CSTRING_ENCODING_UTF8, cMime);
     	if (!g_ascii_strcasecmp(cMime.get(), "text/xul"))  return KZ_CONTEXT_NONE;

	PRUint32 flags = KZ_CONTEXT_NONE;

	// check framed page
	nsCOMPtr<nsIDOMDocument> mainDocument;
	result = priv->wrapper->GetMainDomDocument (getter_AddRefs(mainDocument));
	if (domDoc != mainDocument)
	{
		flags |= KZ_CONTEXT_FRAME;
		nsEmbedCString 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 = 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)
		{
			nsEmbedString uTag;
			element->GetLocalName(uTag);

			nsEmbedCString utf8_tag;
			NS_UTF16ToCString(uTag, NS_CSTRING_ENCODING_UTF8, utf8_tag);

			if (!g_ascii_strcasecmp(utf8_tag.get(), "input"))
			{
				flags |= KZ_CONTEXT_INPUT;
			}
			else if (!g_ascii_strcasecmp(utf8_tag.get(), "textarea"))
			{
				flags |= KZ_CONTEXT_INPUT | KZ_CONTEXT_TEXTAREA;
				info->element = (void*)element;
			}
			else if (!g_ascii_strcasecmp(utf8_tag.get(), "img"))
			{
				flags |= KZ_CONTEXT_IMAGE;

				char *src = NULL;
				wrapper->GetAttributeFromNode(node, "src", &src);
				if (!src)  return KZ_CONTEXT_NONE;

			     	nsEmbedCString srca;
			     	srca = nsEmbedCString(src);

			     	nsEmbedCString srcc,imgc;
				
			     	srcc = nsEmbedCString(src);

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

			     	info->img = g_strdup(imgc.get());

			     	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;
				nsEmbedString href;

				NS_CStringToUTF16(nsEmbedCString("href"),
						  NS_CSTRING_ENCODING_UTF8,
						  href);
				attributes->GetNamedItem(href, getter_AddRefs(hrefNode));
				if (hrefNode)
				{
					gchar *link;
					flags |= KZ_CONTEXT_LINK;

					wrapper->GetLinkAndTitleFromNode(domDoc,
								 node,
								 &link,
								 &info->linktext);
					if (!link)
					{
						g_free(info->linktext);
						return KZ_CONTEXT_NONE;
					}
					if (!strncasecmp(link, "mailto:", 7))
						info->link = g_strdup(link+7); 
					else 
						info->link = g_strdup(link);
					g_free(link);
					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 gchar *
kz_gecko_embed_get_up_location(KzGeckoEmbed *kzembed)
{
	const gchar *location;
	gchar *up_location = NULL;
	gchar *pos, *dummy;
	int len; 

	location = kz_gecko_embed_get_location(KZ_EMBED(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;
}

static void
kz_gecko_embed_reload (KzEmbed *kzembed, KzEmbedReloadFlag flags)
{
	gint32 moz_flags;
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	switch (flags) {
	  case KZ_EMBED_RELOAD_NORMAL:
	    moz_flags = GTK_MOZ_EMBED_FLAG_RELOADNORMAL;
	    break;
	  case KZ_EMBED_RELOAD_BYPASS_CACHE:
	    moz_flags = GTK_MOZ_EMBED_FLAG_RELOADBYPASSCACHE;
	    break;
	  case KZ_EMBED_RELOAD_BYPASS_PROXY:
	    moz_flags = GTK_MOZ_EMBED_FLAG_RELOADBYPASSPROXY;
	    break;
	  case KZ_EMBED_RELOAD_BYPASS_PROXY_AND_CACHE:
	    moz_flags = GTK_MOZ_EMBED_FLAG_RELOADBYPASSPROXYANDCACHE;
	    break;
	  case KZ_EMBED_RELOAD_CHARSET_CHANGE:
	    moz_flags = GTK_MOZ_EMBED_FLAG_RELOADCHARSETCHANGE;
	    break;
	}

	gtk_moz_embed_reload(GTK_MOZ_EMBED(kzembed), moz_flags);
}


static void
kz_gecko_embed_stop_load (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	gtk_moz_embed_stop_load(GTK_MOZ_EMBED(kzembed));
}


static void
kz_gecko_embed_go_back (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	gtk_moz_embed_go_back(GTK_MOZ_EMBED(kzembed));
}


static void
kz_gecko_embed_go_up (KzEmbed *kzembed)
{
	gchar *location;
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	location = kz_gecko_embed_get_up_location(KZ_GECKO_EMBED(kzembed));
	kz_gecko_embed_load_url(kzembed, location);
	g_free(location);
}

static void
kz_gecko_embed_go_forward (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	gtk_moz_embed_go_forward(GTK_MOZ_EMBED(kzembed));
}


static gboolean
kz_gecko_embed_can_go_back (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);
	return gtk_moz_embed_can_go_back(GTK_MOZ_EMBED(kzembed));
}

static gboolean
kz_gecko_embed_can_go_forward (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);
	return gtk_moz_embed_can_go_forward(GTK_MOZ_EMBED(kzembed));
}

static gboolean
kz_gecko_embed_can_go_nav_link (KzEmbed *kzembed,
			      KzEmbedNavLink link)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	KzNavi *navi = KZ_NAVI(g_list_nth_data(priv->nav_links[link], 0));
	return (navi && navi->uri) ? TRUE : FALSE;
}

static void
kz_gecko_embed_go_nav_link (KzEmbed *kzembed,
			  KzEmbedNavLink link)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	KzNavi *navi = KZ_NAVI(g_list_nth_data(priv->nav_links[link], 0));
	g_return_if_fail(navi);

	kz_gecko_embed_load_url(kzembed, navi->uri);
}


static void
kz_gecko_embed_append_nav_link (KzEmbed *kzembed,
			      KzEmbedNavLink link,
			      KzNavi *navi)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	g_return_if_fail(link < KZ_EMBED_LINK_GUARD);
	g_return_if_fail(navi);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	priv->nav_links[link] = g_list_append(priv->nav_links[link],
					      g_object_ref(navi));
}

static void
kz_gecko_embed_set_nth_nav_link (KzEmbed *kzembed,
			       KzEmbedNavLink link,
			       KzNavi *navi,
			       guint n)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	g_return_if_fail(link < KZ_EMBED_LINK_GUARD);
	g_return_if_fail(navi);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	guint len = g_list_length(priv->nav_links[link]);
	GList *nth_nav_link;

	if (n >= len)
	{
		int i;
		for (i = len; i < n; i++)
		{
			priv->nav_links[link] =
				g_list_append(priv->nav_links[link], NULL);
		}
	}
	
	nth_nav_link = g_list_nth(priv->nav_links[link], n);
	if (nth_nav_link->data)
		g_object_unref(nth_nav_link->data);
	nth_nav_link->data = g_object_ref(navi);
}


static KzNavi *
kz_gecko_embed_get_nth_nav_link (KzEmbed *kzembed,
			       KzEmbedNavLink link,
			       guint n)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), NULL);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE(kzembed);

	return KZ_NAVI(g_list_nth_data(priv->nav_links[link], n));
}


static GList *
kz_gecko_embed_get_nav_links (KzEmbed *kzembed,
			    KzEmbedNavLink link)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), NULL);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE(kzembed);

	return priv->nav_links[link];
}


static void
kz_gecko_embed_go_history_index (KzEmbed *kzembed, gint index)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	g_return_if_fail(priv->wrapper);
	
	priv->wrapper->GoHistoryIndex(index);
}

static void
kz_gecko_embed_copy_page (KzEmbed *kzembed, KzEmbed *dkzembed,
			KzEmbedCopyType type)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	g_return_if_fail(KZ_IS_GECKO_EMBED(dkzembed));

	KzMozWrapper *dWrapper = KZ_GECKO_EMBED_GET_PRIVATE(dkzembed)->wrapper;;
	KzMozWrapper *sWrapper = KZ_GECKO_EMBED_GET_PRIVATE(kzembed)->wrapper;;

        nsresult rv;

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

	PRUint32 aDisplayType = nsIWebPageDescriptor::DISPLAY_NORMAL;
	switch (type) {
	  case KZ_EMBED_COPY_NORMAL:
	    aDisplayType = nsIWebPageDescriptor::DISPLAY_NORMAL;
	    break;
	  case KZ_EMBED_COPY_SOURCE:
	    aDisplayType = nsIWebPageDescriptor::DISPLAY_AS_SOURCE;
	    break;
	}
        rv = dWrapper->LoadDocument(pageDescriptor, aDisplayType);
}


static gboolean
kz_gecko_embed_shistory_copy (KzEmbed *source,
			    KzEmbed *dest,
			    gboolean back_history,
			    gboolean forward_history,
			    gboolean set_current)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(source), FALSE);
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(dest), FALSE);

	nsresult rv;
	KzMozWrapper *d_wrapper = KZ_GECKO_EMBED_GET_PRIVATE(dest)->wrapper;;
	KzMozWrapper *s_wrapper = KZ_GECKO_EMBED_GET_PRIVATE(source)->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 */
static gboolean
kz_gecko_embed_shistory_get_pos (KzEmbed *kzembed,
			       int *pos, int *count)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	nsresult rv;
	int total, index;

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

	*pos = index;
	*count = total;

	return NS_SUCCEEDED(rv) ? TRUE : FALSE;
}

/* picked from galeon-1.3.11a */
static void
kz_gecko_embed_shistory_get_nth (KzEmbed *kzembed, 
			       int nth,
			       gboolean is_relative,
			       char **aUrl,
			       char **aTitle)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbed *mozembed = KZ_GECKO_EMBED(kzembed);
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	KzMozWrapper *wrapper = priv->wrapper;
	nsresult rv;

	if (is_relative)
	{
		int pos, count;
		if (kz_gecko_embed_shistory_get_pos(kzembed, &pos, &count))
			pos += nth;
		else
			return;
		nth = pos;
	}
	
        nsEmbedCString url;
        rv = wrapper->GetSHUrlAtIndex(nth, url);

        *aUrl = (NS_SUCCEEDED (rv) && url.Length()) ? g_strdup(url.get()) : NULL;

	PRUnichar *title;
	rv = wrapper->GetSHTitleAtIndex(nth, &title);

	nsEmbedCString str;
	NS_UTF16ToCString(nsEmbedString(title),
			  NS_CSTRING_ENCODING_UTF8, str);
	*aTitle = g_strdup (str.get());
	nsMemory::Free(title);
}


static gboolean
kz_gecko_embed_get_lock (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	return priv->lock;
}


static void
kz_gecko_embed_set_lock (KzEmbed *kzembed, gboolean lock)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	priv->lock = lock;
}


static gchar *
kz_gecko_embed_get_body_text(KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);

	nsEmbedString text;
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	g_return_val_if_fail(priv->wrapper, FALSE);
	priv->wrapper->GetBodyString(text);

	nsEmbedCString cText;
	NS_UTF16ToCString(text,
			  NS_CSTRING_ENCODING_UTF8, cText);
	return g_strdup(cText.get());
}


static void
kz_gecko_embed_set_encoding (KzEmbed *kzembed, const char *encoding)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	g_return_if_fail(priv->wrapper);
	priv->wrapper->ForceEncoding(encoding);
}

static void
kz_gecko_embed_get_encoding (KzEmbed *kzembed, char **encoding, gboolean *forced)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	g_return_if_fail(priv->wrapper);
	priv->wrapper->GetEncoding(encoding, *forced);
}

static gboolean 
kz_gecko_embed_save_with_content (KzEmbed *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;

	nsEmbedCString 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);

	nsEmbedString DataPath;
	NS_CStringToUTF16(nsEmbedCString(datapath),
			  NS_CSTRING_ENCODING_UTF8, DataPath);
	NS_NewLocalFile(DataPath, PR_TRUE, getter_AddRefs(path));
	g_free (datapath);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	wrapper = 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);

	nsCOMPtr<nsIDOMDocument> DOMDocument;

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

	nsCOMPtr<nsIURI> inURI;
	nsEmbedCString sURI;

	rv = wrapper->GetDocumentUrl(sURI);
      	rv = NewURI(getter_AddRefs(inURI), sURI.get());

	// 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;
}


static void
kz_gecko_embed_print (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

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

	priv->wrapper->Print();
}


static void
kz_gecko_embed_print_preview (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

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


static GList *
kz_gecko_embed_get_printer_list (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), NULL);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	g_return_val_if_fail (priv->wrapper != NULL, NULL);
	
	GList *list = NULL;
	priv->wrapper->GetPrinterList(&list);

	return list;
}


static gchar *
kz_gecko_embed_get_text_from_textarea (KzEmbed *kzembed,
				     gpointer element)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), NULL);

	nsIDOMHTMLElement *htmlElement = (nsIDOMHTMLElement*)element;

	nsCOMPtr<nsIDOMHTMLTextAreaElement> tElement = do_QueryInterface(htmlElement);

	g_return_val_if_fail(tElement, NULL);

	nsEmbedString string;
	tElement->GetValue(string);

	nsEmbedCString cString;
	NS_UTF16ToCString(string,
			  NS_CSTRING_ENCODING_UTF8, cString);

	return g_strdup(cString.get());
}


static gboolean
kz_gecko_embed_set_text_into_textarea (KzEmbed *kzembed,
				     gpointer element,
				     const gchar *text)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);

	nsIDOMHTMLElement *htmlElement = (nsIDOMHTMLElement*)element;

	nsCOMPtr<nsIDOMHTMLTextAreaElement> tElement = do_QueryInterface(htmlElement);

	if (!tElement) return FALSE;

	nsEmbedString string;
	NS_CStringToUTF16(nsEmbedCString(text),
			  NS_CSTRING_ENCODING_UTF8, string);
	tElement->SetValue(string);

	return TRUE;
}


static gchar *
kz_gecko_embed_store_history_file(KzGeckoEmbed *kzembed)
{
	nsresult rv;

	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), NULL);
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	KzMozWrapper *wrapper = 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;
	nsEmbedCString sURI;

	rv = wrapper->GetDocumentUrl(sURI);
      	rv = NewURI(getter_AddRefs(inURI), sURI.get());

	gchar *dir, *filename;

	filename = create_filename_with_path_from_uri(sURI.get());
		
	dir = g_build_filename(g_get_home_dir(),
			       HISTORY_DIR,
			       filename,
			       NULL);
	g_free(filename);

	nsCOMPtr<nsILocalFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
	rv = localFile->InitWithNativePath(nsEmbedCString(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);

	// update timestamp file
	gchar *timestamp_file = g_build_filename(g_get_home_dir(),
			HISTORY_DIR,
			"timestamp",
			NULL);

	struct stat st;
	g_stat(dir, &st);
	// Hmm, rounding st_mtime causes some problem I think. Is it OK?
	gchar *time = g_strdup_printf("%d", (int)st.st_mtime);

	GIOChannel *io = g_io_channel_new_file(timestamp_file, "a+", NULL);
	g_io_channel_set_encoding(io, NULL, NULL);
	g_io_channel_write_chars(io, time, strlen(time),
			NULL, NULL);
	g_io_channel_write_chars(io, ",", 1, NULL, NULL);
	g_io_channel_write_chars(io, dir, strlen(dir),
			NULL, NULL);
	g_io_channel_write_chars(io, "\n", 1, NULL, NULL);
	g_io_channel_shutdown(io, TRUE, NULL);
	g_io_channel_unref(io);

	g_free(time);
	g_free(timestamp_file);

	return dir;
}


#ifndef MOZ_NSICANVASRENDERINGCONTEXTINTERNAL_HAVE_GETINPUTSTREAM_
static KzMozThumbnailCreator *
kz_window_create_thumbnail_creator (KzWindow *kz)
{
	KzMozThumbnailCreator *creator;

	creator	= KZ_MOZ_THUMBNAIL_CREATOR(g_object_get_data(G_OBJECT(kz),
							     "KzGeckoEmbed::ThumbnailCreator"));
	if (!creator)
	{
		creator = kz_moz_thumbnail_creator_new();
		gtk_widget_set_size_request(GTK_WIDGET(creator), 0, 0);
		gtk_widget_show(GTK_WIDGET(creator));

		gtk_box_pack_start(GTK_BOX(kz->statusbar),
				   GTK_WIDGET(creator),
				   FALSE, FALSE, 0);
		g_object_set_data(G_OBJECT(kz), 
				  "KzGeckoEmbed::ThumbnailCreator", 
				  creator);
	}

	return creator;
}
#endif

static void
kz_gecko_embed_create_thumbnail (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	gboolean create_thumbnail = FALSE;
	KZ_CONF_GET("Global", "create_thumbnail", create_thumbnail, BOOL);
	if (!create_thumbnail) return;

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	KzMozWrapper *wrapper = priv->wrapper;
	g_return_if_fail (wrapper != NULL);

#ifdef MOZ_NSICANVASRENDERINGCONTEXTINTERNAL_HAVE_GETINPUTSTREAM_
	wrapper->CreateThumbnail();
#else
	nsresult rv;
	int total, index;
	rv = wrapper->GetSHInfo (&total, &index);

	nsCOMPtr<nsIHistoryEntry> he;
	rv = wrapper->GetHistoryEntry(index, getter_AddRefs(he));
	if (NS_FAILED(rv)) return;

	GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(kzembed));

	if (!KZ_IS_WINDOW(window)) return;

	KzWindow *kz = KZ_WINDOW(window);

	KzMozThumbnailCreator *creator;
	creator = kz_window_create_thumbnail_creator(kz);
	kz_moz_thumbnail_creator_append_queue(creator, he);
#endif
}


void
kz_gecko_embed_do_command (KzEmbed *kzembed, const char *command)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	nsCOMPtr<nsICommandManager> commandManager;
	commandManager = do_GetInterface(priv->wrapper->mWebBrowser);
	if (!commandManager) return;
	
	commandManager->DoCommand(command, nsnull, nsnull);

}

static gboolean
kz_gecko_embed_can_do_command (KzEmbed *kzembed, const char *command)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);
	
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	nsCOMPtr<nsICommandManager> commandManager;
	commandManager = do_GetInterface(priv->wrapper->mWebBrowser);
	if (!commandManager) return FALSE;
	
	PRBool enabled;
	commandManager->IsCommandEnabled(command, nsnull, &enabled);

	return (enabled == PR_TRUE);
}

static void
kz_gecko_embed_zoom_set (KzEmbed *kzembed, int zoom, gboolean reflow)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	priv->wrapper->SetImageZoom ((float)(zoom) / 100);
		
	kz_gecko_embed_set_text_size(kzembed, zoom, reflow);
}

static int
kz_gecko_embed_zoom_get (KzEmbed *kzembed)
{
	return kz_gecko_embed_get_text_size(kzembed);
}


static void
kz_gecko_embed_set_text_size (KzEmbed *kzembed,  int zoom, gboolean reflow)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	priv->wrapper->SetZoom ((float)(zoom) / 100, reflow);
}

static int 
kz_gecko_embed_get_text_size (KzEmbed *kzembed)
{
	float f;
	int zoom;

	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), 100);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (!priv->wrapper) return 100;

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

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

	return zoom;
}

static void
kz_gecko_embed_set_history (KzEmbed *kzembed, KzBookmark *history)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	g_return_if_fail(KZ_IS_BOOKMARK(history));

	if (!kz_bookmark_is_folder(history)) return;

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (!priv->wrapper) return;

	nsCOMPtr<nsISHistory> sHistory;
	nsresult rv = priv->wrapper->GetSHistory(getter_AddRefs(sHistory));
	if (NS_FAILED(rv) || !sHistory) return;

	nsCOMPtr<nsISHistoryInternal> sHistoryInternal;
	sHistoryInternal = do_QueryInterface(sHistory);

	PRInt32 n;
	sHistory->GetCount(&n);
	sHistory->PurgeHistory(n);

	GList *children, *node;
	children = kz_bookmark_get_children(history);
	for (node = children; node; node = g_list_next (node))
	{
		KzBookmark *child = KZ_BOOKMARK(node->data);
		const gchar *title, *uri;

		title = kz_bookmark_get_title(child);
		uri   = kz_bookmark_get_link(child);

		nsCOMPtr<nsISHEntry> entry;
		entry = do_CreateInstance(NS_SHENTRY_CONTRACTID);

		nsCOMPtr<nsIURI> aURI;
		NewURI(getter_AddRefs(aURI), uri);		
		/* FIXME! set correct contentType */
		nsEmbedCString contentType;
		entry->SetURI(aURI);
		sHistoryInternal->AddEntry(entry, PR_TRUE);
	}
	g_list_free(children);

	/* set current */
	gint cur = kz_bookmark_get_current(history);
	kz_gecko_embed_go_history_index(kzembed, cur);
}

static void
kz_gecko_embed_get_history (KzEmbed *kzembed, KzBookmark *history)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));
	g_return_if_fail(KZ_IS_BOOKMARK(history));

	if (!kz_bookmark_is_folder(history)) return;

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);
	if (!priv->wrapper) return;

	nsCOMPtr<nsISHistory> sHistory;
	nsresult rv = priv->wrapper->GetSHistory(getter_AddRefs(sHistory));

	if (NS_FAILED(rv) || !sHistory) return;

	GList *children, *tab;
	children = kz_bookmark_get_children(history);
	tab = children;

	PRInt32 count;
	sHistory->GetCount(&count);

	PRInt32 index;
	sHistory->GetIndex(&index);
	for (PRInt32 i = 0; i < count; i++)
	{
		gchar *title = NULL, *uri = NULL;
		KzBookmark *bookmark = NULL;
		
		kz_gecko_embed_shistory_get_nth (kzembed,
					       i, FALSE,
					       &uri,
					       &title);
		if (tab)
		{
			bookmark = KZ_BOOKMARK(tab->data);
			tab = g_list_next(tab);
		}

		/* if there's no children of bookmarks, create a new bookmark */
		if (!bookmark)
		{
			bookmark = kz_bookmark_new_with_attrs(title, uri, NULL);
			kz_bookmark_append(history, bookmark);
			g_object_unref(bookmark);
		}
		else if (uri && !strcmp(uri, kz_bookmark_get_link(bookmark)))
		{
			g_free(uri);
			continue;
		}
		else
		{
			kz_bookmark_set_link(bookmark, uri);
			kz_bookmark_set_title(bookmark, title);
			kz_bookmark_set_last_visited(bookmark, 0);
		}

		if (title)
			g_free(title);
		if (uri)
			g_free(uri);
	}

	if (tab)
		tab = g_list_last(tab);
	GList *prev;

	while (tab)
	{
		KzBookmark *child = KZ_BOOKMARK(tab->data);
		prev = g_list_previous(tab);
		kz_bookmark_remove (history, child);
		tab = prev;
	}

	if (children)
		g_list_free(children);

	kz_bookmark_set_current(history, (gint)index);
}


static guint
kz_gecko_embed_get_last_modified (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), 0);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (!priv->wrapper) return 0;

	nsresult result;

	nsCOMPtr<nsIDOMDocument> DOMDocument;

	result = priv->wrapper->GetDocument (getter_AddRefs(DOMDocument));
	if (NS_FAILED(result) || !DOMDocument) return 0;

	nsCOMPtr<nsIDOMNSDocument> doc = do_QueryInterface(DOMDocument);
	if(!doc) return 0;
	
	nsEmbedString value;

	doc->GetLastModified(value);

	guint mod_time = 0;
	nsEmbedCString cValue;
	NS_UTF16ToCString(value,
			  NS_CSTRING_ENCODING_UTF8, cValue);
	nsTime last_modified (cValue.get(), PR_TRUE);
	LL_DIV (mod_time,
		NS_STATIC_CAST(PRTime, last_modified), PR_USEC_PER_SEC);
	return mod_time;
}


#if 0
static void
kz_gecko_embed_set_edit_mode (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbed *mozembed = KZ_GECKO_EMBED(kzembed);

	nsCOMPtr<nsIDOMWindow> domWindow;
	priv->wrapper->GetDOMWindow(getter_AddRefs(domWindow));
	
	nsCOMPtr<nsIEditingSession> edit;
	edit = do_GetInterface(priv->wrapper->mWebBrowser);
	if (edit)
	{
		edit->MakeWindowEditable(domWindow, "html", PR_FALSE);
	}
}

static void
kz_gecko_embed_set_view_mode (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbed *mozembed = KZ_GECKO_EMBED(kzembed);
	
	nsCOMPtr<nsIDOMWindow> domWindow;
	priv->wrapper->GetDOMWindow(getter_AddRefs(domWindow));
	
	nsCOMPtr<nsIEditingSession> edit;
	edit = do_GetInterface(priv->wrapper->mWebBrowser);
	if (edit)
	{
		edit->TearDownEditorOnWindow(domWindow);
	}
}
#endif

static void
kz_gecko_embed_fine_scroll (KzEmbed *kzembed,
			  int horiz, int vert)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (!priv->wrapper) return;

	priv->wrapper->FineScroll (horiz, vert);
}

static void
kz_gecko_embed_page_up (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (!priv->wrapper) return;
	
	priv->wrapper->PageUp();
}

static void
kz_gecko_embed_page_down (KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (!priv->wrapper) return;
	
	priv->wrapper->PageDown();
}

static gboolean
kz_gecko_embed_get_allow_javascript (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (!priv->wrapper) return FALSE;

	PRBool allow;
	priv->wrapper->GetAllowJavascript(&allow);

	return allow ? TRUE : FALSE;
}

static void
kz_gecko_embed_set_allow_javascript (KzEmbed *kzembed, gboolean allow)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (!priv->wrapper) return;
	
	priv->wrapper->SetAllowJavascript(allow);
}

static gboolean
kz_gecko_embed_get_allow_images (KzEmbed *kzembed)
{
	g_return_val_if_fail(KZ_IS_GECKO_EMBED(kzembed), FALSE);

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (!priv->wrapper) return FALSE;

	PRBool allow;
	priv->wrapper->GetAllowImages(&allow);

	return allow ? TRUE : FALSE;
}

static void
kz_gecko_embed_set_allow_images (KzEmbed *kzembed, gboolean allow)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (!priv->wrapper) return;
	
	priv->wrapper->SetAllowImages(allow);
}

static void
kz_gecko_embed_show_page_certificate (KzEmbed       *kzembed)
{
	g_return_if_fail(KZ_IS_GECKO_EMBED(kzembed));

	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE (kzembed);

	if (!priv->wrapper) return;

	priv->wrapper->ShowPageCertificate();

}

static void
nav_link_elem_free(gpointer object, gpointer unused)
{
	if (object)
		g_object_unref(G_OBJECT(object));
}

static void
kz_gecko_embed_navigation_link_free(KzGeckoEmbed *kzembed)
{	
	gint i;
	KzGeckoEmbedPrivate *priv = KZ_GECKO_EMBED_GET_PRIVATE(kzembed);
	
	for (i = 0; i < KZ_EMBED_LINK_GUARD; i++)
	{
		if (priv->nav_links[i])
		{
			g_list_foreach(priv->nav_links[i], nav_link_elem_free, NULL);
			g_list_free(priv->nav_links[i]);
			priv->nav_links[i] = NULL;
		}
	}
#ifdef USE_MIGEMO
	if (priv->migemo_keyword)
		g_free(priv->migemo_keyword);
	priv->migemo_keyword =NULL;
#endif
	if (priv->last_highlight)
		g_free(priv->last_highlight);
	priv->last_highlight = NULL;
}

