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

//
//  Copyright (C) 2002-2003 Hiroyuki Ikezoe
//  Copyright (C) 2003 Takuro Ashie
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2, or (at your option)
//  any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

#include "kz-mozembed.h"

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

#include "kz-mozwrapper.h"
#include "gobject-utils.h"
#include "intl.h"
#include "mozilla.h"
#include "mozilla-prefs.h"
#include "kz-link.h"

#include <xpcom/nsCOMPtr.h>
#include <dom/nsIDOMDocument.h>
#include <content/nsIDocumentViewer.h>
#include <webbrwsr/nsIWebBrowser.h>
#include <content/nsIDocument.h>
#include <dom/nsIDOMMouseEvent.h>
#include <dom/nsIDOMKeyEvent.h>
#include <dom/nsIDOMEventTarget.h>
#include <dom/nsIDOMHTMLElement.h>
#include <dom/nsIDOMNamedNodeMap.h>
#include <dom/nsIDOMNSHTMLElement.h>  
#include <webbrowserpersist/nsIWebBrowserPersist.h>
#include <necko/nsNetUtil.h>
#include <find/nsIWebBrowserFind.h>
#include <dom/nsIDOMNSDocument.h>
#include <dom/nsIDOMNSEvent.h>
#include <docshell/nsIDocShell.h>
#include <docshell/nsIDocShellTreeItem.h>
#include <docshell/nsIDocShellTreeOwner.h>
#include <dom/nsIDOMNodeList.h>
#include <dom/nsIDOMWindow.h>
#include <content/nsISelection.h>
#include <dom/nsIDOMRange.h>
#include <find/nsIWebBrowserFind.h>
#include <necko/nsNetUtil.h>
#include <uconv/nsICharsetConverterManager.h>
#ifndef MOZILLA_SNAPSHOT
#include <uconv/nsICharsetConverterManager2.h>
#endif
#include <dom/nsIDOMWindow.h>
#include <content/nsISelection.h>

struct _KzMozEmbedPriv
{
	KzMozWrapper *wrapper;
	gint size_inited;
};

static void kz_moz_embed_class_init    (KzMozEmbedClass *klass);
static void kz_moz_embed_init          (KzMozEmbed *kzembed);
static void kz_moz_embed_destroy       (GtkObject *object);
static void kz_moz_embed_finalize      (GObject    *object);
static void kz_moz_embed_realize       (GtkWidget *widget);
static void kz_moz_embed_unrealize     (GtkWidget *widget);
static void kz_moz_embed_size_allocate (GtkWidget *widget,
					GtkAllocation *allocation);
static void kz_moz_embed_title         (GtkMozEmbed *embed);
static void kz_moz_embed_location      (GtkMozEmbed *embed);
static void kz_moz_embed_net_start     (GtkMozEmbed *embed);
static gint kz_moz_embed_open_uri      (GtkMozEmbed *embed, const char *uri);
static void kz_moz_embed_size_to       (GtkMozEmbed *embed,
					gint width, gint height);
static void kz_moz_embed_visibility    (GtkMozEmbed *embed,
					gboolean visibility);
static void kz_moz_embed_net_stop      (GtkMozEmbed *embed);
static gint kz_moz_embed_dom_key_down  (GtkMozEmbed *embed, gpointer event);
static gint kz_moz_embed_dom_key_up    (GtkMozEmbed *embed, gpointer event);
static gint kz_moz_embed_dom_key_press (GtkMozEmbed *embed, gpointer event);

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

static GtkMozEmbedClass *parent_class = NULL;


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


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

	parent_class    = (GtkMozEmbedClass *) g_type_class_peek_parent (klass);

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

	// GObject
	gobject_class->finalize = kz_moz_embed_finalize;

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

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


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

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

	// priv
	kzembed->priv = g_new0(KzMozEmbedPriv, 1);
	kzembed->priv->wrapper     = NULL;
	kzembed->priv->size_inited = FALSE;

	kz_moz_embed_load_url(kzembed, "about:blank");
}

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

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

	return GTK_WIDGET(kzembed);
}

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

	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

	g_free(kzembed->location);
	kzembed->location = NULL;
	g_free(kzembed->title);
	kzembed->title = NULL;

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


KZ_OBJECT_FINALIZE (kz_moz_embed, KzMozEmbed)


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

	KzMozEmbed *kzembed = KZ_MOZ_EMBED(widget);

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


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

	kzembed->priv->size_inited = FALSE;

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

	if (GTK_WIDGET_CLASS(parent_class)->unrealize)
		GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
}


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

	g_return_if_fail(GTK_IS_WIDGET(widget));

	if (!GTK_WIDGET_REALIZED(widget)) return;

	if (kzembed->priv->size_inited && !GTK_WIDGET_MAPPED(widget)) return;

	if (GTK_WIDGET_CLASS(parent_class)->size_allocate)
		GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);

	kzembed->priv->size_inited = TRUE;
}


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

	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

	if (url && *url)
	{
		start_page = g_strdup(url);
	}
	else if (mozilla_prefs_get_string("kazehakase.startup.homepage", &start_page))
	{
		if (!start_page || !*start_page)
			start_page = g_strdup("about:blank");
	}
	else
	{
		start_page = g_strdup("about:blank");
	}

	gtk_moz_embed_load_url(GTK_MOZ_EMBED(kzembed), start_page);

	g_free(start_page);
}

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

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

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

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

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

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

	gchar *message;
	nsXPIDLString  uMessage;

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

	return message;
}

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

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

	KzMozEmbed *kzembed = KZ_MOZ_EMBED(embed);

	g_free(kzembed->title);

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

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

	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

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


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

	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

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

	kzembed->is_loading = TRUE;
}

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

	g_return_if_fail(KZ_IS_MOZ_EMBED(kzembed));

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

	kzembed->is_loading = FALSE;
}

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

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

	return FALSE;
}

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

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

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

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

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

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

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

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

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

	if (NS_FAILED(rv)) return FALSE;

	return retval;
}

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

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

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

	if (NS_FAILED(rv)) return FALSE;

	return retval;
}

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

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

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

	if (NS_FAILED(rv)) return FALSE;

	return retval;
}

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

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

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

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

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

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

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

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

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

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

	nsresult rv;
	nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(web));
	finder->SetSearchString(NS_ConvertUTF8toUCS2(keyword).get());
	finder->SetFindBackwards(FALSE);
	finder->SetWrapFind(TRUE);
	finder->SetEntireWord(TRUE);
	finder->SetSearchFrames(TRUE);
	finder->SetMatchCase(FALSE);
	PRBool did_find;
	rv = finder->FindNext(&did_find);

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

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

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

	nsresult rv;

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

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

	return collapsed;
}


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

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

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

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

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

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

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

		KzLink *kzlink = kz_link_new();
		mozilla_get_link_from_node(domDoc, node,
					   &kzlink->url,
					   &kzlink->title);
		if (kzlink->url && *kzlink->url)
			*list = g_list_append(*list, kzlink);
		else
			kz_link_unref(kzlink);
	}

	return TRUE;
}


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

	nsresult result;

	nsIDOMKeyEvent *aKeyEvent = (nsIDOMKeyEvent*) event;

     	nsCOMPtr<nsIDOMEventTarget> OriginalTarget;

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

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

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

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

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

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

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

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

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

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

     	nsString nodename;
     	OriginalNode->GetNodeName(nodename);

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

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

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


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

	nsresult result;

	nsIDOMMouseEvent *aMouseEvent = (nsIDOMMouseEvent*)event;

     	nsCOMPtr<nsIDOMEventTarget> OriginalTarget;

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

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

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

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

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

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

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

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

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

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

     	nsString nodename;
     	OriginalNode->GetNodeName(nodename);

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

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

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


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

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

	nsAutoString attr; 

	attr.AssignWithConversion(attribute);

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

	nsAutoString nodeValue;

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

	return ToNewCString(nodeValue);
}


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

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

	nsAutoString hrefa;
	hrefa.AssignWithConversion(hrefattr);

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

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

	g_free(hrefattr);

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

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

	nsAutoString linkhtml;
	nsCOMPtr<nsIDOMNSHTMLElement> nsElement;

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

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

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

	return TRUE;
}


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

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

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

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

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

	nsIURI *baseURI;
#ifdef MOZILLA_SNAPSHOT
	result = doc->GetBaseURL(&baseURI);
#else
	result = doc->GetBaseURL(baseURI);
#endif
	if (NS_FAILED(result) || !baseURI) return KZ_CONTEXT_NONE;

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

	PRUint32 flags = KZ_CONTEXT_NONE;

	// check whether the node is in the selection or not
	nsCOMPtr<nsISelection> selection;
	result = kzembed->priv->wrapper->GetSelection(getter_AddRefs(selection));
	if (selection)
	{
		PRBool contains;
		selection->ContainsNode(node, PR_TRUE, &contains);
		if (contains)
			flags |= KZ_CONTEXT_SELECTION;
	}

	// Get other context
	nsCOMPtr<nsIDOMHTMLElement> element;

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

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

			if (tag.EqualsWithConversion("input", PR_TRUE))
			{
				flags |= KZ_CONTEXT_INPUT;
			}
			else if (tag.EqualsWithConversion("textarea", PR_TRUE))
			{
				flags |= KZ_CONTEXT_INPUT;
			}
			else if (tag.EqualsWithConversion("img", PR_TRUE))
			{
				flags |= KZ_CONTEXT_IMAGE;

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

			     	nsAutoString srca;
			     	srca.AssignWithConversion(src);

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

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

			     	info->img = ToNewCString(imgc);

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

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

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

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

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

	info->context = flags;

	return flags;
}
