/*
* 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>
#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;
/* 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);
PRUint16 type = 0;
element->GetNodeType(&type);
if (nsIDOMNode::ELEMENT_NODE != type) return retval;
nsEmbedString uTag;
rv = element->GetLocalName(uTag);
NS_ENSURE_SUCCESS (rv, retval);
nsEmbedCString tag;
NS_UTF16ToCString (uTag, NS_CSTRING_ENCODING_UTF8, tag);
if (g_ascii_strcasecmp (tag.get(), "input") == 0 ||
g_ascii_strcasecmp (tag.get(), "textarea") == 0 ||
g_ascii_strcasecmp (tag.get(), "select") == 0 ||
g_ascii_strcasecmp (tag.get(), "button") == 0 ||
g_ascii_strcasecmp (tag.get(), "isindex") == 0) 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));
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;
}