/*
 *  Copyright (C) 2000-2004 Marco Pesenti Gritti
 *  Copyright (C) 2003, 2004 Christian Persch
 *  Copyright (C) 2004 Crispin Flowerday
 *
 *  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.
 *
 *  $Id$
 */

#include "mozilla-config.h"

#include "config.h"

#include "EventContext.h"
#include "EphyUtils.h"
#include "ephy-debug.h"

#include <gdk/gdkkeysyms.h>

#include <nsIInterfaceRequestor.h>
#include <nsIInterfaceRequestorUtils.h>
#include <nsIServiceManager.h>
#undef MOZILLA_INTERNAL_API
#include <nsEmbedString.h>
#define MOZILLA_INTERNAL_API 1
#include <nsIDOMEventTarget.h>
#include <nsIDOMHTMLInputElement.h>
#include <nsIDOMHTMLObjectElement.h>
#include <nsIDOMHTMLImageElement.h>
#include <nsIDOMElement.h>
#include <nsIURI.h>
#include <nsIDOMCharacterData.h>
#include <nsIDOMHTMLAreaElement.h>
#include <nsIDOMHTMLButtonElement.h>
#include <nsIDOMHTMLLabelElement.h>
#include <nsIDOMHTMLLegendElement.h>
#include <nsIDOMHTMLMapElement.h>
#include <nsIDOMHTMLTextAreaElement.h>
#include <nsIDOMElementCSSInlineStyle.h>
#include <nsIDOMCSSStyleDeclaration.h>
#include <nsIDOM3Node.h>
#include <nsIDOMCSSPrimitiveValue.h>
#include <nsIDOMNodeList.h>
#include <nsIDOMDocumentView.h>
#include <nsIDOMAbstractView.h>
#include <nsIDOMNSHTMLDocument.h>
#include <nsIDOMNSUIEvent.h>
#include <nsIDOMHTMLSelectElement.h>
#include <nsIDOMHTMLIsIndexElement.h>

#ifdef ALLOW_PRIVATE_API
#include <nsITextToSubURI.h>
#include <nsIDOMXULDocument.h>
#include <nsIDOMNSEvent.h>
#include <nsIDOMNSHTMLElement.h>
#include <nsIDOMViewCSS.h>
#endif

#define KEY_CODE 256

EventContext::EventContext ()
{
	LOG ("EventContext ctor [%p]", this);
}

EventContext::~EventContext ()
{
	LOG ("EventContext dtor [%p]", this);
}

nsresult EventContext::Init (EphyBrowser *browser)
{
	mBrowser = browser;
	mDOMDocument = nsnull;

	return NS_OK;
}

nsresult EventContext::GatherTextUnder (nsIDOMNode* aNode, nsAString& aResult)
{
	nsEmbedString text;
	nsCOMPtr<nsIDOMNode> node;
	aNode->GetFirstChild(getter_AddRefs(node));
	PRUint32 depth = 1;

	while (node && depth)
	{
		nsCOMPtr<nsIDOMCharacterData> charData(do_QueryInterface(node));
		PRUint16 nodeType;

		node->GetNodeType(&nodeType);
		if (charData && nodeType == nsIDOMNode::TEXT_NODE)
		{
			/* Add this text to our collection. */
			text += ' ';
			nsEmbedString data;
			charData->GetData(data);
			text += data;
		}
		else
		{
			nsCOMPtr<nsIDOMHTMLImageElement> img(do_QueryInterface(node));
			if (img)
			{
				nsEmbedString altText;
				img->GetAlt(altText);
				if (altText.Length())
				{
					text = altText;
					break;
				}
			}
		}

		/* Find the next node to test. */
		PRBool hasChildNodes;
		node->HasChildNodes(&hasChildNodes);
		if (hasChildNodes)
		{
			nsCOMPtr<nsIDOMNode> temp = node;
			temp->GetFirstChild(getter_AddRefs(node));
			depth++;
		}
		else
		{
			nsCOMPtr<nsIDOMNode> nextSibling;
			node->GetNextSibling(getter_AddRefs(nextSibling));
			if (nextSibling)
			{
				node = nextSibling;
			}
			else
			{
				nsCOMPtr<nsIDOMNode> parentNode;
				node->GetParentNode(getter_AddRefs(parentNode));
				if (!parentNode)
				{
					node = nsnull;
				}
				else
				{
					parentNode->GetNextSibling(getter_AddRefs(nextSibling));
					node = nextSibling;
					depth--;
				}
			}
		}
	}

	/* FIXME we should trim spaces here */

	aResult = text;

	return NS_OK;
}

/* FIXME: we should resolve against the element's base, not the document's base */
nsresult EventContext::ResolveBaseURL (const nsAString &relurl, nsACString &url)
{
	nsEmbedCString cRelURL;
	NS_UTF16ToCString (relurl, NS_CSTRING_ENCODING_UTF8, cRelURL);	

	return mBaseURI->Resolve (cRelURL, url);
}

nsresult EventContext::Unescape (const nsACString &aEscaped, nsACString &aUnescaped)
{
	if (!aEscaped.Length()) return NS_ERROR_FAILURE;

	nsCOMPtr<nsITextToSubURI> escaper
		(do_CreateInstance ("@mozilla.org/intl/texttosuburi;1"));
	NS_ENSURE_TRUE (escaper, NS_ERROR_FAILURE);

	nsresult rv;
	nsEmbedCString encoding;
	rv = mBrowser->GetEncoding (encoding);
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

	nsEmbedString unescaped;
	rv = escaper->UnEscapeURIForUI (encoding, aEscaped, unescaped);
	NS_ENSURE_TRUE (NS_SUCCEEDED (rv) && unescaped.Length(), NS_ERROR_FAILURE);

	NS_UTF16ToCString (unescaped, NS_CSTRING_ENCODING_UTF8, aUnescaped);

	return NS_OK;
}

nsresult EventContext::GetEventContext (nsIDOMEventTarget *EventTarget,
					MozillaEmbedEvent *info)
{
	nsresult rv;

	const PRUnichar hrefLiteral[] = {'h', 'r', 'e', 'f', '\0'};
	const PRUnichar imgLiteral[] = {'i', 'm', 'g', '\0'};
	const PRUnichar typeLiteral[] = {'t', 'y', 'p', 'e', '\0'};
	const PRUnichar xlinknsLiteral[] = {'h', 't', 't', 'p', ':', '/', '/','w',
				            'w', 'w', '.', 'w', '3', '.', 'o', 'r',
				            'g', '/', '1', '9', '9', '9', '/', 'x',
				            'l', 'i', 'n', 'k', '\0'};
	const PRUnichar bodyLiteral[] = { 'b', 'o', 'd', 'y', '\0' };

	mEmbedEvent = info;

	info->context = EPHY_EMBED_CONTEXT_DOCUMENT;

	nsCOMPtr<nsIDOMNode> node = do_QueryInterface(EventTarget, &rv);
	if (NS_FAILED(rv) || !node) return NS_ERROR_FAILURE;

        /* Is page xul ? then do not display context menus
	 * FIXME I guess there is an easier way ... */
	/* From philipl: This test needs to be here otherwise we
	 * arrogantly assume we can QI to a HTMLElement, which is
	 * not true for xul content. */ 

	nsCOMPtr<nsIDOMDocument> domDoc;
	rv = node->GetOwnerDocument(getter_AddRefs(domDoc));
	if (NS_FAILED(rv) || !domDoc) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMXULDocument> xul_document = do_QueryInterface(domDoc);
	if (xul_document)
	{
		info->context = EPHY_EMBED_CONTEXT_NONE;
		return NS_ERROR_FAILURE;
	}

	mDOMDocument = domDoc;

	rv = mBrowser->GetEncoding (mCharset);
	NS_ENSURE_SUCCESS (rv, rv);

	/* Get base URI and CSS view */
	nsCOMPtr<nsIDOMDocumentView> docView (do_QueryInterface (domDoc));
	NS_ENSURE_TRUE (docView, NS_ERROR_FAILURE);

	nsCOMPtr<nsIDOMAbstractView> abstractView;
	docView->GetDefaultView (getter_AddRefs (abstractView));
	NS_ENSURE_TRUE (abstractView, NS_ERROR_FAILURE);
	/* the abstract view is really the DOM window */

	mViewCSS = do_QueryInterface (abstractView);
	NS_ENSURE_TRUE (mViewCSS, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebNavigation> webNav (do_GetInterface (abstractView, &rv));
	NS_ENSURE_SUCCESS (rv, rv);

	rv = webNav->GetCurrentURI (getter_AddRefs (mBaseURI));
	NS_ENSURE_SUCCESS (rv, rv);

	// Now we know that the page isn't a xul window, we can try and
	// do something useful with it.

	PRUint16 type;
	rv = node->GetNodeType(&type);
	if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

	PRBool has_background = PR_FALSE;

	nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(node);
	if ((nsIDOMNode::ELEMENT_NODE == type) && element)
	{
		nsEmbedString uTag;
		rv = element->GetLocalName(uTag);
		if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

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

		if (g_ascii_strcasecmp (tag.get(), "img") == 0)
		{
			info->context |= EPHY_EMBED_CONTEXT_IMAGE;

			nsEmbedString img;
			nsCOMPtr <nsIDOMHTMLImageElement> image = 
						do_QueryInterface(node, &rv);
			if (NS_FAILED(rv) || !image) return NS_ERROR_FAILURE;			

			rv = image->GetSrc (img);
			if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
			SetStringProperty ("image", img);
		}
		else if (g_ascii_strcasecmp (tag.get(), "area") == 0)
		{
			nsCOMPtr <nsIDOMHTMLAreaElement> area = 
						do_QueryInterface(node, &rv);
			if (NS_FAILED(rv) || !area) return NS_ERROR_FAILURE;			

			// Parent node is the map itself
			nsCOMPtr<nsIDOMNode> parentNode;
			node->GetParentNode (getter_AddRefs(parentNode));

			nsCOMPtr <nsIDOMHTMLMapElement> map = 
				do_QueryInterface(parentNode, &rv);
			if (NS_FAILED(rv) || !area) return NS_ERROR_FAILURE;			

			nsEmbedString mapName;
			rv = map->GetName (mapName);
			if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

			// Now we are searching for all the images with a usemap attribute
			nsCOMPtr<nsIDOMNodeList> imgs;
			rv = mDOMDocument->GetElementsByTagName (nsEmbedString(imgLiteral), 	
								 getter_AddRefs (imgs));
			if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
			
			PRUint32 imgs_count;
			rv = imgs->GetLength (&imgs_count);
			if (NS_FAILED (rv)) return NS_ERROR_FAILURE;

			for (PRUint32 i = 0; i < imgs_count; i++)
			{
				nsCOMPtr<nsIDOMNode> aNode;
				rv = imgs->Item (i, getter_AddRefs (aNode));
				if (NS_FAILED (rv)) continue;

				nsCOMPtr<nsIDOMHTMLImageElement> img = 
						do_QueryInterface(aNode, &rv);
				if (NS_FAILED(rv) || !img) continue;
			
				nsEmbedString imgMapName;
				rv = img->GetUseMap (imgMapName);
				if (NS_FAILED (rv)) continue;

				// usemap always starts with #
				imgMapName.Cut (0,1);

				// Check if the current image is attached to the map we are looking for
				if (EphyUtils::StringEquals(imgMapName, mapName))
				{
					nsEmbedString imgSrc;
					rv = img->GetSrc (imgSrc);
					if (NS_FAILED(rv)) continue;

					info->context |= EPHY_EMBED_CONTEXT_IMAGE;

					SetStringProperty ("image", imgSrc);

					break;
				}
			}
		}
		else if (g_ascii_strcasecmp (tag.get(), "input") == 0)
		{
			CheckInput (node);
		}
		else if (g_ascii_strcasecmp (tag.get(), "textarea") == 0)
		{
			info->context |= EPHY_EMBED_CONTEXT_INPUT;
		}
		else if (g_ascii_strcasecmp (tag.get(), "object") == 0)
		{
			nsCOMPtr<nsIDOMHTMLObjectElement> object;
			object = do_QueryInterface (node);
			if (!element) return NS_ERROR_FAILURE;

			nsEmbedString value;
			object->GetType(value);

			nsEmbedCString cValue;
			NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cValue);

			// MIME types are always lower case
			if (g_str_has_prefix (cValue.get(), "image/"))
			{
				info->context |= EPHY_EMBED_CONTEXT_IMAGE;
				
				nsEmbedString img;
				
				rv = object->GetData (img);
				if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
				
				nsEmbedCString cImg;
				rv = ResolveBaseURL (img, cImg);
                                if (NS_FAILED (rv)) return NS_ERROR_FAILURE;

				SetStringProperty ("image", cImg.get());
			}
			else
			{
				info->context = EPHY_EMBED_CONTEXT_NONE;
				return NS_OK;
			}
		}
		else if (g_ascii_strcasecmp (tag.get(), "html") == 0)
		{
			/* Clicked on part of the page without a <body>, so
			 * look for a background image in the body tag */
			nsCOMPtr<nsIDOMNodeList> nodeList;

			rv = mDOMDocument->GetElementsByTagName (nsEmbedString(bodyLiteral),
								 getter_AddRefs (nodeList));
			if (NS_SUCCEEDED (rv) && nodeList)
			{
				nsCOMPtr<nsIDOMNode> bodyNode;
				nodeList->Item (0, getter_AddRefs (bodyNode));

				nsEmbedString cssurl;
				rv = GetCSSBackground (bodyNode, cssurl);
				if (NS_SUCCEEDED (rv))
				{
					nsEmbedCString bgimg;
					rv = ResolveBaseURL (cssurl, bgimg);
					if (NS_FAILED (rv))
						return NS_ERROR_FAILURE;

					SetStringProperty ("background_image",
							   bgimg.get());

					has_background = PR_TRUE;
				}
			}
 		}
	}

	/* Is page framed ? */
	PRBool framed;
	IsPageFramed (node, &framed);
	SetIntProperty ("framed_page", framed);

	/* Bubble out, looking for items of interest */
	while (node)
	{
		nsCOMPtr <nsIDOMElement> dom_elem = do_QueryInterface(node);
		if (dom_elem)
		{
			nsEmbedString value;
			dom_elem->GetAttributeNS (nsEmbedString(xlinknsLiteral),
						  nsEmbedString(typeLiteral), value);

			nsEmbedCString cValue;
			NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cValue);

			if (g_ascii_strcasecmp (cValue.get(), "simple") == 0)
			{
				info->context |= EPHY_EMBED_CONTEXT_LINK;
				dom_elem->GetAttributeNS (nsEmbedString(xlinknsLiteral),
							  nsEmbedString(hrefLiteral), value);
				
				SetURIProperty (node, "link", value);
				CheckLinkScheme (value);
			}
		}

		rv = node->GetNodeType(&type);
		if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

		element = do_QueryInterface(node);
		if ((nsIDOMNode::ELEMENT_NODE == type) && element)
		{
			nsEmbedString uTag;
			rv = element->GetLocalName(uTag);
			if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

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

			/* Link */
			if (g_ascii_strcasecmp (tag.get(), "a") == 0)
			{
				nsEmbedString tmp;

				rv = GatherTextUnder (node, tmp);
				if (NS_SUCCEEDED(rv))
                                	SetStringProperty ("linktext", tmp);

				nsCOMPtr <nsIDOMHTMLAnchorElement> anchor =
					do_QueryInterface(node);

				nsEmbedCString href;
				anchor->GetHref (tmp);
				NS_UTF16ToCString (tmp, NS_CSTRING_ENCODING_UTF8, href);

				if (g_str_has_prefix (href.get(), "mailto:"))
				{
					/* cut "mailto:" */
					href.Cut (0, 7);

					char *str = g_strdup (href.get());
					g_strdelimit (str, "?", '\0');

					nsEmbedCString unescapedHref;
					rv = Unescape (nsEmbedCString(str), unescapedHref);
					if (NS_SUCCEEDED (rv) && unescapedHref.Length())
					{
						SetStringProperty ("email", unescapedHref.get());
						info->context |= EPHY_EMBED_CONTEXT_EMAIL_LINK;
					}
					g_free (str);
				}
				
				if (anchor && tmp.Length()) 
				{
					info->context |= EPHY_EMBED_CONTEXT_LINK;

					SetURIProperty (node, "link", tmp);
					CheckLinkScheme (tmp);
					rv = anchor->GetHreflang (tmp);
					if (NS_SUCCEEDED(rv))
						SetStringProperty ("link_lang", tmp);
					rv = anchor->GetTarget (tmp);
					if (NS_SUCCEEDED(rv))
						SetStringProperty ("link_target", tmp);
					rv = anchor->GetRel (tmp);
					if (NS_SUCCEEDED(rv))
						SetStringProperty ("link_rel", tmp);
					rv = anchor->GetRev (tmp);
					if (NS_SUCCEEDED(rv))
						SetStringProperty ("link_rev", tmp);
					rv = element->GetTitle (tmp);
		                        if (NS_SUCCEEDED(rv))
						SetStringProperty ("link_title", tmp);
					rv = anchor->GetType (tmp);
					if (NS_SUCCEEDED(rv))
						SetStringProperty ("link_type", tmp);

					nsEmbedCString linkType;
					NS_UTF16ToCString (tmp, NS_CSTRING_ENCODING_UTF8, linkType);

					if (g_ascii_strcasecmp (linkType.get(), "text/smartbookmark") == 0)
					{
						SetIntProperty ("link_is_smart", TRUE);
						
						nsCOMPtr<nsIDOMNode> childNode;
						node->GetFirstChild (getter_AddRefs(childNode));
						if (childNode)
						{
							nsCOMPtr <nsIDOMHTMLImageElement> image = 
								do_QueryInterface(childNode, &rv);

							if (image)
							{
								nsEmbedString img;
								rv = image->GetSrc (img);
								if (!NS_FAILED(rv))
								{
									SetStringProperty ("image", img);
								}
							}
						}
					}
					else
					{
						SetIntProperty ("link_is_smart", FALSE);
					}
				}
			
			}
			else if (g_ascii_strcasecmp (tag.get(), "option") == 0)
			{
				info->context = EPHY_EMBED_CONTEXT_NONE;
				return NS_OK;
			}
			else if (g_ascii_strcasecmp (tag.get(), "area") == 0)
			{
				info->context |= EPHY_EMBED_CONTEXT_LINK;
				nsCOMPtr <nsIDOMHTMLAreaElement> area =
						do_QueryInterface(node, &rv);
				if (NS_SUCCEEDED(rv) && area)
				{
					nsEmbedString href;
					rv = area->GetHref (href);
					if (NS_FAILED(rv))
						return NS_ERROR_FAILURE;
					
					SetURIProperty (node, "link", href);
					CheckLinkScheme (href);
				}
			}
			else if (g_ascii_strcasecmp (tag.get(), "input") == 0)
			{
				CheckInput (node);
			}
			else if (g_ascii_strcasecmp (tag.get(), "textarea") == 0)
			{
				info->context |= EPHY_EMBED_CONTEXT_INPUT;
			}

			if (!has_background)
			{
				nsEmbedString cssurl;
				rv = GetCSSBackground (node, cssurl);
				if (NS_SUCCEEDED (rv))
				{
					nsEmbedCString bgimg;

                                        rv = ResolveBaseURL (cssurl, bgimg);
                                        if (NS_FAILED (rv))
                                                return NS_ERROR_FAILURE;
					SetStringProperty ("background_image",
						           bgimg.get());

					has_background = PR_TRUE;
				}
			}
		}
		
		nsCOMPtr<nsIDOMNode> parentNode;
		node->GetParentNode (getter_AddRefs(parentNode));
		node = parentNode;
	}
	
	return NS_OK;
}

nsresult EventContext::GetCSSBackground (nsIDOMNode *node, nsAString& url)
{
	if (!mViewCSS) return NS_ERROR_NOT_INITIALIZED;

	nsresult rv;

	const PRUnichar bgimage[] = {'b', 'a', 'c', 'k', 'g', 'r', 'o', 'u', 'n', 'd',
				     '-', 'i', 'm', 'a', 'g', 'e', '\0'};

	nsCOMPtr<nsIDOMElement> element = do_QueryInterface (node);
	NS_ENSURE_TRUE (element, NS_ERROR_FAILURE);

	nsCOMPtr<nsIDOMCSSStyleDeclaration> decl;
	mViewCSS->GetComputedStyle (element, nsEmbedString(),
				    getter_AddRefs (decl));
	NS_ENSURE_TRUE (decl, NS_ERROR_FAILURE);

	nsCOMPtr<nsIDOMCSSValue> CSSValue;
	decl->GetPropertyCSSValue (nsEmbedString(bgimage),
				   getter_AddRefs (CSSValue));

	nsCOMPtr<nsIDOMCSSPrimitiveValue> primitiveValue = 
		do_QueryInterface (CSSValue);
	if (!primitiveValue) return NS_ERROR_FAILURE;
	
	PRUint16 type;
	rv = primitiveValue->GetPrimitiveType (&type);
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

	if (type != nsIDOMCSSPrimitiveValue::CSS_URI) return NS_ERROR_FAILURE;

	rv = primitiveValue->GetStringValue (url);
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

	return NS_OK;
}

nsresult EventContext::GetTargetCoords (nsIDOMEventTarget *aTarget, PRInt32 *aX, PRInt32 *aY)
{
	/* Calculate the node coordinates relative to the widget origin */
	nsCOMPtr<nsIDOMNSHTMLElement> elem (do_QueryInterface(aTarget));

	PRInt32 x = 0, y = 0;
	while (elem)
	{
		PRInt32 val;
		elem->GetOffsetTop(&val);	y += val;
		elem->GetScrollTop(&val);	y -= val;
		elem->GetOffsetLeft(&val);	x += val;
		elem->GetScrollLeft(&val);	x -= val;

		nsCOMPtr<nsIDOMElement> parent;
		elem->GetOffsetParent (getter_AddRefs (parent));
		elem = do_QueryInterface(parent);
	}

	*aX = x;
	*aY = y;

	return NS_OK;
}

nsresult EventContext::GetMouseEventInfo (nsIDOMMouseEvent *aMouseEvent, MozillaEmbedEvent *info)
{
	/* FIXME: casting 32-bit guint* to PRUint16* below will break on big-endian */
	PRUint16 btn = 1729;
	aMouseEvent->GetButton (&btn);

	switch (btn)
	{
		/* mozilla's button counting is one-off from gtk+'s */
		case 0:
			info->button = 1;
			break;
		case 1:
			info->button = 2;
			break;
		case 2:
			info->button = 3;
			break;

#ifndef HAVE_GECKO_1_8
		case 1729:
			/* This only appears to happen when getting a mouse context menu
			 * signal, so map it to button 3 (right mouse button)
			 * http://bugzilla.mozilla.org/show_bug.cgi?id=258193 
			 * Fixed since 1.8a4
			 */
			info->button = 3;
			break;
#endif

		case (PRUint16) -1:
			/* when the user submits a form with Return, mozilla synthesises
			 * a _mouse_ click event with btn=65535 (-1).
			 */
		default:
			info->button = 0;
			break;
	}

	if (info->button != 0)
	{
		/* OTOH, casting only between (un)signedness is safe */
		aMouseEvent->GetScreenX ((PRInt32*)&info->x);
		aMouseEvent->GetScreenY ((PRInt32*)&info->y);
	}
	else /* this is really a keyboard event */
	{
		nsCOMPtr<nsIDOMEventTarget> eventTarget;
		aMouseEvent->GetTarget (getter_AddRefs (eventTarget));

		GetTargetCoords (eventTarget, (PRInt32*)&info->x, (PRInt32*)&info->y);
	}

	/* be sure we are not clicking on the scroolbars */

	nsCOMPtr<nsIDOMNSEvent> nsEvent = do_QueryInterface(aMouseEvent);
	if (!nsEvent) return NS_ERROR_FAILURE;

#ifdef MOZ_NSIDOMNSEVENT_GETISTRUSTED
	/* make sure the event is trusted */
	PRBool isTrusted = PR_FALSE;
	nsEvent->GetIsTrusted (&isTrusted);
	if (!isTrusted) return NS_ERROR_UNEXPECTED;
#endif /* MOZ_NSIDOMNSEVENT_GETISTRUSTED */

	nsresult rv;
	nsCOMPtr<nsIDOMEventTarget> OriginalTarget;
	rv = nsEvent->GetOriginalTarget(getter_AddRefs(OriginalTarget));
	if (NS_FAILED (rv) || !OriginalTarget) return NS_ERROR_FAILURE;

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

	nsEmbedString nodename;
	OriginalNode->GetNodeName(nodename);
	nsEmbedCString cNodeName;
	NS_UTF16ToCString (nodename, NS_CSTRING_ENCODING_UTF8, cNodeName);

	if (g_ascii_strcasecmp (cNodeName.get(), "xul:scrollbarbutton") == 0 ||
	    g_ascii_strcasecmp (cNodeName.get(), "xul:thumb") == 0 ||
	    g_ascii_strcasecmp (cNodeName.get(), "xul:vbox") == 0 ||
	    g_ascii_strcasecmp (cNodeName.get(), "xul:spacer") == 0 ||
	    g_ascii_strcasecmp (cNodeName.get(), "xul:slider") == 0)
		return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMEventTarget> EventTarget;
	rv = aMouseEvent->GetTarget(getter_AddRefs(EventTarget));
	if (NS_FAILED (rv) || !EventTarget) return NS_ERROR_FAILURE;

	rv = GetEventContext (EventTarget, info);
	if (NS_FAILED (rv)) return rv;

	/* Get the modifier */

	PRBool mod_key;

	info->modifier = 0;

	aMouseEvent->GetAltKey(&mod_key);
	if (mod_key) info->modifier |= GDK_MOD1_MASK;

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

	/* no need to check GetMetaKey, it's always PR_FALSE,
	 * see widget/src/gtk2/nsWindow.cpp:InitMouseEvent
	 */
	
	aMouseEvent->GetCtrlKey(&mod_key);
	if (mod_key) info->modifier |= GDK_CONTROL_MASK;

	return NS_OK;
}

nsresult EventContext::GetKeyEventInfo (nsIDOMKeyEvent *aKeyEvent, MozillaEmbedEvent *info)
{
#ifdef MOZ_NSIDOMNSEVENT_GETISTRUSTED
	/* make sure the event is trusted */
	nsCOMPtr<nsIDOMNSEvent> nsEvent (do_QueryInterface (aKeyEvent));
	NS_ENSURE_TRUE (nsEvent, NS_ERROR_FAILURE);

	PRBool isTrusted = PR_FALSE;
	nsEvent->GetIsTrusted (&isTrusted);
	if (!isTrusted) return NS_ERROR_UNEXPECTED;
#endif /* MOZ_NSIDOMNSEVENT_GETISTRUSTED */


	info->button = 0;

	nsresult rv;
	PRUint32 keyCode;
	rv = aKeyEvent->GetKeyCode(&keyCode);
	if (NS_FAILED(rv)) return rv;
	info->keycode = keyCode;

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

	GetTargetCoords (target, (PRInt32*)&info->x, (PRInt32*)&info->y);

	/* Context */
	rv = GetEventContext (target, info);
	if (NS_FAILED(rv)) return rv;

	/* Get the modifier */

	PRBool mod_key;

	info->modifier = 0;

	aKeyEvent->GetAltKey(&mod_key);
	if (mod_key) info->modifier |= GDK_MOD1_MASK;

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

	aKeyEvent->GetMetaKey(&mod_key);
	if (mod_key) info->modifier |= GDK_MOD2_MASK;
	
	aKeyEvent->GetCtrlKey(&mod_key);
	if (mod_key) info->modifier |= GDK_CONTROL_MASK;

	return NS_OK;
}

nsresult EventContext::IsPageFramed (nsIDOMNode *node, PRBool *Framed)
{
	nsresult rv;

	nsCOMPtr<nsIDOMDocument> mainDocument;
	rv = mBrowser->GetDocument (getter_AddRefs(mainDocument));
	if (NS_FAILED (rv) || !mainDocument) return NS_ERROR_FAILURE;
	
	nsCOMPtr<nsIDOMDocument> nodeDocument;
	rv = node->GetOwnerDocument (getter_AddRefs(nodeDocument));
	if (NS_FAILED (rv) || !nodeDocument) return NS_ERROR_FAILURE;
 
	*Framed = (mainDocument != nodeDocument);

        return NS_OK;
}

nsresult EventContext::GetTargetDocument (nsIDOMDocument **domDoc)
{
	if (!mDOMDocument) return NS_ERROR_FAILURE;

	*domDoc = mDOMDocument.get();

	NS_IF_ADDREF(*domDoc);

	return NS_OK;
}

nsresult EventContext::CheckInput (nsIDOMNode *aNode)
{
	const PRUnichar typeLiteral[] = { 't', 'y', 'p', 'e', '\0' };

	nsCOMPtr<nsIDOMElement> element;
	element = do_QueryInterface (aNode);
	if (!element) return NS_ERROR_FAILURE;

	nsEmbedString uValue;
	element->GetAttribute (nsEmbedString(typeLiteral), uValue);

	nsEmbedCString value;
	NS_UTF16ToCString (uValue, NS_CSTRING_ENCODING_UTF8, value);

	if (g_ascii_strcasecmp (value.get(), "image") == 0)
	{
		mEmbedEvent->context |= EPHY_EMBED_CONTEXT_IMAGE;
		nsCOMPtr<nsIDOMHTMLInputElement> input;
		input = do_QueryInterface (aNode);
		if (!input) return NS_ERROR_FAILURE;

		nsresult rv;
		nsEmbedString img;
		rv = input->GetSrc (img);
		if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

		nsEmbedCString cImg;
		rv = ResolveBaseURL (img, cImg);
		if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
		SetStringProperty ("image", cImg.get());
	}
	else if (g_ascii_strcasecmp (value.get(), "radio") != 0 &&
		 g_ascii_strcasecmp (value.get(), "submit") != 0 &&
		 g_ascii_strcasecmp (value.get(), "reset") != 0 &&
		 g_ascii_strcasecmp (value.get(), "hidden") != 0 &&
		 g_ascii_strcasecmp (value.get(), "button") != 0 &&
		 g_ascii_strcasecmp (value.get(), "checkbox") != 0)
	{
		mEmbedEvent->context |= EPHY_EMBED_CONTEXT_INPUT;

		if (g_ascii_strcasecmp (value.get(), "password") == 0)
		{
			mEmbedEvent->context |= EPHY_EMBED_CONTEXT_INPUT_PASSWORD;
		}
	}

	return NS_OK;
}

nsresult EventContext::CheckLinkScheme (const nsAString &link)
{
	nsCOMPtr<nsIURI> uri;
	EphyUtils::NewURI (getter_AddRefs (uri), link);
	if (!uri) return NS_ERROR_FAILURE;

	nsresult rv;
	nsEmbedCString scheme;
	rv = uri->GetScheme (scheme);
	if (NS_FAILED (rv)) return NS_ERROR_FAILURE;

	if (g_ascii_strcasecmp (scheme.get(), "http") == 0 ||
	    g_ascii_strcasecmp (scheme.get(), "https") == 0 ||
	    g_ascii_strcasecmp (scheme.get(), "ftp") == 0 ||
	    g_ascii_strcasecmp (scheme.get(), "file") == 0 ||
	    g_ascii_strcasecmp (scheme.get(), "data") == 0 ||
	    g_ascii_strcasecmp (scheme.get(), "resource") == 0 ||
	    g_ascii_strcasecmp (scheme.get(), "about") == 0 ||
	    g_ascii_strcasecmp (scheme.get(), "gopher") == 0)
	{
		SetIntProperty ("link-has-web-scheme", TRUE);
	}

	return NS_OK;
}

nsresult EventContext::SetIntProperty (const char *name, int value)
{

	GValue *val = g_new0 (GValue, 1);

	g_value_init (val, G_TYPE_INT);
	
	g_value_set_int (val, value);

	mozilla_embed_event_set_property (mEmbedEvent, name, val);

	return NS_OK;
}

nsresult EventContext::SetStringProperty (const char *name, const char *value)
{
	GValue *val = g_new0 (GValue, 1);

	g_value_init (val, G_TYPE_STRING);
	
	g_value_set_string (val, value);
			 
	mozilla_embed_event_set_property (mEmbedEvent, name, val);

	return NS_OK;
}

nsresult EventContext::SetStringProperty (const char *name, const nsAString &value)
{
	nsEmbedCString cValue;
	NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cValue);
	return SetStringProperty (name, cValue.get());
}

nsresult EventContext::SetURIProperty (nsIDOMNode *node, const char *name, const nsACString &value)
{
	nsresult rv;
	nsCOMPtr<nsIURI> uri;
	rv = EphyUtils::NewURI (getter_AddRefs (uri), value, mCharset.Length () ? mCharset.get() : nsnull, mBaseURI);
	if (NS_SUCCEEDED (rv) && uri)
	{
		/* Hide password part */
		nsEmbedCString user;
		uri->GetUsername (user);
		uri->SetUserPass (user);

		nsEmbedCString spec;
		uri->GetSpec (spec);
		rv = SetStringProperty (name, spec.get());
	}
	else
	{
		rv = SetStringProperty (name, nsEmbedCString(value).get());
	}

	return rv;
}

nsresult EventContext::SetURIProperty (nsIDOMNode *node, const char *name, const nsAString &value)
{
	nsEmbedCString cValue;
	NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cValue);
	return SetURIProperty (node, name, cValue);
}

/* static */
PRBool
EventContext::CheckKeyPress (nsIDOMKeyEvent *aEvent)
{
	PRBool retval = PR_FALSE;

	/* make sure the event is trusted */
	nsCOMPtr<nsIDOMNSEvent> nsEvent (do_QueryInterface (aEvent));
	NS_ENSURE_TRUE (nsEvent, retval);
	PRBool isTrusted = PR_FALSE;
	nsEvent->GetIsTrusted (&isTrusted);
	if (!isTrusted) return retval;

	/* check for alt/ctrl */
	PRBool isCtrl = PR_FALSE, isAlt = PR_FALSE;
	aEvent->GetCtrlKey (&isCtrl);
	aEvent->GetAltKey (&isAlt);
	if (isCtrl || isAlt) return retval;

	nsCOMPtr<nsIDOMNSUIEvent> uiEvent (do_QueryInterface (aEvent));
	NS_ENSURE_TRUE (uiEvent, retval);

	/* check for already handled event */
	PRBool isPrevented = PR_FALSE;
	uiEvent->GetPreventDefault (&isPrevented);
	if (isPrevented) return retval;

	/* check for form controls */
	nsresult rv;
	nsCOMPtr<nsIDOMEventTarget> target;
	rv = aEvent->GetTarget (getter_AddRefs (target));
	NS_ENSURE_SUCCESS (rv, retval);

	nsCOMPtr<nsIDOMHTMLElement> element (do_QueryInterface (target, &rv));
	NS_ENSURE_SUCCESS (rv, retval);

	nsCOMPtr<nsIDOMHTMLInputElement> inputElement (do_QueryInterface (element));
	if (inputElement)
	{
		nsEmbedString type;
		inputElement->GetType (type);

		nsEmbedCString (cType);
		NS_UTF16ToCString (type, NS_CSTRING_ENCODING_UTF8, cType);

		if (g_ascii_strcasecmp (cType.get(), "text") == 0 ||
		    g_ascii_strcasecmp (cType.get(), "password") == 0 ||
		    g_ascii_strcasecmp (cType.get(), "file") == 0) return retval;
	}

	nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea;
	nsCOMPtr<nsIDOMHTMLSelectElement> selectElement;
	nsCOMPtr<nsIDOMHTMLIsIndexElement> indexElement;

	if ((textArea = do_QueryInterface (element)) ||
	    (selectElement = do_QueryInterface (element)) ||
	    (indexElement = do_QueryInterface (element))) return retval;

	/* check for design mode */
	nsCOMPtr<nsIDOMDocument> doc;
	rv = element->GetOwnerDocument (getter_AddRefs (doc));
	NS_ENSURE_SUCCESS (rv, retval);

	nsCOMPtr<nsIDOMNSHTMLDocument> htmlDoc (do_QueryInterface (doc, &rv));
	/* FIXME: return PR_TRUE here ? */
	if (NS_FAILED (rv)) return retval; /* it's okay not to be a HTML document */

	nsEmbedString uDesign;
	rv = htmlDoc->GetDesignMode (uDesign);
	NS_ENSURE_SUCCESS (rv, retval);

	nsEmbedCString design;
	NS_UTF16ToCString (uDesign, NS_CSTRING_ENCODING_UTF8, design);

	retval = g_ascii_strcasecmp (design.get(), "on") != 0;

	return retval;
}