/*
 *  Copyright © 2000-2004 Marco Pesenti Gritti
 *  Copyright © 2003, 2004, 2005 Christian Persch
 *
 *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *  $Id: EphyBrowser.cpp 7626 2007-11-05 19:54:41Z xan $
 */

#include "mozilla-config.h"
#include "config.h"

#include <unistd.h>

#include <nsStringAPI.h>

#include <gtkmozembed.h>
#include <gtkmozembed_internal.h>
#include <nsIChannel.h>
#include <nsICommandManager.h>
#include <nsIContentViewer.h>
#include <nsIDocCharset.h>
#include <nsIDocShellTreeItem.h>
#include <nsIDocShellTreeNode.h>
#include <nsIDocShellTreeOwner.h>
#include <nsIDOM3Document.h>
#include <nsIDOMAbstractView.h>
#include <nsIDOMAbstractView.h>
#include <nsIDOMCSSPrimitiveValue.h>
#include <nsIDOMCSSStyleDeclaration.h>
#include <nsIDOMCSSStyleDeclaration.h>
#include <nsIDOMCSSValue.h>
#include <nsIDOMDocument.h>
#include <nsIDOMDocumentView.h>
#include <nsIDOMDocumentView.h>
#include <nsIDOMElement.h>
#include <nsIDOMEvent.h>
#include <nsIDOMEventTarget.h>
#include <nsIDOMHTMLCollection.h>
#include <nsIDOMHTMLDocument.h>
#include <nsIDOMHTMLDocument.h>
#include <nsIDOMHTMLElement.h>
#include <nsIDOMHTMLFormElement.h>
#include <nsIDOMHTMLInputElement.h>
#include <nsIDOMHTMLTextAreaElement.h>
#include <nsIDOMKeyEvent.h>
#include <nsIDOMMouseEvent.h>
#include <nsIDOMNode.h>
#include <nsIDOMNSEvent.h>
#include <nsIDOMNSEventTarget.h>
#include <nsIDOMPopupBlockedEvent.h>
#include <nsIDOMViewCSS.h>
#include <nsIDOMWindow2.h>
#include <nsIDOMXMLDocument.h>
#include <nsIHistoryEntry.h>
#include <nsIInterfaceRequestor.h>
#include <nsIInterfaceRequestorUtils.h>
#include <nsIPrintSettings.h>
#include <nsIPrintSettingsService.h>
#include <nsIScriptSecurityManager.h>
#include <nsIServiceManager.h>
#include <nsISHEntry.h>
#include <nsISHistory.h>
#include <nsISHistoryInternal.h>
#include <nsISimpleEnumerator.h>
#include <nsIURI.h>
#include <nsIWebBrowserFocus.h>
#include <nsIWebBrowserPrint.h>
#include <nsIWebPageDescriptor.h>
#include <nsMemory.h>
#include <nsServiceManagerUtils.h>

#ifdef HAVE_MOZILLA_PSM
#include <nsICertificateDialogs.h>
#include <nsISSLStatus.h>
#include <nsISSLStatusProvider.h>
#include <nsITransportSecurityInfo.h>
#include <nsIX509Cert.h>
#endif

#ifdef ALLOW_PRIVATE_API
#include <nsIContentPolicy.h>
#include <nsIDocShell.h>
#include <nsIDOMWindowInternal.h>
#include <nsIImageDocument.h>
#include <nsIMarkupDocumentViewer.h>
#endif

#include "ephy-debug.h"
#include "ephy-embed.h"
#include "ephy-embed-shell.h"
#include "ephy-string.h"
#include "ephy-zoom.h"
#include "mozilla-embed-event.h"
#include "mozilla-embed.h"

#include "AutoJSContextStack.h"
#include "EphyUtils.h"
#include "EventContext.h"
#include "GeckoPrintService.h"

#include "EphyBrowser.h"

NS_IMPL_ISUPPORTS1(EphyEventListener, nsIDOMEventListener)

NS_IMETHODIMP
EphyDOMLinkEventListener::HandleEvent (nsIDOMEvent* aDOMEvent)
{
	nsCOMPtr<nsIDOMEventTarget> eventTarget;
	aDOMEvent->GetTarget(getter_AddRefs(eventTarget));

	nsCOMPtr<nsIDOMElement> linkElement (do_QueryInterface (eventTarget));
	if (!linkElement) return NS_ERROR_FAILURE;

	nsresult rv;
	nsString value;
	rv = linkElement->GetAttribute (NS_LITERAL_STRING ("rel"), value);
	if (NS_FAILED (rv)) return NS_ERROR_FAILURE;

	nsCString rel;
	NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, rel);	

	if (g_ascii_strcasecmp (rel.get(), "SHORTCUT ICON") == 0 ||
	    g_ascii_strcasecmp (rel.get(), "ICON") == 0)
	{
		nsCOMPtr<nsIDOMDocument> domDoc;
		linkElement->GetOwnerDocument(getter_AddRefs(domDoc));
		NS_ENSURE_TRUE (domDoc, NS_ERROR_FAILURE);

		nsCOMPtr<nsIDOMDocumentView> docView (do_QueryInterface (domDoc));
		NS_ENSURE_TRUE (docView, NS_ERROR_FAILURE);

		nsCOMPtr<nsIDOMAbstractView> abstractView;
		docView->GetDefaultView (getter_AddRefs (abstractView));

		nsCOMPtr<nsIDOMWindow> domWin (do_QueryInterface (abstractView));
		NS_ENSURE_TRUE (domWin, NS_ERROR_FAILURE);

		nsCOMPtr<nsIDOMWindow> topDomWin;
		domWin->GetTop (getter_AddRefs (topDomWin));

		nsCOMPtr<nsISupports> domWinAsISupports (do_QueryInterface (domWin));
		nsCOMPtr<nsISupports> topDomWinAsISupports (do_QueryInterface (topDomWin));
		/* disallow subframes to set favicon */
		if (domWinAsISupports != topDomWinAsISupports) return NS_OK;

		nsCOMPtr<nsIURI> docUri;
		rv = GetDocURI (linkElement, getter_AddRefs (docUri));
		NS_ENSURE_TRUE (NS_SUCCEEDED (rv) && docUri, NS_ERROR_FAILURE);

		rv = linkElement->GetAttribute (NS_LITERAL_STRING ("href"), value);
		if (NS_FAILED (rv) || !value.Length()) return NS_ERROR_FAILURE;

		nsCString cLink;
		NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cLink);

		nsCString faviconUrl;
		rv = docUri->Resolve (cLink, faviconUrl);
		NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

		nsCOMPtr<nsIURI> favUri;
		EphyUtils::NewURI (getter_AddRefs (favUri), faviconUrl);
		NS_ENSURE_TRUE (favUri, NS_ERROR_FAILURE);

		/* Only proceed for http favicons. Bug #312291 */
		PRBool isHttp = PR_FALSE, isHttps = PR_FALSE;
		favUri->SchemeIs ("http", &isHttp);
		favUri->SchemeIs ("https", &isHttps);
		if (!isHttp && !isHttps) return NS_OK;

		/* check if load is allowed */
		nsCOMPtr<nsIScriptSecurityManager> secMan
			(do_GetService("@mozilla.org/scriptsecuritymanager;1"));
		/* refuse if we can't check */
		NS_ENSURE_TRUE (secMan, NS_OK);

		rv = secMan->CheckLoadURI(docUri, favUri,
					  nsIScriptSecurityManager::STANDARD);
		/* failure means it didn't pass the security check */
		if (NS_FAILED (rv)) return NS_OK;

		/* security check passed, now check with content policy */
		nsCOMPtr<nsIContentPolicy> policy =
			do_GetService("@mozilla.org/layout/content-policy;1");
		/* refuse if we can't check */
		NS_ENSURE_TRUE (policy, NS_OK);

		linkElement->GetAttribute (NS_LITERAL_STRING ("type"), value);

		nsCString cTypeVal;
		NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cTypeVal);

		PRInt16 decision = 0;
		rv = policy->ShouldLoad (nsIContentPolicy::TYPE_IMAGE,
					 favUri, docUri, eventTarget,
					 cTypeVal, nsnull,
					 &decision);
		NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);
		if (decision != nsIContentPolicy::ACCEPT) return NS_OK;
		
		/* Hide password part */
		nsCString user;
		favUri->GetUsername (user);
		favUri->SetUserPass (user);

		nsCString spec;
		favUri->GetSpec (spec);

		/* ok, we accept this as a valid favicon for this site */
		g_signal_emit_by_name (mOwner->mEmbed, "ge_favicon", spec.get());
	}
	else if (g_ascii_strcasecmp (rel.get (), "search") == 0)
	{
		linkElement->GetAttribute (NS_LITERAL_STRING ("type"), value);

		nsCString cTypeVal;
		NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cTypeVal);

		if (g_ascii_strcasecmp (cTypeVal.get (),
		    "application/opensearchdescription+xml") == 0)
		{
			rv = linkElement->GetAttribute (NS_LITERAL_STRING ("href"), value);
			if (NS_FAILED (rv) || !value.Length()) return NS_ERROR_FAILURE;

			nsCString cLink;
			NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cLink);

			nsCOMPtr<nsIURI> docUri;
			rv = GetDocURI (linkElement, getter_AddRefs (docUri));
			NS_ENSURE_TRUE (NS_SUCCEEDED (rv) && docUri, NS_ERROR_FAILURE);

			nsCString resolvedLink;
			rv = docUri->Resolve (cLink, resolvedLink);
			NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

			linkElement->GetAttribute (NS_LITERAL_STRING ("title"), value);

			nsCString cTitle;
			NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cTitle);

			g_signal_emit_by_name (mOwner->mEmbed, "ge_search_link",
					       cTypeVal.get(), cTitle.get(), resolvedLink.get());
		}
	}
	else if (g_ascii_strcasecmp (rel.get (), "alternate") == 0)
	{
		linkElement->GetAttribute (NS_LITERAL_STRING ("type"), value);

		nsCString cTypeVal;
		NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cTypeVal);

		if (g_ascii_strcasecmp (cTypeVal.get (), "application/rss+xml") == 0 ||
		    g_ascii_strcasecmp (cTypeVal.get (), "application/atom+xml") == 0)
		{
			rv = linkElement->GetAttribute (NS_LITERAL_STRING ("href"), value);
			if (NS_FAILED (rv) || !value.Length()) return NS_ERROR_FAILURE;

			nsCString cLink;
			NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cLink);

			nsCOMPtr<nsIURI> docUri;
			rv = GetDocURI (linkElement, getter_AddRefs (docUri));
			NS_ENSURE_TRUE (NS_SUCCEEDED (rv) && docUri, NS_ERROR_FAILURE);

			/* Hide password part */
			nsCString user;
			docUri->GetUsername (user);
			docUri->SetUserPass (user);

			nsCString resolvedLink;
			rv = docUri->Resolve (cLink, resolvedLink);
			NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

			linkElement->GetAttribute (NS_LITERAL_STRING ("title"), value);

			nsCString cTitle;
			NS_UTF16ToCString (value, NS_CSTRING_ENCODING_UTF8, cTitle);

			g_signal_emit_by_name (mOwner->mEmbed, "ge_feed_link",
					       cTypeVal.get(), cTitle.get(), resolvedLink.get());
		}
	}

	return NS_OK;
}

NS_IMETHODIMP
EphyMiscDOMEventsListener::HandleEvent (nsIDOMEvent* aDOMEvent)
{
	/* make sure the event is trusted */
	nsCOMPtr<nsIDOMNSEvent> nsEvent (do_QueryInterface (aDOMEvent));
	NS_ENSURE_TRUE (nsEvent, NS_ERROR_FAILURE);
	PRBool isTrusted = PR_FALSE;
	nsEvent->GetIsTrusted (&isTrusted);
	if (!isTrusted) return NS_OK;

	nsresult rv;
	nsString type;
	rv = aDOMEvent->GetType (type);
	NS_ENSURE_SUCCESS (rv, rv);

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

	if (g_ascii_strcasecmp (cType.get(), "DOMContentLoaded") == 0)
	{
		g_signal_emit_by_name (mOwner->mEmbed, "dom_content_loaded",
				       (gpointer)aDOMEvent);
	}
	else if (g_ascii_strcasecmp (cType.get(), "DOMWindowClose") == 0)
	{
		gboolean prevent = FALSE;

		g_signal_emit_by_name (mOwner->mEmbed, "close-request", &prevent);

		if (prevent)
		{
			aDOMEvent->PreventDefault ();
		}
	}

	return NS_OK;
}

nsresult
EphyDOMLinkEventListener::GetDocURI (nsIDOMElement *aElement,
				     nsIURI **aDocURI)
{
	nsCOMPtr<nsIDOMDocument> domDoc;
	aElement->GetOwnerDocument (getter_AddRefs(domDoc));

	nsCOMPtr<nsIDOM3Document> doc (do_QueryInterface (domDoc));
	NS_ENSURE_TRUE (doc, NS_ERROR_FAILURE);

	nsresult rv;
	nsString spec;
	rv = doc->GetDocumentURI (spec);
	NS_ENSURE_SUCCESS (rv, rv);

	nsCString encoding;
	rv = mOwner->GetEncoding (encoding);
	NS_ENSURE_SUCCESS (rv, rv);

	return EphyUtils::NewURI (aDocURI, spec, encoding.get());
}

NS_IMETHODIMP
EphyPopupBlockEventListener::HandleEvent (nsIDOMEvent * aDOMEvent)
{
	nsCOMPtr<nsIDOMPopupBlockedEvent> popupEvent =
		do_QueryInterface (aDOMEvent);
	NS_ENSURE_TRUE (popupEvent, NS_ERROR_FAILURE);

	nsCOMPtr<nsIURI> popupWindowURI;
	popupEvent->GetPopupWindowURI (getter_AddRefs (popupWindowURI));
	
	nsCString popupWindowURIString;
	nsresult rv;

	if (popupWindowURI)
	{
		rv = popupWindowURI->GetSpec (popupWindowURIString);
		NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);
	}

	nsString popupWindowFeatures;
	rv = popupEvent->GetPopupWindowFeatures (popupWindowFeatures);
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

	nsCString popupWindowFeaturesString;
	NS_UTF16ToCString (popupWindowFeatures,
			   NS_CSTRING_ENCODING_UTF8,
			   popupWindowFeaturesString);

	nsCString popupWindowNameString;
#ifdef HAVE_GECKO_1_9
	nsString popupWindowName;
	rv = popupEvent->GetPopupWindowName (popupWindowName);
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

	NS_UTF16ToCString (popupWindowName,
			   NS_CSTRING_ENCODING_UTF8,
			   popupWindowNameString);
#endif

	g_signal_emit_by_name(mOwner->mEmbed, "ge-popup-blocked",
			      popupWindowURI == NULL ? NULL : popupWindowURIString.get(),
			      popupWindowNameString.get(),
			      popupWindowFeaturesString.get());

	return NS_OK;
}

NS_IMETHODIMP
EphyModalAlertEventListener::HandleEvent (nsIDOMEvent * aDOMEvent)
{
	NS_ENSURE_TRUE (mOwner, NS_ERROR_FAILURE);

	nsresult rv;
	nsString type;
	rv = aDOMEvent->GetType (type);
	NS_ENSURE_SUCCESS (rv, rv);

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

	LOG ("ModalAlertListener event %s", cType.get());

	if (strcmp (cType.get(), "DOMWillOpenModalDialog") == 0)
	{
		gboolean retval = FALSE;
		g_signal_emit_by_name (mOwner->mEmbed, "ge-modal-alert", &retval);

		/* suppress alert */
		if (retval)
		{
			aDOMEvent->PreventDefault ();
			aDOMEvent->StopPropagation();
		}
	}
	else if (strcmp (cType.get(), "DOMModalDialogClosed") == 0)
	{
		g_signal_emit_by_name (mOwner->mEmbed, "ge-modal-alert-closed");
	}

	return NS_OK;
}

NS_IMETHODIMP
EphyDOMScrollEventListener::HandleEvent (nsIDOMEvent * aEvent)
{
	nsresult rv;
	nsCOMPtr<nsIDOMMouseEvent> mouseEvent (do_QueryInterface (aEvent, &rv));
	NS_ENSURE_SUCCESS (rv, rv);
		
	PRBool isAlt = PR_FALSE, isControl = PR_FALSE, isShift = PR_FALSE;
	mouseEvent->GetAltKey (&isAlt);
	mouseEvent->GetCtrlKey (&isControl);
	mouseEvent->GetShiftKey (&isShift);
	/* GetMetaKey is always false on gtk2 mozilla */

	if (isControl && !isAlt && !isShift)
	{
		PRInt32 detail = 0;
		mouseEvent->GetDetail(&detail);

		float zoom;
		rv = mOwner->GetZoom (&zoom);
		NS_ENSURE_SUCCESS (rv, rv);

		zoom = ephy_zoom_get_changed_zoom_level (zoom, detail > 0 ? 1 : detail < 0 ? -1 : 0);
		rv = mOwner->SetZoom (zoom);
		if (NS_SUCCEEDED (rv))
		{
			g_signal_emit_by_name (mOwner->mEmbed, "ge_zoom_change", zoom);
		}

		/* we consumed the event */
		aEvent->PreventDefault();	 
	}
	 
	return NS_OK;
}

NS_IMPL_ISUPPORTS1(EphyContextMenuListener, nsIDOMContextMenuListener)

NS_IMETHODIMP
EphyContextMenuListener::ContextMenu (nsIDOMEvent* aDOMEvent)
{
	nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aDOMEvent);
	NS_ENSURE_TRUE (mouseEvent, NS_ERROR_FAILURE);

	MozillaEmbedEvent *info;
	info = mozilla_embed_event_new (static_cast<gpointer>(aDOMEvent));

	nsresult rv;
	EventContext context;
	context.Init (mOwner);
        rv = context.GetMouseEventInfo (mouseEvent, MOZILLA_EMBED_EVENT (info));

	/* Don't do any magic handling if we can't actually show the context
	 * menu, this can happen for XUL pages (e.g. about:config)
	 */
	if (NS_FAILED (rv))
	{
		g_object_unref (info);
		return NS_OK;   
	}

	if (info->button == 0)
	{
		/* Translate relative coordinates to absolute values, and try
		 * to avoid covering links by adding a little offset
		 */
		int x, y;
		gdk_window_get_origin (GTK_WIDGET (mOwner->mEmbed)->window, &x, &y);
		info->x += x + 6;       
		info->y += y + 6;

		// Set the keycode to something sensible
		info->keycode = nsIDOMKeyEvent::DOM_VK_CONTEXT_MENU;
	}

	if (info->modifier == GDK_CONTROL_MASK)
	{
		info->context = EPHY_EMBED_CONTEXT_DOCUMENT;
	}

	gboolean retval = FALSE;
	nsCOMPtr<nsIDOMDocument> domDoc;
	rv = context.GetTargetDocument (getter_AddRefs(domDoc));
	if (NS_SUCCEEDED(rv))
	{
		mOwner->PushTargetDocument (domDoc);

		g_signal_emit_by_name (mOwner->mEmbed, "ge_context_menu",
				       info, &retval);

		mOwner->PopTargetDocument ();
	}

	/* We handled the event, block javascript calls */
	if (retval)
	{
		aDOMEvent->PreventDefault();
		aDOMEvent->StopPropagation();
	}
	
	g_object_unref (info);

	return NS_OK;
}

NS_IMETHODIMP
EphyContextMenuListener::HandleEvent (nsIDOMEvent* aDOMEvent)
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

EphyBrowser::EphyBrowser ()
: mDOMLinkEventListener(nsnull)
, mMiscDOMEventsListener(nsnull)
, mDOMScrollEventListener(nsnull)
, mPopupBlockEventListener(nsnull)
, mModalAlertListener(nsnull)
, mContextMenuListener(nsnull)
, mInitialized(PR_FALSE)
{
	LOG ("EphyBrowser ctor (%p)", this);
}

EphyBrowser::~EphyBrowser ()
{
	LOG ("EphyBrowser dtor (%p)", this);
}

nsresult EphyBrowser::Init (EphyEmbed *embed)
{
	if (mInitialized) return NS_OK;

	mEmbed = GTK_WIDGET (embed);

	gtk_moz_embed_get_nsIWebBrowser (GTK_MOZ_EMBED (gtk_bin_get_child (GTK_BIN (embed))),
					 getter_AddRefs(mWebBrowser));
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	mWebBrowserFocus = do_QueryInterface (mWebBrowser);
	NS_ENSURE_TRUE (mWebBrowserFocus, NS_ERROR_FAILURE);

	mWebBrowser->GetContentDOMWindow (getter_AddRefs (mDOMWindow));
	NS_ENSURE_TRUE (mDOMWindow, NS_ERROR_FAILURE);

	/* This will instantiate an about:blank doc if necessary */
	nsresult rv;
	nsCOMPtr<nsIDOMDocument> domDocument;
	rv = mDOMWindow->GetDocument (getter_AddRefs (domDocument));
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

	mDOMLinkEventListener = new EphyDOMLinkEventListener(this);
	if (!mDOMLinkEventListener) return NS_ERROR_OUT_OF_MEMORY;

	mMiscDOMEventsListener = new EphyMiscDOMEventsListener(this);
	if (!mMiscDOMEventsListener) return NS_ERROR_OUT_OF_MEMORY;

	mDOMScrollEventListener = new EphyDOMScrollEventListener(this);
	if (!mDOMScrollEventListener) return NS_ERROR_OUT_OF_MEMORY;

	mPopupBlockEventListener = new EphyPopupBlockEventListener(this);
	if (!mPopupBlockEventListener) return NS_ERROR_OUT_OF_MEMORY;

	mModalAlertListener = new EphyModalAlertEventListener (this);
	if (!mModalAlertListener) return NS_ERROR_OUT_OF_MEMORY;

	mContextMenuListener = new EphyContextMenuListener(this);
	if (!mContextMenuListener) return NS_ERROR_OUT_OF_MEMORY;

 	rv = GetListener();
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

	rv = AttachListeners();
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

#ifdef HAVE_MOZILLA_PSM
	nsCOMPtr<nsIDocShell> docShell (do_GetInterface (mWebBrowser, &rv));
	NS_ENSURE_SUCCESS (rv, rv);

	rv = docShell->GetSecurityUI (getter_AddRefs (mSecurityInfo));
	if (!mSecurityInfo)
	{
		g_warning ("Failed to get nsISecureBrowserUI!\n");
 	}
	NS_ENSURE_SUCCESS (rv, rv);
#endif /* HAVE_MOZILLA_PSM */

	mInitialized = PR_TRUE;

	return NS_OK;
}

nsresult
EphyBrowser::GetListener (void)
{
  	if (mEventTarget) return NS_ERROR_FAILURE;

  	nsCOMPtr<nsIDOMWindow> domWindowExternal;
  	mWebBrowser->GetContentDOMWindow (getter_AddRefs(domWindowExternal));
  
  	nsCOMPtr<nsIDOMWindow2> domWindow (do_QueryInterface (domWindowExternal));
	NS_ENSURE_TRUE (domWindow, NS_ERROR_FAILURE);
	
  	domWindow->GetWindowRoot (getter_AddRefs(mEventTarget));
	NS_ENSURE_TRUE (mEventTarget, NS_ERROR_FAILURE);

	return NS_OK;
}

nsresult
EphyBrowser::AttachListeners(void)
{
	NS_ENSURE_TRUE (mEventTarget, NS_ERROR_FAILURE);

	nsresult rv;
	nsCOMPtr<nsIDOMNSEventTarget> target (do_QueryInterface (mEventTarget, &rv));
	NS_ENSURE_SUCCESS (rv, rv);

	rv = target->AddEventListener(NS_LITERAL_STRING ("DOMLinkAdded"),
				      mDOMLinkEventListener, PR_FALSE, PR_FALSE);
	rv |= target->AddEventListener(NS_LITERAL_STRING ("DOMContentLoaded"),
				       mMiscDOMEventsListener, PR_FALSE, PR_FALSE);
	rv |= target->AddEventListener(NS_LITERAL_STRING ("DOMWindowClose"),
				       mMiscDOMEventsListener, PR_FALSE, PR_FALSE);
	rv |= target->AddEventListener(NS_LITERAL_STRING ("DOMMouseScroll"),
				       mDOMScrollEventListener, PR_TRUE /* capture */, PR_FALSE);
	rv |= target->AddEventListener(NS_LITERAL_STRING ("DOMPopupBlocked"),
				       mPopupBlockEventListener, PR_FALSE, PR_FALSE);
	rv |= target->AddEventListener(NS_LITERAL_STRING ("DOMWillOpenModalDialog"),
				       mModalAlertListener, PR_TRUE, PR_FALSE);
	rv |= target->AddEventListener(NS_LITERAL_STRING ("DOMModalDialogClosed"),
				       mModalAlertListener, PR_TRUE, PR_FALSE);
	rv |= target->AddEventListener(NS_LITERAL_STRING ("contextmenu"),
				       mContextMenuListener, PR_TRUE /* capture */, PR_FALSE);
	NS_ENSURE_SUCCESS (rv, rv);

	return NS_OK;
}

nsresult
EphyBrowser::DetachListeners(void)
{
	if (!mEventTarget) return NS_OK;

	nsresult rv;
	rv = mEventTarget->RemoveEventListener(NS_LITERAL_STRING ("DOMLinkAdded"),
					       mDOMLinkEventListener, PR_FALSE);
	rv |= mEventTarget->RemoveEventListener(NS_LITERAL_STRING ("DOMContentLoaded"),
					        mMiscDOMEventsListener, PR_FALSE);
	rv |= mEventTarget->RemoveEventListener(NS_LITERAL_STRING ("DOMWindowClose"),
						mMiscDOMEventsListener, PR_FALSE);
	rv |= mEventTarget->RemoveEventListener(NS_LITERAL_STRING ("DOMMouseScroll"),
						mDOMScrollEventListener, PR_TRUE); /* capture */
	rv |= mEventTarget->RemoveEventListener(NS_LITERAL_STRING ("DOMPopupBlocked"),
					        mPopupBlockEventListener, PR_FALSE);
	rv |= mEventTarget->RemoveEventListener(NS_LITERAL_STRING ("DOMWillOpenModalDialog"),
						mModalAlertListener, PR_TRUE);
	rv |= mEventTarget->RemoveEventListener(NS_LITERAL_STRING ("DOMModalDialogClosed"),
						mModalAlertListener, PR_TRUE);
	rv |= mEventTarget->RemoveEventListener(NS_LITERAL_STRING ("contextmenu"),
					        mContextMenuListener, PR_TRUE /* capture */);
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

	return NS_OK;
}

nsresult EphyBrowser::Print ()
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserPrint> print(do_GetInterface(mWebBrowser));
	NS_ENSURE_TRUE (print, NS_ERROR_FAILURE);

	nsCOMPtr<nsIPrintSettingsService> printSettingsService
		(do_GetService("@mozilla.org/gfx/printsettings-service;1"));
	NS_ENSURE_STATE (printSettingsService);

	nsCOMPtr<nsIPrintSettings> settings;
	printSettingsService->GetNewPrintSettings (getter_AddRefs (settings));
	NS_ENSURE_STATE (settings);

	nsresult rv;
	AutoJSContextStack stack;
	rv = stack.Init ();
	NS_ENSURE_SUCCESS (rv, rv);

	return  print->Print (settings, nsnull);
}

nsresult EphyBrowser::SetPrintPreviewMode (PRBool previewMode)
{
	nsresult rv;

	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserPrint> print (do_GetInterface (mWebBrowser));
	NS_ENSURE_STATE (print);

	if (previewMode)
	{
		nsCOMPtr<nsIPrintSettingsService> printSettingsService
			(do_GetService("@mozilla.org/gfx/printsettings-service;1"));
		NS_ENSURE_STATE (printSettingsService);

		nsCOMPtr<nsIPrintSettings> settings;
		printSettingsService->GetNewPrintSettings (getter_AddRefs (settings));
		NS_ENSURE_STATE (settings);

		EphyEmbedShell *shell = ephy_embed_shell_get_default ();
		rv = GeckoPrintService::TranslateSettings (ephy_embed_shell_get_print_settings (shell),
							   ephy_embed_shell_get_page_setup (shell),
							   NULL,
							   nsCString(),
                                                           nsIPrintSettings::kFramesAsIs,
							   PR_FALSE,
							   settings);

		if (NS_SUCCEEDED (rv))
		{
			rv = print->PrintPreview (settings, mDOMWindow, nsnull);
		}
	}
	else
	{
		PRBool isPreview = PR_FALSE;

		rv = print->GetDoingPrintPreview(&isPreview);
		NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

		if (isPreview)
		{
			rv = print->ExitPrintPreview();
		}
	}

	return rv;
}

nsresult EphyBrowser::PrintPreviewNumPages (int *numPages)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserPrint> print(do_GetInterface(mWebBrowser));
	NS_ENSURE_TRUE (print, NS_ERROR_FAILURE);

	return print->GetPrintPreviewNumPages(numPages);
}

nsresult EphyBrowser::PrintPreviewNavigate(PRInt16 navType, PRInt32 pageNum)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserPrint> print(do_GetInterface(mWebBrowser));
	NS_ENSURE_TRUE (print, NS_ERROR_FAILURE);

	return print->PrintPreviewNavigate(navType, pageNum);
}

nsresult EphyBrowser::GetSHistory (nsISHistory **aSHistory)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebNavigation> ContentNav = do_QueryInterface (mWebBrowser);
	NS_ENSURE_TRUE (ContentNav, NS_ERROR_FAILURE);

	nsCOMPtr<nsISHistory> SessionHistory;
	ContentNav->GetSessionHistory (getter_AddRefs (SessionHistory));
	NS_ENSURE_TRUE (SessionHistory, NS_ERROR_FAILURE);

	*aSHistory = SessionHistory.get();
	NS_IF_ADDREF (*aSHistory);

	return NS_OK;
}

nsresult EphyBrowser::CopySHistory (EphyBrowser *dest, PRBool copy_back,
	                            PRBool copy_forward, PRBool copy_current)
{
	nsresult rv;
	
	nsCOMPtr<nsISHistory> h_src;
	GetSHistory (getter_AddRefs(h_src));
	NS_ENSURE_TRUE (h_src, NS_ERROR_FAILURE);

	PRInt32 count, index;
	h_src->GetCount (&count);
	h_src->GetIndex (&index);

	nsCOMPtr<nsISHistory> h_dest;
	dest->GetSHistory (getter_AddRefs (h_dest));
	NS_ENSURE_TRUE (h_dest, NS_ERROR_FAILURE);

	nsCOMPtr<nsISHistoryInternal> hi_dest = do_QueryInterface (h_dest);
	NS_ENSURE_TRUE (hi_dest, NS_ERROR_FAILURE);

	if (count)
	{
		nsCOMPtr<nsIHistoryEntry> he;
		nsCOMPtr<nsISHEntry> she, dhe;

		for (PRInt32 i = (copy_back ? 0 : index + 1); 
		     i < (copy_forward ? count : index + 1);
		     i++) 
		{
			rv = h_src->GetEntryAtIndex (i, PR_FALSE,
						     getter_AddRefs (he));
			NS_ENSURE_SUCCESS (rv, rv);

			she = do_QueryInterface (he);
			NS_ENSURE_TRUE (she, NS_ERROR_FAILURE);
			
			rv = she->Clone(getter_AddRefs (dhe));
			NS_ENSURE_SUCCESS (rv, rv);

			rv = hi_dest->AddEntry (dhe, PR_TRUE);
			NS_ENSURE_SUCCESS (rv, rv);
		}
		
		if (copy_current)
		{
			nsCOMPtr<nsIWebNavigation> wn_dest = do_QueryInterface (dest->mWebBrowser);
			NS_ENSURE_TRUE (wn_dest, NS_ERROR_FAILURE);
			
			rv = wn_dest->GotoIndex(index);
			if (!NS_SUCCEEDED(rv)) return NS_ERROR_FAILURE;
		}
	}

	return NS_OK;
}

nsresult EphyBrowser::Destroy ()
{
	DetachListeners ();

      	mWebBrowser = nsnull;
	mDOMWindow = nsnull;
	mEventTarget = nsnull;
	mEmbed = nsnull;

	mInitialized = PR_FALSE;

	return NS_OK;
}

nsresult EphyBrowser::GoToHistoryIndex (PRInt16 index)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebNavigation> ContentNav = do_QueryInterface (mWebBrowser);
	NS_ENSURE_TRUE (ContentNav, NS_ERROR_FAILURE);

	return ContentNav->GotoIndex (index);
}

nsresult EphyBrowser::SetZoom (float aZoom)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIContentViewer> contentViewer;	
	GetContentViewer (getter_AddRefs(contentViewer));
	NS_ENSURE_TRUE (contentViewer, NS_ERROR_FAILURE);

	nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(contentViewer);
	NS_ENSURE_TRUE (mdv, NS_ERROR_FAILURE);

	return mdv->SetTextZoom (aZoom);
}

nsresult EphyBrowser::GetContentViewer (nsIContentViewer **aViewer)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIDocShell> ourDocShell(do_GetInterface(mWebBrowser));
	NS_ENSURE_TRUE (ourDocShell, NS_ERROR_FAILURE);

	return ourDocShell->GetContentViewer(aViewer);
}

nsresult EphyBrowser::GetZoom (float *aZoom)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIContentViewer> contentViewer;	
	GetContentViewer (getter_AddRefs(contentViewer));
	NS_ENSURE_TRUE (contentViewer, NS_ERROR_FAILURE);

	nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(contentViewer);
	NS_ENSURE_TRUE (mdv, NS_ERROR_FAILURE);

	return mdv->GetTextZoom (aZoom);
}

nsresult
EphyBrowser::ScrollLines (PRInt32 aNumLines)
{
	nsCOMPtr<nsIDOMWindow> DOMWindow;

	mWebBrowserFocus->GetFocusedWindow (getter_AddRefs(DOMWindow));
	if (!DOMWindow)
	{
		DOMWindow = mDOMWindow;
	}
	NS_ENSURE_TRUE (DOMWindow, NS_ERROR_FAILURE);

	return DOMWindow->ScrollByLines (aNumLines);
}

nsresult
EphyBrowser::ScrollPages (PRInt32 aNumPages)
{
	nsCOMPtr<nsIDOMWindow> DOMWindow;

	mWebBrowserFocus->GetFocusedWindow (getter_AddRefs(DOMWindow));
	if (!DOMWindow)
	{
		DOMWindow = mDOMWindow;
	}
	NS_ENSURE_TRUE (DOMWindow, NS_ERROR_FAILURE);

	return DOMWindow->ScrollByPages (aNumPages);
}

nsresult
EphyBrowser::ScrollPixels (PRInt32 aDeltaX,
			   PRInt32 aDeltaY)
{
	nsCOMPtr<nsIDOMWindow> DOMWindow;

	mWebBrowserFocus->GetFocusedWindow (getter_AddRefs(DOMWindow));
	if (!DOMWindow)
	{
		DOMWindow = mDOMWindow;
	}
	NS_ENSURE_TRUE (DOMWindow, NS_ERROR_FAILURE);

	return DOMWindow->ScrollBy (aDeltaX, aDeltaY);
}

nsresult
EphyBrowser::GetDocument (nsIDOMDocument **aDOMDocument)
{
	return mDOMWindow->GetDocument (aDOMDocument);
}

nsresult
EphyBrowser::GetTargetDocument (nsIDOMDocument **aDOMDocument)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	/* Use the current target document */
	if (mTargetDocument)
	{
		*aDOMDocument = mTargetDocument.get();

		NS_IF_ADDREF(*aDOMDocument);

		return NS_OK;
	}

	/* Use the focused document */
	nsresult rv;
	nsCOMPtr<nsIDOMWindow> DOMWindow;
	rv = mWebBrowserFocus->GetFocusedWindow (getter_AddRefs(DOMWindow));
	if (NS_SUCCEEDED (rv) && DOMWindow)
	{
		return DOMWindow->GetDocument (aDOMDocument);
	}

	/* Use the main document */
	return mDOMWindow->GetDocument (aDOMDocument);
}

nsresult EphyBrowser::GetSHInfo (PRInt32 *count, PRInt32 *index)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsISHistory> SessionHistory;
	GetSHistory (getter_AddRefs(SessionHistory));
	NS_ENSURE_TRUE (SessionHistory, NS_ERROR_FAILURE);

	SessionHistory->GetCount (count);
	SessionHistory->GetIndex (index);	

	return NS_OK;
}

nsresult EphyBrowser::GetSHTitleAtIndex (PRInt32 index, PRUnichar **title)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsISHistory> SessionHistory;
	GetSHistory (getter_AddRefs(SessionHistory));
	NS_ENSURE_TRUE (SessionHistory, NS_ERROR_FAILURE);

	nsCOMPtr<nsIHistoryEntry> he;
	SessionHistory->GetEntryAtIndex (index, PR_FALSE,
					 getter_AddRefs (he));
	NS_ENSURE_TRUE (he, NS_ERROR_FAILURE);

	nsresult rv;
	rv = he->GetTitle (title);
	NS_ENSURE_TRUE (NS_SUCCEEDED (rv) && title, NS_ERROR_FAILURE);

	return NS_OK;
}

nsresult EphyBrowser::GetSHUrlAtIndex (PRInt32 index, nsACString &url)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsISHistory> SessionHistory;
	GetSHistory (getter_AddRefs(SessionHistory));
	NS_ENSURE_TRUE (SessionHistory, NS_ERROR_FAILURE);

	nsCOMPtr<nsIHistoryEntry> he;
	SessionHistory->GetEntryAtIndex (index, PR_FALSE,
					 getter_AddRefs (he));
	NS_ENSURE_TRUE (he, NS_ERROR_FAILURE);

	nsCOMPtr<nsIURI> uri;
	he->GetURI (getter_AddRefs(uri));
	NS_ENSURE_TRUE (uri, NS_ERROR_FAILURE);

	nsresult rv;
	rv = uri->GetSpec(url);
	NS_ENSURE_TRUE (NS_SUCCEEDED (rv) && url.Length(), NS_ERROR_FAILURE);

	return NS_OK;
}

nsresult EphyBrowser::GetPageDescriptor(nsISupports **aPageDescriptor)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIDocShell> ds = do_GetInterface (mWebBrowser);

	nsCOMPtr<nsIWebPageDescriptor> wpd = do_QueryInterface (ds);
	NS_ENSURE_TRUE (wpd, NS_ERROR_FAILURE);

	*aPageDescriptor = wpd.get();
	NS_IF_ADDREF (*aPageDescriptor);

	return NS_OK;
}

nsresult EphyBrowser::GetDOMWindow (nsIDOMWindow **aDOMWindow)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	NS_IF_ADDREF (*aDOMWindow = mDOMWindow);

	return NS_OK;
}

nsresult EphyBrowser::GetDocumentURI (nsIURI **aURI)
{
	if (!mDOMWindow) return NS_ERROR_NOT_INITIALIZED;

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

	return webNav->GetCurrentURI (aURI);
}

nsresult EphyBrowser::GetTargetDocumentURI (nsIURI **aURI)
{
	if (!mWebBrowser) return NS_ERROR_NOT_INITIALIZED;

        nsCOMPtr<nsIDOMDocument> domDoc;
	GetTargetDocument (getter_AddRefs(domDoc));
	NS_ENSURE_TRUE (domDoc, NS_ERROR_FAILURE);

	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 */

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

	return webNav->GetCurrentURI (aURI);
}

nsresult EphyBrowser::ForceEncoding (const char *encoding) 
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIContentViewer> contentViewer;	
	GetContentViewer (getter_AddRefs(contentViewer));
	NS_ENSURE_TRUE (contentViewer, NS_ERROR_FAILURE);

	nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(contentViewer);
	NS_ENSURE_TRUE (mdv, NS_ERROR_FAILURE);

	return mdv->SetForceCharacterSet (nsCString(encoding));
}

nsresult EphyBrowser::GetEncoding (nsACString &encoding)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIDocCharset> docCharset = do_GetInterface (mWebBrowser);
	NS_ENSURE_TRUE (docCharset, NS_ERROR_FAILURE);

	char *charset;
	docCharset->GetCharset (&charset);
	encoding = charset;
	nsMemory::Free (charset);

	return NS_OK;
}

nsresult EphyBrowser::GetForcedEncoding (nsACString &encoding)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIContentViewer> contentViewer;	
	GetContentViewer (getter_AddRefs(contentViewer));
	NS_ENSURE_TRUE (contentViewer, NS_ERROR_FAILURE);

	nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(contentViewer);
	NS_ENSURE_TRUE (mdv, NS_ERROR_FAILURE);

	nsresult rv;
	rv = mdv->GetForceCharacterSet (encoding);
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

	return NS_OK;
}

nsresult EphyBrowser::PushTargetDocument (nsIDOMDocument *domDoc)
{
	mTargetDocument = domDoc;

	return NS_OK;
}

nsresult EphyBrowser::PopTargetDocument ()
{
	mTargetDocument = nsnull;

	return NS_OK;
}

nsresult EphyBrowser::DoCommand (const char *command)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsICommandManager> cmdManager;
	cmdManager = do_GetInterface (mWebBrowser);
	NS_ENSURE_TRUE (cmdManager, NS_ERROR_FAILURE);

	return cmdManager->DoCommand (command, nsnull, nsnull);
}

nsresult EphyBrowser::GetCommandState (const char *command, PRBool *enabled)
{
	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsICommandManager> cmdManager;
	cmdManager = do_GetInterface (mWebBrowser);
	NS_ENSURE_TRUE (cmdManager, NS_ERROR_FAILURE);

	return cmdManager->IsCommandEnabled (command, nsnull, enabled);
}

#define NUM_MODIFIED_TEXTFIELDS_REQUIRED	2

PRBool
EphyBrowser::CompareFormsText (nsAString &aDefaultText, nsAString &aUserText)
{
	if (aDefaultText.Length() != aUserText.Length())
	{
		return FALSE;
	}

	/* Mozilla Bug 218277, 195946 and others */
	const PRUnichar *text = aDefaultText.BeginReading();
	for (PRUint32 i = 0; i < aDefaultText.Length(); i++)
	{
		if (text[i] == 0xa0)
		{
			aDefaultText.Replace (i, 1, ' ');
		}
	}

	return (memcmp (aDefaultText.BeginReading(),
		        aUserText.BeginReading(),
		        aUserText.Length() * sizeof (PRUnichar)) == 0);
}

nsresult EphyBrowser::GetDocumentHasModifiedForms (nsIDOMDocument *aDomDoc, PRUint32 *aNumTextFields, PRBool *aHasTextArea)
{
	nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(aDomDoc);
	/* it's okay not to be a HTML doc (happens for XUL documents, like about:config) */
	if (!htmlDoc) return NS_OK;

	nsCOMPtr<nsIDOMHTMLCollection> forms;
	htmlDoc->GetForms (getter_AddRefs (forms));
	if (!forms) return NS_OK; /* it's ok not to have any forms */
	
	nsCOMPtr<nsIDOMDocumentView> docView (do_QueryInterface (aDomDoc));
	nsCOMPtr<nsIDOMViewCSS> defaultCSSView;
	if (docView)
	{
		nsCOMPtr<nsIDOMAbstractView> defaultView;
		docView->GetDefaultView (getter_AddRefs (defaultView));
		defaultCSSView = do_QueryInterface (defaultView);
	}

	const PRUnichar visibilityLiteral[] = { 'v', 'i', 's', 'i', 'b', 'i', 'l', 'i', 't', 'y', '\0' };
	const PRUnichar visibleLiteral[] = { 'v', 'i', 's', 'i', 'b', 'l', 'e', '\0' };
	nsString visibilityAttr(visibilityLiteral);
	nsString visibleAttr(visibleLiteral);
	nsString EmptyString;
	nsCOMPtr<nsIDOMCSSStyleDeclaration> computedStyle;
	nsCOMPtr<nsIDOMCSSValue> cssValue;
	nsCOMPtr<nsIDOMCSSPrimitiveValue> primitiveValue;

	PRUint32 formNum;
	forms->GetLength (&formNum);

	/* check all forms */
	for (PRUint32 formIndex = 0; formIndex < formNum; formIndex++)
	{
		nsCOMPtr<nsIDOMNode> formNode;
		forms->Item (formIndex, getter_AddRefs (formNode));
		if (!formNode) continue;

		nsCOMPtr<nsIDOMHTMLFormElement> formElement = do_QueryInterface (formNode);
		if (!formElement) continue;

		PRBool isVisible = PR_FALSE;
		nsresult rv;
		computedStyle = nsnull;

		/* Check defaultCSSView for NULL, see bug #327764 */
		if (defaultCSSView &&
		    NS_SUCCEEDED (defaultCSSView->GetComputedStyle (formElement, EmptyString,
				  				    getter_AddRefs (computedStyle))) &&
		    computedStyle)
		{
			rv = computedStyle->GetPropertyCSSValue(visibilityAttr, getter_AddRefs (cssValue));
			if (NS_SUCCEEDED (rv) && cssValue)
			{
				nsString value;
				rv = cssValue->GetCssText (value);
				if (NS_SUCCEEDED (rv) && value.Length ())
				{
					/* what to do for "collapse" and "inherit" values? */
					isVisible = CompareFormsText (value, visibleAttr);
				}
			}
		}

		if (!isVisible)
		{
			LOG ("Form node %p is invisible\n", formNode.get());
			continue;
		}
		
		nsCOMPtr<nsIDOMHTMLCollection> formElements;
		formElement->GetElements (getter_AddRefs (formElements));
		if (!formElements) continue;

		PRUint32 elementNum;
		formElements->GetLength (&elementNum);

		/* check all input elements in the form for user input */
		for (PRUint32 elementIndex = 0; elementIndex < elementNum; elementIndex++)
		{
			nsCOMPtr<nsIDOMNode> domNode;
			formElements->Item (elementIndex, getter_AddRefs (domNode));
			if (!domNode) continue;

			nsCOMPtr<nsIDOMElement> domElement (do_QueryInterface (domNode));
			if (!domElement) continue;

			isVisible = PR_FALSE;
			computedStyle = nsnull;
			/* Check defaultCSSView for NULL, see bug #327764 */
			if (defaultCSSView &&
			    NS_SUCCEEDED (defaultCSSView->GetComputedStyle (domElement, EmptyString,
					  				    getter_AddRefs (computedStyle))) &&
			    computedStyle)
			{
				rv = computedStyle->GetPropertyCSSValue(visibilityAttr, getter_AddRefs (cssValue));
				if (NS_SUCCEEDED (rv) && cssValue)
				{
					nsString value;
					rv = cssValue->GetCssText (value);
					if (NS_SUCCEEDED (rv) && value.Length ())
					{
						/* what to do for "collapse" and "inherit" values? */
						isVisible = CompareFormsText (value, visibleAttr);
					}
				}
			}

			if (!isVisible)
			{
				LOG("Form node %p element %p is invisible\n", formNode.get(), domNode.get());
				continue;
			}
	
			nsCOMPtr<nsIDOMHTMLTextAreaElement> areaElement = do_QueryInterface (domNode);
			if (areaElement)
			{
				PRBool isDisabled = PR_TRUE;
				areaElement->GetDisabled (&isDisabled);
				if (isDisabled)
				{
					LOG ("Form node %p element %p [textarea] is disabled\n", formNode.get(), areaElement.get());
					continue;
				}

				nsString defaultText, userText;
				areaElement->GetDefaultValue (defaultText);
				areaElement->GetValue (userText);

				if (!CompareFormsText (defaultText, userText))
				{
					*aHasTextArea = PR_TRUE;
					return NS_OK;
				}

				continue;
			}

			nsCOMPtr<nsIDOMHTMLInputElement> inputElement = do_QueryInterface(domNode);
			if (!inputElement) continue;
	
			PRBool isDisabled = PR_TRUE;
			inputElement->GetDisabled (&isDisabled);
			if (isDisabled)
			{
				LOG ("Form node %p element %p [input] is disabled\n", formNode.get(), inputElement.get());
				continue;
			}

			nsString type;
			inputElement->GetType(type);

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

			if (g_ascii_strcasecmp (cType.get(), "text") == 0)
			{
				nsString defaultText, userText;
				PRInt32 max_length;
				inputElement->GetDefaultValue (defaultText);
				inputElement->GetValue (userText);
				inputElement->GetMaxLength (&max_length);

				/* There are forms for which defaultValue is longer than
				 * userValue. Mozilla consider this not a bug [see WONTFIXed
				 * bug 232057], but we need to check for this here.
				 */
				if (defaultText.Length() > (PRUint32)max_length)
				{
					defaultText.Cut (max_length, PR_UINT32_MAX);
				}

				if (!CompareFormsText (defaultText, userText))
				{
					(*aNumTextFields)++;
					if (*aNumTextFields >= NUM_MODIFIED_TEXTFIELDS_REQUIRED)
					{
						return NS_OK;
					}
				}
			}
		}
	}

	return NS_OK;
}

nsresult EphyBrowser::GetHasModifiedForms (PRBool *modified)
{
	*modified = PR_FALSE;

	NS_ENSURE_TRUE (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIDocShell> rootDocShell = do_GetInterface (mWebBrowser);
	NS_ENSURE_TRUE (rootDocShell, NS_ERROR_FAILURE);

	nsCOMPtr<nsISimpleEnumerator> enumerator;
	rootDocShell->GetDocShellEnumerator(nsIDocShellTreeItem::typeContent,
					    nsIDocShell::ENUMERATE_FORWARDS,
					    getter_AddRefs(enumerator));
	NS_ENSURE_TRUE (enumerator, NS_ERROR_FAILURE);

	PRBool hasMore;
	PRBool hasTextArea = PR_FALSE;
	PRUint32 numTextFields = 0;
	while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
	{
		nsCOMPtr<nsISupports> element;
		enumerator->GetNext (getter_AddRefs(element));
		if (!element) continue;

		nsCOMPtr<nsIDocShell> docShell = do_QueryInterface (element);
		if (!docShell) continue;

		nsCOMPtr<nsIContentViewer> contentViewer;
		docShell->GetContentViewer (getter_AddRefs(contentViewer));
		if (!contentViewer) continue;

		nsCOMPtr<nsIDOMDocument> domDoc;
		contentViewer->GetDOMDocument (getter_AddRefs (domDoc));

		nsresult rv;
		rv = GetDocumentHasModifiedForms (domDoc, &numTextFields, &hasTextArea);
		if (NS_SUCCEEDED (rv) &&
		    (numTextFields >= NUM_MODIFIED_TEXTFIELDS_REQUIRED || hasTextArea))
		{
			*modified = PR_TRUE;
			break;
		}
	}

	return NS_OK;
}

nsresult
EphyBrowser::GetSecurityInfo (PRUint32 *aState, nsACString &aDescription)
{
#ifdef HAVE_MOZILLA_PSM
	NS_ENSURE_TRUE (mSecurityInfo, NS_ERROR_FAILURE);

	nsresult rv;
	rv = mSecurityInfo->GetState (aState);
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

	nsString tooltip;
	rv = mSecurityInfo->GetTooltipText (tooltip);
	NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE);

	NS_UTF16ToCString (tooltip,
			   NS_CSTRING_ENCODING_UTF8, aDescription);

	return NS_OK;
#else
	return NS_ERROR_NOT_IMPLEMENTED;
#endif
}

nsresult
EphyBrowser::ShowCertificate ()
{
#ifdef HAVE_MOZILLA_PSM
	NS_ENSURE_TRUE (mSecurityInfo, NS_ERROR_FAILURE);

	nsCOMPtr<nsISSLStatusProvider> statusProvider (do_QueryInterface (mSecurityInfo));
	NS_ENSURE_TRUE (statusProvider, NS_ERROR_FAILURE);

	nsCOMPtr<nsISSLStatus> SSLStatus;
	statusProvider->GetSSLStatus (getter_AddRefs (SSLStatus));
	NS_ENSURE_TRUE (SSLStatus, NS_ERROR_FAILURE);

	nsCOMPtr<nsIX509Cert> serverCert;
	SSLStatus->GetServerCert (getter_AddRefs (serverCert));
	NS_ENSURE_TRUE (serverCert, NS_ERROR_FAILURE);

	nsCOMPtr<nsICertificateDialogs> certDialogs (do_GetService (NS_CERTIFICATEDIALOGS_CONTRACTID));
	NS_ENSURE_TRUE (certDialogs, NS_ERROR_FAILURE);

	nsCOMPtr<nsIInterfaceRequestor> requestor(do_QueryInterface (mDOMWindow));

	return certDialogs->ViewCert (requestor, serverCert);
#else
	return NS_OK;
#endif
}

EphyEmbedDocumentType
EphyBrowser::GetDocumentType ()
{
  EphyEmbedDocumentType type = EPHY_EMBED_DOCUMENT_OTHER;

  NS_ENSURE_TRUE (mDOMWindow, type);

  nsresult rv;
  nsCOMPtr<nsIDOMDocument> domDoc;
  rv = GetDocument (getter_AddRefs (domDoc));
  NS_ENSURE_SUCCESS (rv, type);

  nsCOMPtr<nsIDOMHTMLDocument> htmlDoc (do_QueryInterface (domDoc));
  nsCOMPtr<nsIDOMXMLDocument> xmlDoc (do_QueryInterface (domDoc));
  nsCOMPtr<nsIImageDocument> imgDoc (do_QueryInterface (domDoc));

  if (xmlDoc)
    {
      type = EPHY_EMBED_DOCUMENT_XML;
    }
  else if (imgDoc)
    {
      type = EPHY_EMBED_DOCUMENT_IMAGE;
    }
  else if (htmlDoc)
    {
      type = EPHY_EMBED_DOCUMENT_HTML;
    }

  return type;
}

nsresult
EphyBrowser::Close ()
{
	nsCOMPtr<nsIDOMWindowInternal> domWin (do_QueryInterface (mDOMWindow));
	NS_ENSURE_TRUE (domWin, NS_ERROR_FAILURE);

	return domWin->Close();
}

nsresult
EphyBrowser::GetPIDOMWindow(nsPIDOMWindow **aPIWin)
{
  *aPIWin = nsnull;

  // get the private DOM window
  nsCOMPtr<nsPIDOMWindow> domWindowPrivate = do_QueryInterface(mDOMWindow);

  // and the root window for that DOM window
  *aPIWin = domWindowPrivate->GetPrivateRoot();

  if (*aPIWin) 
  {
    NS_ADDREF(*aPIWin);
    return NS_OK;
  }

  return NS_ERROR_FAILURE;

}

nsresult
EphyBrowser::LoadURI(const char *aURI,
		     PRUint32 aLoadFlags,
		     nsIURI *aReferrer)
{
	nsString uURI;
	nsresult rv = NS_OK;

 	NS_CStringToUTF16 (nsCString (aURI), NS_CSTRING_ENCODING_UTF8, uURI);

	if (uURI.Length() == 0) return NS_OK;
 
	nsCOMPtr<nsIWebNavigation> contentNav = do_QueryInterface (mWebBrowser);
	NS_ENSURE_TRUE (contentNav, NS_ERROR_FAILURE);

	nsCOMPtr<nsPIDOMWindow> piWin;
	rv = GetPIDOMWindow(getter_AddRefs(piWin));
  	NS_ENSURE_SUCCESS (rv, rv);

	nsAutoPopupStatePusher popupStatePusher(piWin, openAllowed);

	rv = contentNav->LoadURI(uURI.get(),   // URI string
			 	 aLoadFlags,   // Load flags
			 	 aReferrer,    // Referring URI
			 	 nsnull,       // Post data
			 	 nsnull);      // extra headers

	return rv;
}

