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

#include "galeon-config.h"
#include "galeon-embed.h"
#include "gul-string.h"
#include "GaleonWrapper.h"
#include "GlobalHistory.h"
#include "PrintProgressListener.h"

#include <gtkmozembed_internal.h>
#include <unistd.h>

#include "nsIContentViewer.h"
#include "nsIPermissionManager.h"
#include "nsIWebBrowserFind.h"
#include "nsIWebBrowserFocus.h"
#include "nsIDocument.h"
#include "nsISHEntry.h"
#include "nsISHistoryInternal.h"
#include "nsIHistoryEntry.h"
#include "nsIWebBrowserPrint.h"
#include "nsIURI.h"
#include "nsIPresShell.h"
#include "nsIMarkupDocumentViewer.h"
#include "nsIComponentManager.h"
#include "nsIDOMElement.h"
#include "nsIDOMNodeList.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptContext.h"

#include "nsIDOMWindowInternal.h"
#include "nsIInterfaceRequestor.h"
#include "nsIFocusController.h"
#include "nsIWebBrowserPersist.h"
#include "nsCWebBrowserPersist.h"
#include "nsNetUtil.h"
#include "nsIChromeEventHandler.h"
#include "nsIClipboardCommands.h"
#include "nsIDOMDocumentStyle.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocShellTreeNode.h"
#include "nsIDocShellTreeOwner.h"
#include "nsICSSLoader.h"
#include "nsICSSStyleSheet.h"
#include "nsICSSLoaderObserver.h"
#if MOZILLA_SNAPSHOT <= 13
#include "nsIHTMLContentContainer.h"
#include "nsIStyleSet.h"
#endif
#include "nsIDocumentObserver.h"
#include "nsCWebBrowser.h"
#include "nsReadableUtils.h"
#include "nsIDOMPopupBlockedEvent.h"
#include "nsIDOMNSDocument.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLCollection.h"
#include "nsIDOMHTMLElement.h"
#include "nsIDOMHTMLImageElement.h"
#include "nsIDOMHTMLFormElement.h"
#include "nsIDOMHTMLAnchorElement.h"
#include "nsIDOMHTMLLinkElement.h"
#include "nsIDOMHTMLMetaElement.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMHTMLTextAreaElement.h"
#include "nsIDeviceContext.h"
#include "nsIPresContext.h"
#include "nsSupportsPrimitives.h"
#include "ContentHandler.h"
#include "nsICacheService.h"
#include "nsICacheSession.h"
#include "nsIDOMNSHTMLDocument.h"
#include "nsIDOMLocation.h"
#include "nsTime.h"
#include "nsITransportSecurityInfo.h"
#include "nsIDocumentCharsetInfo.h"
#include "nsIParser.h"

#ifdef HAVE_MOZILLA_PSM
# include "nsISSLStatus.h"
# include "nsISSLStatusProvider.h"
# include "nsICertificateDialogs.h"
# include "nsIX509Cert.h"
#endif

#include <nsIGenericFactory.h>

template<class T>
static nsresult sProcessLinkNode(nsIDOMNode *node,
				 nsIDOMDocument *doc,
				 GList **ret);

GaleonWrapper::GaleonWrapper () :
			mFaviconEventListener(new GDOMEventListener())
			,mPopupEventListener(new GDOMPopupEventListener())
{
}

GaleonWrapper::~GaleonWrapper ()
{
}

static nsresult
GetDOMEventTarget (nsIWebBrowser* aWebBrowser, nsIDOMEventTarget** aTarget)
{
	nsresult rv;

	nsCOMPtr<nsIDOMWindow> domWin;
	rv = aWebBrowser->GetContentDOMWindow (getter_AddRefs(domWin));
	if (NS_FAILED(rv)) return rv;

	nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(domWin, &rv);
	if (NS_FAILED(rv)) return rv;

#if MOZILLA_SNAPSHOT <= 16
	nsCOMPtr<nsIChromeEventHandler> eventHandler;
	rv = piWin->GetChromeEventHandler (getter_AddRefs(eventHandler));
	if (NS_FAILED(rv)) return rv;
#else
	nsIChromeEventHandler* eventHandler;
	eventHandler = piWin->GetChromeEventHandler();
	if (!eventHandler) return NS_ERROR_FAILURE;
#endif

	nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(eventHandler, &rv);
	if (NS_FAILED(rv)) return rv;

	NS_ADDREF(*aTarget = target.get());
	return NS_OK;
}

nsresult GaleonWrapper::Init (GtkMozEmbed *mozembed)
{
	nsresult result;
	/* Dont redo intialisation if we have already run it */
	if (mWebBrowser) return NS_OK;

	gtk_moz_embed_get_nsIWebBrowser (mozembed,
					 getter_AddRefs(mWebBrowser));
	if (!mWebBrowser) return NS_ERROR_FAILURE;

	
	nsCOMPtr<nsIDOMWindow> DOMWindow;
	mWebBrowser->GetContentDOMWindow (getter_AddRefs (DOMWindow));

	/* This will instantiate an about:blank doc if necessary, but don't
	 * initialize for mozilla XUL dialogs, it breaks them */
	if (!(gtk_moz_embed_get_chrome_mask (mozembed) & GTK_MOZ_EMBED_FLAG_OPENASCHROME))
	{
		nsCOMPtr<nsIDOMDocument> domDocument;
		result = DOMWindow->GetDocument (getter_AddRefs (domDocument));
		if (NS_FAILED (result)) return NS_ERROR_FAILURE;
	}

	// set up favicon listener
	mFaviconEventListener->Init (GALEON_EMBED(mozembed));
	result = ::GetDOMEventTarget (mWebBrowser, getter_AddRefs(mEventTarget));
	if (NS_FAILED(result)) return result;
	result = mEventTarget->AddEventListener(NS_LITERAL_STRING("DOMLinkAdded"),
			                        mFaviconEventListener, PR_FALSE);
	if (NS_FAILED(result)) g_warning ("AddFaviconEventListener failed!");

	// set up popup block listener
	mPopupEventListener->Init(GALEON_EMBED(mozembed));
	result = mEventTarget->AddEventListener(NS_LITERAL_STRING("DOMPopupBlocked"),
						mPopupEventListener, PR_FALSE);
	if (NS_FAILED(result)) g_warning ("AddPopupEventListener failed!");
	return NS_OK;
}

nsresult GaleonWrapper::GetDocShell (nsIDocShell **aDocShell)
{
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

        nsCOMPtr<nsIDocShellTreeItem> browserAsItem;
        browserAsItem = do_QueryInterface(mWebBrowser);
        if (!browserAsItem) return NS_ERROR_FAILURE;

        // get the owner for that item
        nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
        browserAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
        if (!treeOwner) return NS_ERROR_FAILURE;

        // get the primary content shell as an item
        nsCOMPtr<nsIDocShellTreeItem> contentItem;
        treeOwner->GetPrimaryContentShell(getter_AddRefs(contentItem));
        if (!contentItem) return NS_ERROR_FAILURE;

        // QI that back to a docshell
        nsCOMPtr<nsIDocShell> DocShell;
        DocShell = do_QueryInterface(contentItem);
        if (!DocShell) return NS_ERROR_FAILURE;

        *aDocShell = DocShell.get();

        NS_IF_ADDREF(*aDocShell);
        
        return NS_OK;
}
nsresult GaleonWrapper::Print (nsIPrintSettings *options, PRBool preview)
{
	nsresult result;

	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserPrint> print(do_GetInterface(mWebBrowser, &result));
	if (NS_FAILED(result) || !print) return NS_ERROR_FAILURE;

	if (!preview)
	{
		GPrintListener *listener = new GPrintListener();
		result = print->Print (options, listener);
	}
	else
	{
		result = print->PrintPreview(options, nsnull, nsnull);
	}

	return result;
}

nsresult GaleonWrapper::PrintPreviewClose (void)
{
	nsresult rv;
	PRBool isPreview = PR_FALSE;

	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserPrint> print(do_GetInterface(mWebBrowser, &rv));
	if (NS_FAILED(rv) || !print) return NS_ERROR_FAILURE;

	rv = print->GetDoingPrintPreview(&isPreview);
	if (NS_SUCCEEDED (rv) && isPreview == PR_TRUE)
	{
		rv = print->ExitPrintPreview();
	}

	return rv;
}

nsresult GaleonWrapper::PrintPreviewNumPages (int *numPages)
{
	nsresult rv;

	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserPrint> print(do_GetInterface(mWebBrowser, &rv));
	if (NS_FAILED(rv) || !print) return NS_ERROR_FAILURE;

	rv = print->GetPrintPreviewNumPages(numPages);
	return rv;
}

nsresult GaleonWrapper::PrintPreviewNavigate(PRInt16 navType, PRInt32 pageNum)
{
	nsresult rv;

	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserPrint> print(do_GetInterface(mWebBrowser, &rv));
	if (NS_FAILED(rv) || !print) return NS_ERROR_FAILURE;

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

nsresult GaleonWrapper::GetPrintSettings (nsIPrintSettings **options)
{
	nsresult result;
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserPrint> print(do_GetInterface(mWebBrowser, &result));
	if (NS_FAILED(result) || !print) return NS_ERROR_FAILURE;

	return print->GetGlobalPrintSettings(options);
}

nsresult GaleonWrapper::GetSHistory (nsISHistory **aSHistory)
{
	nsresult result;

	nsCOMPtr<nsIDocShell> DocShell;
	result = GetDocShell (getter_AddRefs(DocShell));
	if (NS_FAILED(result) || !DocShell) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIWebNavigation> ContentNav = do_QueryInterface (DocShell,
								   &result);
	if (!ContentNav) return NS_ERROR_FAILURE;

	nsCOMPtr<nsISHistory> SessionHistory;
	result = ContentNav->GetSessionHistory (getter_AddRefs (SessionHistory));
	if (!SessionHistory) return NS_ERROR_FAILURE;

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

	return NS_OK;
}

nsresult GaleonWrapper::Destroy ()
{
	mEventTarget->RemoveEventListener(NS_LITERAL_STRING("DOMLinkAdded"),
					  mFaviconEventListener, PR_FALSE);
	mEventTarget->RemoveEventListener(NS_LITERAL_STRING("DOMPopupBlocked"),
					  mPopupEventListener, PR_FALSE);

	mEventTarget = nsnull;
      	mWebBrowser = nsnull;
	
	return NS_OK;
}

nsresult GaleonWrapper::GoToHistoryIndex (PRInt16 index)
{
	nsresult result;

	nsCOMPtr<nsIDocShell> DocShell;
	result = GetDocShell (getter_AddRefs(DocShell));
	if (NS_FAILED(result) || !DocShell) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIWebNavigation> ContentNav = do_QueryInterface (DocShell,
								   &result);
	if (!ContentNav) return NS_ERROR_FAILURE;

	return  ContentNav->GotoIndex (index);
}

nsresult GaleonWrapper::SetZoom (float aZoom, PRBool reflow)
{
	nsresult result;

	nsCOMPtr<nsIDocShell> DocShell;
	result = GetDocShell (getter_AddRefs(DocShell));
	if (NS_FAILED(result) || !DocShell) return NS_ERROR_FAILURE;

	if (reflow)
	{
		nsCOMPtr<nsIContentViewer> contentViewer;	
		result = DocShell->GetContentViewer (getter_AddRefs(contentViewer));
		if (!NS_SUCCEEDED (result) || !contentViewer) return NS_ERROR_FAILURE;

		nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(contentViewer,
								  &result);
		if (NS_FAILED(result) || !mdv) return NS_ERROR_FAILURE;

		return mdv->SetTextZoom (aZoom);
	}
	else
	{
		SetZoomOnDocshell (aZoom, DocShell);

		nsCOMPtr<nsIDocShellTreeNode> docShellNode(do_QueryInterface(DocShell));
		if (docShellNode)
		{
			PRInt32 i;
			PRInt32 n;
			docShellNode->GetChildCount(&n);
			for (i=0; i < n; i++) 
			{
				nsCOMPtr<nsIDocShellTreeItem> child;
				docShellNode->GetChildAt(i, getter_AddRefs(child));
				nsCOMPtr<nsIDocShell> childAsShell(do_QueryInterface(child));
				if (childAsShell) 
				{
					return SetZoomOnDocshell (aZoom, childAsShell);
				}
			}
		}
	}

	return NS_OK;
}

nsresult GaleonWrapper::SetZoomOnDocshell (float aZoom, nsIDocShell *DocShell)
{
	nsresult result;

	nsCOMPtr<nsIPresContext> PresContext;
	result = DocShell->GetPresContext (getter_AddRefs(PresContext));
	if (NS_FAILED(result) || !PresContext) return NS_ERROR_FAILURE;

#if MOZILLA_SNAPSHOT > 13
	nsIDeviceContext *DeviceContext(nsnull);
	DeviceContext = PresContext->DeviceContext();
	if (!DeviceContext) return NS_ERROR_FAILURE;
#else					
	nsCOMPtr<nsIDeviceContext> DeviceContext;
	result = PresContext->GetDeviceContext (getter_AddRefs(DeviceContext));
	if (NS_FAILED(result) || !DeviceContext) return NS_ERROR_FAILURE;
#endif

	return DeviceContext->SetTextZoom (aZoom);
}

nsresult GaleonWrapper::GetZoom (float *aZoom)
{
	nsresult result;

	nsCOMPtr<nsIDocShell> DocShell;
	result = GetDocShell (getter_AddRefs(DocShell));
	if (NS_FAILED(result) || !DocShell) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIContentViewer> contentViewer;	
	result = DocShell->GetContentViewer (getter_AddRefs(contentViewer));
	if (!NS_SUCCEEDED (result) || !contentViewer) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(contentViewer,
								  &result);
	if (NS_FAILED(result) || !mdv) return NS_ERROR_FAILURE;

	return mdv->GetTextZoom (aZoom);
}

nsresult GaleonWrapper::GetFocusedDOMWindow (nsIDOMWindow **aDOMWindow)
{
	nsresult rv;
	
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserFocus> focus = do_GetInterface(mWebBrowser, &rv);
	if (NS_FAILED(rv) || !focus) return NS_ERROR_FAILURE;

	rv = focus->GetFocusedWindow (aDOMWindow);
	if (NS_FAILED(rv))
		rv = mWebBrowser->GetContentDOMWindow (aDOMWindow);
	return rv;
}

nsresult GaleonWrapper::GetDOMWindow (nsIDOMWindow **aDOMWindow)
{
	nsresult rv;
	
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	rv = mWebBrowser->GetContentDOMWindow (aDOMWindow);
	
	return rv;
}

nsresult GaleonWrapper::GetDOMDocument (nsIDOMDocument **aDOMDocument)
{
	nsresult result;

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

		NS_IF_ADDREF(*aDOMDocument);

		return NS_OK;
	}

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

	/* Use the main document */
	return GetMainDOMDocument (aDOMDocument);
}

nsresult GaleonWrapper::GetMainDOMDocument (nsIDOMDocument **aDOMDocument)
{
	nsresult result;

	nsCOMPtr<nsIDocShell> DocShell;
	result = GetDocShell (getter_AddRefs(DocShell));
	if (NS_FAILED(result) || !DocShell) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIContentViewer> contentViewer;	
	result = DocShell->GetContentViewer (getter_AddRefs(contentViewer));
	if (!NS_SUCCEEDED (result) || !contentViewer) return NS_ERROR_FAILURE;

	return contentViewer->GetDOMDocument (aDOMDocument);
}

nsresult GaleonWrapper::GetSHInfo (PRInt32 *count, PRInt32 *index)
{
	nsresult result;

	nsCOMPtr<nsISHistory> SessionHistory;
	result = GetSHistory (getter_AddRefs(SessionHistory));
	if (NS_FAILED(result) || ! SessionHistory) return NS_ERROR_FAILURE;

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

	return NS_OK;
}

nsresult GaleonWrapper::GetSHTitleAtIndex (PRInt32 index, PRUnichar **title)
{
	nsresult result;

	nsCOMPtr<nsISHistory> SessionHistory;
	result = GetSHistory (getter_AddRefs(SessionHistory));
	if (NS_FAILED(result) || ! SessionHistory) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIHistoryEntry> he;
	result = SessionHistory->GetEntryAtIndex (index, PR_FALSE,
						  getter_AddRefs (he));
	if (!NS_SUCCEEDED(result) || (!he)) return NS_ERROR_FAILURE;

	result = he->GetTitle (title);
	if (!NS_SUCCEEDED(result) || (!title)) return NS_ERROR_FAILURE;

	return NS_OK;
}

nsresult GaleonWrapper::GetSHUrlAtIndex (PRInt32 index, nsCString &url)
{
	nsresult result;

	nsCOMPtr<nsISHistory> SessionHistory;
	result = GetSHistory (getter_AddRefs(SessionHistory));
	if (NS_FAILED(result) || ! SessionHistory) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIHistoryEntry> he;
	result = SessionHistory->GetEntryAtIndex (index, PR_FALSE,
						  getter_AddRefs (he));
	if (NS_FAILED(result) || (!he)) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIURI> uri;
	result = he->GetURI (getter_AddRefs(uri));
	if (NS_FAILED(result) || (!uri)) return NS_ERROR_FAILURE;

	result = uri->GetSpec(url);
	if (NS_FAILED(result) || url.IsEmpty()) return NS_ERROR_FAILURE;

	return NS_OK;
}

nsresult GaleonWrapper::FindSetProperties (const PRUnichar *search_string,
					   PRBool case_sensitive,
					   PRBool wrap_around)
{
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserFind> finder (do_GetInterface(mWebBrowser));
	
	finder->SetSearchString (search_string);
	finder->SetMatchCase (case_sensitive);
	finder->SetWrapFind (wrap_around);
	return NS_OK;
}

nsresult GaleonWrapper::Find (PRBool backwards,
			      PRBool *didFind)
{
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIWebBrowserFind> finder (do_GetInterface(mWebBrowser));
	
	finder->SetFindBackwards (backwards);

	return finder->FindNext(didFind);
}


nsresult GaleonWrapper::GetWebNavigation(nsIWebNavigation **aWebNavigation)
{
	nsresult result;

	nsCOMPtr<nsIDOMWindow> DOMWindow;
	result = GetFocusedDOMWindow (getter_AddRefs(DOMWindow));
	if (NS_FAILED(result) || !DOMWindow) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIScriptGlobalObject> scriptGlobal = do_QueryInterface(DOMWindow);
	if (!scriptGlobal) return NS_ERROR_FAILURE;

#if MOZILLA_SNAPSHOT > 13
	nsIDocShell *docshell = scriptGlobal->GetDocShell();
	if (!docshell) return NS_ERROR_FAILURE;
#else
	nsCOMPtr<nsIDocShell> docshell;
	if (NS_FAILED(scriptGlobal->GetDocShell(getter_AddRefs(docshell))))
	        return NS_ERROR_FAILURE;
#endif

	nsCOMPtr<nsIWebNavigation> wn = do_QueryInterface (docshell, &result);
	if (!wn || !NS_SUCCEEDED (result)) return NS_ERROR_FAILURE;

	NS_IF_ADDREF(*aWebNavigation = wn);
	return NS_OK;
}

nsresult GaleonWrapper::ReloadDocument ()
{
	nsresult result;

	nsCOMPtr<nsIWebNavigation> wn;
	result = GetWebNavigation(getter_AddRefs(wn));
	if (!wn || !NS_SUCCEEDED (result)) return NS_ERROR_FAILURE;

	result = wn->Reload (nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE | 
			     nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY);
	if (!NS_SUCCEEDED (result)) return NS_ERROR_FAILURE;

	return NS_OK;
}

nsresult GaleonWrapper::LoadDocument(nsISupports *aPageDescriptor,
				     PRUint32 aDisplayType)
{
	nsresult rv;

	nsCOMPtr<nsIWebNavigation> wn;
	rv = GetWebNavigation(getter_AddRefs(wn));
	if (!wn || !NS_SUCCEEDED(rv)) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIWebPageDescriptor> wpd = do_QueryInterface(wn, &rv);
	if (!wpd || !NS_SUCCEEDED(rv)) return NS_ERROR_FAILURE;

	return wpd->LoadPage(aPageDescriptor, aDisplayType);
}

nsresult GaleonWrapper::GetPageDescriptor(nsISupports **aPageDescriptor)
{
	nsresult rv;

	nsCOMPtr<nsIWebNavigation> wn;
	rv = GetWebNavigation(getter_AddRefs(wn));
	if (!wn || !NS_SUCCEEDED(rv)) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIWebPageDescriptor> wpd = do_QueryInterface(wn, &rv);
	if (!wpd || !NS_SUCCEEDED(rv)) return NS_ERROR_FAILURE;

	return wpd->GetCurrentDescriptor(aPageDescriptor);
}

nsresult GaleonWrapper::GetMainDocumentUrl (nsCString &url)
{
	nsresult result;

	nsCOMPtr<nsIDOMDocument> DOMDocument;

	result = GetMainDOMDocument (getter_AddRefs(DOMDocument));
	if (NS_FAILED(result) || !DOMDocument) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDocument> doc = do_QueryInterface(DOMDocument);
	if(!doc) return NS_ERROR_FAILURE;

#if MOZILLA_SNAPSHOT > 13
	nsIURI *uri = doc->GetDocumentURI ();
#elif MOZILLA_SNAPSHOT > 11
	nsIURI *uri = doc->GetDocumentURL ();
#else
	nsCOMPtr<nsIURI> uri;
	doc->GetDocumentURL(getter_AddRefs(uri));
#endif

	return uri->GetSpec (url);
}

nsresult GaleonWrapper::GetDocumentUrl (nsCString &url)
{
        nsresult result;

        nsCOMPtr<nsIDOMDocument> DOMDocument;

        result = GetDOMDocument (getter_AddRefs(DOMDocument));
        if (NS_FAILED(result) || !DOMDocument) return NS_ERROR_FAILURE;

        nsCOMPtr<nsIDocument> doc = do_QueryInterface(DOMDocument);
        if(!doc) return NS_ERROR_FAILURE;

#if MOZILLA_SNAPSHOT > 13
	nsIURI *uri = doc->GetDocumentURI ();
#elif MOZILLA_SNAPSHOT > 11
	nsIURI *uri = doc->GetDocumentURL ();
#else
        nsCOMPtr<nsIURI> uri;
        doc->GetDocumentURL(getter_AddRefs(uri));
#endif

        uri->GetSpec (url);

        return NS_OK;
}

nsresult  GaleonWrapper::ClearHistory ()
{
	nsresult result;
	PRInt32 count, index;
	nsCOMPtr<nsIHistoryEntry> he;
	nsCOMPtr<nsISHEntry> she;

	nsCOMPtr<nsISHistory> SessionHistory;
	result = GetSHistory (getter_AddRefs(SessionHistory));
	if (NS_FAILED(result) || !SessionHistory) return NS_ERROR_FAILURE;

	SessionHistory->GetCount (&count);
	if (count <= 1) return NS_OK;

	SessionHistory->GetIndex (&index);
	
	/* Save the current entry */
	result = SessionHistory->GetEntryAtIndex (index, PR_FALSE,
						  getter_AddRefs (he));
	if (!NS_SUCCEEDED(result) || (!he))
		return NS_ERROR_FAILURE;

	she = do_QueryInterface (he);
	if (!she) return NS_ERROR_FAILURE;

	/* Clear the history */
	SessionHistory->PurgeHistory (count);

	/* Put the current entry back in */
	nsCOMPtr<nsISHistoryInternal> hi = do_QueryInterface (SessionHistory);
	if (!hi) return NS_ERROR_FAILURE;

	result = hi->AddEntry (she, PR_TRUE);
	if (!NS_SUCCEEDED(result) || (!she))
		return NS_ERROR_FAILURE;

	return NS_OK;
}

nsresult  GaleonWrapper::CopyHistoryTo (GaleonWrapper *dest, 
					PRBool back_history, 
					PRBool forward_history, 
					PRBool set_current)
{
	nsresult result;
	PRInt32 count, index;
	
	nsCOMPtr<nsISHistory> h_src;
	result = GetSHistory (getter_AddRefs(h_src));
	if (NS_FAILED(result) || !h_src) return NS_ERROR_FAILURE;

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

	nsCOMPtr<nsISHistory> h_dest;
	result = dest->GetSHistory (getter_AddRefs (h_dest));
	if (!NS_SUCCEEDED (result) || (!h_dest)) return NS_ERROR_FAILURE;

	nsCOMPtr<nsISHistoryInternal> hi_dest = do_QueryInterface (h_dest);
	if (!hi_dest) return NS_ERROR_FAILURE;

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

		for (PRInt32 i = (back_history ? 0 : index + 1); 
		     i < (forward_history ? count : index + 1);
		     i++) 
		{

			result = h_src->GetEntryAtIndex (i, PR_FALSE,
							 getter_AddRefs (he));
			if (!NS_SUCCEEDED(result) || (!he))
				return NS_ERROR_FAILURE;

			she = do_QueryInterface (he);
			if (!she) return NS_ERROR_FAILURE;

			result = hi_dest->AddEntry (she, PR_TRUE);
			if (!NS_SUCCEEDED(result) || (!she))
				return NS_ERROR_FAILURE;
		}
		
		if (set_current)
		{
			nsCOMPtr<nsIDocShell> destDocShell;
			result = dest->GetDocShell (getter_AddRefs(destDocShell));
			if (NS_FAILED(result) || !destDocShell) return NS_ERROR_FAILURE;
		
			nsCOMPtr<nsIWebNavigation> wn_dest = do_QueryInterface (destDocShell, &result);
			
			result = wn_dest->GotoIndex(index);
			if (!NS_SUCCEEDED(result)) return NS_ERROR_FAILURE;
		}
	}

	return NS_OK;
}

nsresult GaleonWrapper::ForceEncoding (const char *charset) 
{
	nsresult result;

	nsCOMPtr<nsIDocShell> DocShell;
	result = GetDocShell (getter_AddRefs(DocShell));
	if (NS_FAILED(result) || !DocShell) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIContentViewer> contentViewer;	
	result = DocShell->GetContentViewer (getter_AddRefs(contentViewer));
	if (!NS_SUCCEEDED (result) || !contentViewer) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(contentViewer,
								  &result);
	if (NS_FAILED(result) || !mdv) return NS_ERROR_FAILURE;

#if MOZILLA_SNAPSHOT > 9
	result = mdv->SetForceCharacterSet (nsDependentCString(charset));
#else
	result = mdv->SetForceCharacterSet (NS_ConvertUTF8toUCS2(charset).get());
#endif

	return result;
}

nsresult GaleonWrapper::GetEncodingInfo (GaleonEncodingPageInfo **infoptr)
{
	nsresult result;
	GaleonEncodingPageInfo *info;

	nsCOMPtr<nsIDOMDocument> domDoc;
	result = GetDOMDocument (getter_AddRefs(domDoc));
	if (NS_FAILED (result) || !domDoc) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc, &result);
	if (NS_FAILED (result) || !doc) return NS_ERROR_FAILURE;

	info = g_new0 (GaleonEncodingPageInfo, 1);
	*infoptr = info;

	PRInt32 source;
#if MOZILLA_SNAPSHOT > 11
	source = doc->GetDocumentCharacterSetSource ();
#else
	result = doc->GetDocumentCharacterSetSource (&source);
	if (NS_FAILED (result)) return NS_ERROR_FAILURE;
#endif
	if (source >= kCharsetFromParentForced)
	{
		info->forced = TRUE;
	}

	nsCOMPtr<nsIDocShell> ds;
	ds = do_GetInterface (mWebBrowser);
	if (!ds) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDocumentCharsetInfo> ci;
	result = ds->GetDocumentCharsetInfo (getter_AddRefs (ci));
	if (NS_FAILED(result) || !ci) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIAtom> atom;
	result = ci->GetForcedCharset (getter_AddRefs (atom));
	if (NS_FAILED(result)) return NS_ERROR_FAILURE;
	if (atom)
	{
		nsCAutoString atomstr;
		atom->ToUTF8String (atomstr);

		if (atomstr.Length())
		{
			info->forced = TRUE;
		}
	}

	nsCOMPtr<nsIContentViewer> contentViewer;	
	result = ds->GetContentViewer (getter_AddRefs(contentViewer));
	if (!NS_SUCCEEDED (result) || !contentViewer) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(contentViewer,
								  &result);
	if (NS_FAILED(result) || !mdv) return NS_ERROR_FAILURE;

#if MOZILLA_SNAPSHOT > 11
	const nsACString& charsetEnc = doc->GetDocumentCharacterSet ();
	if (charsetEnc.IsEmpty()) return NS_ERROR_FAILURE;
	info->encoding = g_strdup (PromiseFlatCString(charsetEnc).get());

#elif MOZILLA_SNAPSHOT >= 10
	nsCAutoString charsetEnc;	
	result = doc->GetDocumentCharacterSet (charsetEnc);
	if (NS_FAILED (result)) return NS_ERROR_FAILURE;

	info->encoding = g_strdup (charsetEnc.get());
#else
	nsAutoString charsetEnc;
	result = doc->GetDocumentCharacterSet (charsetEnc);
	if (NS_FAILED (result)) return NS_ERROR_FAILURE;

	info->encoding = g_strdup (NS_ConvertUCS2toUTF8(charsetEnc).get());
#endif

	return NS_OK;
}


nsresult GaleonWrapper::CanCutSelection(PRBool *result)
{
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIClipboardCommands> clipboard (do_GetInterface(mWebBrowser));
	return clipboard->CanCutSelection (result);
}

nsresult GaleonWrapper::CanCopySelection(PRBool *result)
{
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIClipboardCommands> clipboard (do_GetInterface(mWebBrowser));
	return clipboard->CanCopySelection (result);
}

nsresult GaleonWrapper::CanPaste(PRBool *result)
{
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIClipboardCommands> clipboard (do_GetInterface(mWebBrowser));
	return clipboard->CanPaste (result);
}

nsresult GaleonWrapper::CutSelection(void)
{
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIClipboardCommands> clipboard (do_GetInterface(mWebBrowser));
	return clipboard->CutSelection ();
}

nsresult GaleonWrapper::CopySelection(void)
{
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIClipboardCommands> clipboard (do_GetInterface(mWebBrowser));
	return clipboard->CopySelection ();
}

nsresult GaleonWrapper::Paste(void)
{
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIClipboardCommands> clipboard (do_GetInterface(mWebBrowser));
	return clipboard->Paste ();
}

nsresult GaleonWrapper::GetStyleSheets(nsIDOMStyleSheetList** list)
{
	nsresult result;

	nsCOMPtr<nsIDOMDocument> doc;
	result = GetDOMDocument(getter_AddRefs(doc));
	if (NS_FAILED(result) || !doc) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMDocumentStyle> docstyle(do_QueryInterface(doc));
	if (!docstyle) return NS_ERROR_FAILURE;

	result = docstyle->GetStyleSheets(list);
	if (NS_FAILED(result) || !list) return NS_ERROR_FAILURE;

	return NS_OK;
}

nsresult GaleonWrapper::LoadOverrideStyleSheet(char *css,
					       nsIStyleSheet **return_sheet)
{
	nsresult result;

	/* catch necessary stuff */
	nsCOMPtr<nsIDocShell> DocShell;
	result = GetDocShell (getter_AddRefs(DocShell));
	if (NS_FAILED(result) || !DocShell) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIPresShell> ps;
	result = DocShell->GetPresShell (getter_AddRefs(ps));
	if (NS_FAILED(result) || !ps) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDocument> doc;
	ps->GetDocument(getter_AddRefs(doc));

#if MOZILLA_SNAPSHOT > 13
	nsICSSLoader *loader = doc->GetCSSLoader();
	if (!loader) return NS_ERROR_FAILURE;
#else
	nsCOMPtr<nsIHTMLContentContainer> container = do_QueryInterface(doc);
	if(!container) return NS_ERROR_FAILURE;

	nsICSSLoader *loader;
	result = container->GetCSSLoader (loader);
	if (NS_FAILED(result)) return NS_ERROR_FAILURE;
#endif

	/* load sheet */
	nsCOMPtr<nsIURI> uri;
	result = NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF8toUCS2(css));
	if (NS_FAILED(result)) return NS_ERROR_FAILURE;

	nsICSSStyleSheet *sheet;
	result = loader->LoadAgentSheet(uri, &sheet);
	if (NS_FAILED(result)) return NS_ERROR_FAILURE;

	/* catch stylesheet stuff and apply by appending it as a override
	 * sheet and enabling stylesheets */
	nsCOMPtr<nsIStyleSheet> styleSheet = do_QueryInterface(sheet);
	if (!styleSheet) return NS_ERROR_FAILURE;
	
	/* Override - Agent */
#if MOZILLA_SNAPSHOT > 13
	ps->AddOverrideStyleSheet(styleSheet);
#else
	nsCOMPtr<nsIStyleSet> styleSet;
	result = ps->GetStyleSet(getter_AddRefs(styleSet));
	if (NS_FAILED(result) || !styleSet) return NS_ERROR_FAILURE;

	styleSet->AppendUserStyleSheet(styleSheet);
#endif
	styleSheet->SetOwningDocument(doc);

	/* notify mozilla that stylesheets have changed */
	styleSheet->SetEnabled(PR_FALSE);
	styleSheet->SetEnabled(PR_TRUE);
	/* This is Right[tm], but not working for some people (?!)
	doc->SetStyleSheetDisabledState(styleSheet, PR_FALSE);
	*/

	*return_sheet = (nsIStyleSheet*)styleSheet;
	
	return NS_OK;
}

nsresult GaleonWrapper::RemoveOverrideStyleSheet(nsIStyleSheet *remove)
{
	nsresult result;

	/* get all necessary stuff */
	nsCOMPtr<nsIDOMDocument> DOMDocument;
	result = GetMainDOMDocument (getter_AddRefs(DOMDocument));
	if (NS_FAILED(result) || !DOMDocument) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDocument> doc = do_QueryInterface(DOMDocument);
	if(!doc) return NS_ERROR_FAILURE;

	/* catch presshell */
	nsCOMPtr<nsIDocShell> DocShell;
	result = GetDocShell (getter_AddRefs(DocShell));
	if (NS_FAILED(result) || !DocShell) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIPresShell> ps;
	result = DocShell->GetPresShell (getter_AddRefs(ps));
	if (NS_FAILED(result) || !ps) return NS_ERROR_FAILURE;

	/* remove it */
#if MOZILLA_SNAPSHOT > 13
	ps->RemoveOverrideStyleSheet(remove);
#else
	nsCOMPtr<nsIStyleSet> styleSet;
	result = ps->GetStyleSet(getter_AddRefs(styleSet));
	if (NS_FAILED(result) || !styleSet) return NS_ERROR_FAILURE;

	styleSet->RemoveOverrideStyleSheet(remove);
#endif
	remove->SetEnabled(PR_FALSE);

	return NS_OK;
}

nsresult GaleonWrapper::GetLinkInterfaceItems (GList **list)
{
#ifdef NOT_PORTED
	nsresult result;
	PRUint32 links_count;

	/* we accept these rel=.. elements, specified by the w3c */
	const gchar *rel_types[] = {
		"START", "NEXT", "PREV", "PREVIOUS", "CONTENTS", "TOC", "INDEX",
		"GLOSSARY", "COPYRIGHT", "CHAPTER",  "SECTION",
		"SUBSECTION", "APPENDIX", "HELP", "TOP", "SEARCH", "MADE",
		"BOOKMARK", "HOME",
		NULL /* terminator, must be last */
	};

	nsCOMPtr<nsIDOMDocument> DOMDocument;
	result = GetMainDOMDocument (getter_AddRefs(DOMDocument));
	if (NS_FAILED(result) || !DOMDocument) return NS_ERROR_FAILURE;

	/* get list of link elements*/
	NS_NAMED_LITERAL_STRING(strname, "LINK");

	nsCOMPtr<nsIDOMNodeList> links;
	result = aDOMDocument->GetElementsByTagName (strname, 
						     getter_AddRefs (links));
	if (NS_FAILED (result)) return NS_ERROR_FAILURE;

	result = links->GetLength (&links_count);
	if (NS_FAILED (result)) return NS_ERROR_FAILURE;

	for (PRUint32 i = 0; i < links_count; i++)
	{
		/* get to the link element */
		nsCOMPtr<nsIDOMNode> link;
		result = links->Item (i, getter_AddRefs (link));
		if (NS_FAILED (result)) return NS_ERROR_FAILURE;

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

		/* get rel=.. element */
		NS_NAMED_LITERAL_STRING(attr_rel, "rel");
		nsAutoString value;
		linkElement->GetAttribute (attr_rel, value);

		if (value.IsEmpty())
		{
			NS_NAMED_LITERAL_STRING(attr_rev, "rev");
			linkElement->GetAttribute (attr_rev, value);
			if (value.IsEmpty()) continue;
		}

		nsCString relstr = NS_ConvertUCS2toUTF8(value);
		ToUpperCase(relstr);

		/* check for elements we want */
		for (gint j = 0; (rel_types[j] != NULL); j++)
		{
			if (strcmp (relstr.get(), rel_types[j]) == 0)
			{
				/* found one! */
				LinkInterfaceItem *lti =
					g_new0 (LinkInterfaceItem, 1);

				/* fill in struct */
				lti->type = (LinkInterfaceItemType) j;

				/* get href=.. element */
				NS_NAMED_LITERAL_STRING(attr_href, "href");
				nsAutoString value;
				linkElement->GetAttribute (attr_href, value);

				if (value.IsEmpty())
				{
					g_free (lti);
					continue;
				}

				/* resolve uri */
				nsCOMPtr<nsIDocument> doc = 
					do_QueryInterface (aDOMDocument);
				if(!doc) return NS_ERROR_FAILURE;
			
				nsCOMPtr<nsIURI> uri;
				doc->GetDocumentURL(getter_AddRefs(uri));

				const nsACString &link = NS_ConvertUCS2toUTF8(value);
				nsCAutoString href;
				result = uri->Resolve (link, href);
				if (NS_FAILED (result)) return NS_ERROR_FAILURE;
				lti->href = g_strdup (href.get());
		
				/* append to list of items */
				*list = g_list_append (*list, lti);
		
				/* get optional title=... element */
				NS_NAMED_LITERAL_STRING(attr_title, "title");
				linkElement->GetAttribute (attr_title, value);
				if (value.IsEmpty()) continue;

				const nsACString &title = NS_ConvertUCS2toUTF8 (value);
				lti->title = gul_string_strip_newline (PromiseFlatCString(title).get());
			}
		}
	}
#endif
	return NS_OK;
}

nsresult GaleonWrapper::GetRealURL (nsCString &ret)
{
	nsresult result;

	nsCOMPtr<nsIDocShell> DocShell;
	result = GetDocShell (getter_AddRefs(DocShell));
	if (NS_FAILED(result) || !DocShell) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIWebNavigation> ContentNav = do_QueryInterface (DocShell,
								   &result);
	if (!ContentNav) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIURI> uri;
	result = ContentNav->GetCurrentURI (getter_AddRefs(uri));
	if (!NS_SUCCEEDED(result) || (!uri)) return NS_ERROR_FAILURE;

	result = uri->GetSpec(ret);
	if (!NS_SUCCEEDED(result) || ret.IsEmpty()) return NS_ERROR_FAILURE;

	return NS_OK;
}

nsresult GaleonWrapper::SelectAll (void)
{
	g_return_val_if_fail (mWebBrowser, NS_ERROR_FAILURE);

	nsCOMPtr<nsIClipboardCommands> clipboard (do_GetInterface(mWebBrowser));
	return clipboard->SelectAll ();
}

nsresult GaleonWrapper::ScrollUp (void)
{
	nsresult result;

	nsCOMPtr<nsIDOMWindow> DOMWindow;
	result = GetFocusedDOMWindow (getter_AddRefs(DOMWindow));
	if (NS_FAILED(result) || !DOMWindow) return NS_ERROR_FAILURE;

	DOMWindow->ScrollByLines(-1);

	return NS_OK;
}

nsresult GaleonWrapper::ScrollDown (void)
{
	nsresult result;

	nsCOMPtr<nsIDOMWindow> DOMWindow;
	result = GetFocusedDOMWindow (getter_AddRefs(DOMWindow));
	if (NS_FAILED(result) || !DOMWindow) return NS_ERROR_FAILURE;

	DOMWindow->ScrollByLines(1);
	
	return NS_OK;
}

nsresult GaleonWrapper::ScrollLeft (void)
{
	nsresult result;

	nsCOMPtr<nsIDOMWindow> DOMWindow;
	result = GetFocusedDOMWindow (getter_AddRefs(DOMWindow));
	if (NS_FAILED(result) || !DOMWindow) return NS_ERROR_FAILURE;

	DOMWindow->ScrollBy(-16, 0);
	
	return NS_OK;
}

nsresult GaleonWrapper::ScrollRight (void)
{
	nsresult result;

	nsCOMPtr<nsIDOMWindow> DOMWindow;
	result = GetFocusedDOMWindow (getter_AddRefs(DOMWindow));
	if (NS_FAILED(result) || !DOMWindow) return NS_ERROR_FAILURE;

	DOMWindow->ScrollBy(16, 0);
	
	return NS_OK;
}

nsresult GaleonWrapper::FineScroll (int horiz, int vert)
{
	nsresult result;

	nsCOMPtr<nsIDOMWindow> DOMWindow;
	result = GetFocusedDOMWindow (getter_AddRefs(DOMWindow));
	if (NS_FAILED(result) || !DOMWindow) return NS_ERROR_FAILURE;

	DOMWindow->ScrollBy(horiz, vert);
	
	return NS_OK;
}

nsresult GaleonWrapper::SetChannel (nsIChannel *aChannel)
{
	mChannel = aChannel;
	return NS_OK;
}


nsresult GaleonWrapper::GetSecurityTooltip (char **description)
{
	nsresult result;

	/* Will only happen if the embed has not initialised yet */
	if (!mChannel) return NS_ERROR_FAILURE;

        nsCOMPtr<nsISupports> info;
        result = mChannel->GetSecurityInfo (getter_AddRefs(info));
        if (NS_FAILED (result)) return result;

	*description = NULL;
	if (info)
	{
		nsCOMPtr<nsITransportSecurityInfo> secInfo(do_QueryInterface(info));
		if (!secInfo) return NS_ERROR_FAILURE;

		nsXPIDLString tooltip;
		result = secInfo->GetShortSecurityDescription(getter_Copies(tooltip));
		if (NS_FAILED (result)) return NS_ERROR_FAILURE;

		if (tooltip)
		{
			*description = g_strdup (NS_ConvertUCS2toUTF8(tooltip).get());
		}
	}
	return NS_OK;
}

#ifdef HAVE_MOZILLA_PSM

nsresult GaleonWrapper::GetSSLStatus (nsISSLStatus **aSSLStatus)
{
	g_return_val_if_fail (mChannel, NS_ERROR_FAILURE);

	nsCOMPtr<nsISupports> info;
	mChannel->GetSecurityInfo(getter_AddRefs(info));

	nsCOMPtr<nsISSLStatusProvider> sp = do_QueryInterface(info);
	if (!sp) return NS_ERROR_FAILURE;

	nsCOMPtr<nsISSLStatus> SSLStatus;
	sp->GetSSLStatus(getter_AddRefs(SSLStatus));
	if (!SSLStatus) return NS_ERROR_FAILURE;

	NS_ADDREF(*aSSLStatus = SSLStatus);
	return NS_OK;
}

#endif

nsresult GaleonWrapper::ShowPageCertificate ()
{
#ifndef HAVE_MOZILLA_PSM
	return NS_ERROR_NOT_IMPLEMENTED;
#else

	nsresult result;
	nsCOMPtr<nsISSLStatus> SSLStatus;
	result = GetSSLStatus (getter_AddRefs(SSLStatus));
	if (NS_FAILED (result)) return result;
	
	nsCOMPtr<nsIX509Cert> serverCert;
	SSLStatus->GetServerCert (getter_AddRefs (serverCert));

	nsCOMPtr<nsICertificateDialogs> certDialogs =
		do_GetService (NS_CERTIFICATEDIALOGS_CONTRACTID, &result);
	g_return_val_if_fail (NS_SUCCEEDED (result), result);
	
	certDialogs->ViewCert (NULL, serverCert);

	return NS_OK;
#endif
}

nsresult GaleonWrapper::GetPageSecurityInfo (EmbedPageProperties *props)
{
	props->cipher_name = 0;
	props->key_length = 0;
	props->secret_key_length = 0;
	props->cert_issuer_name = 0;

	/* Will only happen if the embed has not initialised yet */
	if (!mChannel) return NS_OK;

#ifndef HAVE_MOZILLA_PSM
	return NS_OK;
#else
	nsresult result;

	nsCOMPtr<nsISSLStatus> SSLStatus;
	result = GetSSLStatus (getter_AddRefs(SSLStatus));
	if (NS_FAILED (result)) return result;

	nsXPIDLCString cipher;
	
	SSLStatus->GetCipherName (getter_Copies (cipher));
	props->cipher_name = g_strdup (cipher.get());

	PRUint32 keyLength;

	SSLStatus->GetKeyLength (&keyLength);
	props->key_length = keyLength;

	SSLStatus->GetSecretKeyLength (&keyLength);
	props->secret_key_length = keyLength;
	
	nsCOMPtr<nsIX509Cert> cert;
	SSLStatus->GetServerCert (getter_AddRefs (cert));

	nsAutoString value;
	cert->GetIssuerOrganization (value);
	if (!value.Length())
	{
		cert->GetIssuerName (value);
	}
	props->cert_issuer_name = g_strdup (NS_ConvertUCS2toUTF8(value).get());

	return NS_OK;
#endif
}

nsresult GaleonWrapper::GetCacheEntryDescriptor(const nsAString &aKey,
						nsICacheEntryDescriptor **aCacheEntryDescriptor)
{
	nsresult rv;

	nsCOMPtr<nsICacheService> cacheService =
		do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
	if (NS_FAILED(rv) || !cacheService) return NS_ERROR_FAILURE;


	nsCOMPtr<nsICacheSession> cacheSession;
	rv = cacheService->CreateSession("HTTP",
					 nsICache::STORE_ANYWHERE,
					 PR_TRUE,
					 getter_AddRefs(cacheSession));
	if(NS_FAILED(rv) || !cacheSession) return NS_ERROR_FAILURE;

	cacheSession->SetDoomEntriesIfExpired(PR_FALSE);
		
	nsCOMPtr<nsICacheEntryDescriptor> cacheEntryDescriptor;
	rv = cacheSession->OpenCacheEntry(NS_ConvertUCS2toUTF8(aKey).get(),
					  nsICache::ACCESS_READ, PR_FALSE,
					  aCacheEntryDescriptor);

	if (NS_FAILED(rv) || !aCacheEntryDescriptor)
	{
		rv = cacheService->CreateSession("FTP",
						 nsICache::STORE_ANYWHERE,
						 PR_TRUE,
						 getter_AddRefs(cacheSession));
		if(NS_FAILED(rv) || !cacheSession) return NS_ERROR_FAILURE;

		cacheSession->SetDoomEntriesIfExpired (PR_FALSE);
		
		return cacheSession->OpenCacheEntry(NS_ConvertUCS2toUTF8(aKey).get(),
						    nsICache::ACCESS_READ, PR_FALSE,
						    aCacheEntryDescriptor);
	}

	return NS_OK;
}

nsresult GaleonWrapper::GetPageProperties (EmbedPageProperties *props)
{
	nsresult result;

	nsCOMPtr<nsIDOMDocument> DOMDocument;

	result = GetDOMDocument (getter_AddRefs(DOMDocument));
	if (NS_FAILED(result) || !DOMDocument) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMNSDocument> doc = do_QueryInterface(DOMDocument);
	if(!doc) return NS_ERROR_FAILURE;

	nsAutoString value;

	doc->GetLastModified(value);
	nsTime last_modified (NS_ConvertUCS2toUTF8(value).get(), PR_TRUE);
	LL_DIV (props->modification_time, last_modified, PR_USEC_PER_SEC);

	doc->GetContentType(value);
	props->content_type = g_strdup (NS_ConvertUCS2toUTF8(value).get());

	doc->GetCharacterSet(value);
	props->encoding = g_strdup (NS_ConvertUCS2toUTF8(value).get());

	/* This might not work with XUL pages (e.g. about:config) */
	nsCOMPtr<nsIDOMHTMLDocument> HTMLDoc = do_QueryInterface(DOMDocument);
	if (HTMLDoc)
	{
		HTMLDoc->GetReferrer (value);
		if (value.Length())
		{
			props->referring_url = g_strdup (NS_ConvertUCS2toUTF8(value).get());
		}
	}

	/* This might not work with XUL pages (e.g. about:config) */
	nsCOMPtr<nsIDOMNSHTMLDocument> NSHTMLDoc = do_QueryInterface (DOMDocument);
	if (NSHTMLDoc)
	{
		NSHTMLDoc->GetCompatMode (value);
		props->rendering_mode = (value == NS_LITERAL_STRING("CSS1Compat")) ?
		                 EMBED_RENDER_STANDARDS :
		                 EMBED_RENDER_QUIRKS;
	}

	/* Get the URL so we can look in the cache for the page */
	nsCOMPtr<nsIDOMLocation> DOMLocation;
	result = doc->GetLocation (getter_AddRefs (DOMLocation));
	if (NS_FAILED (result)) return result;

	nsAutoString url;
	DOMLocation->ToString (url);

	nsCOMPtr<nsICacheEntryDescriptor> cacheEntryDescriptor;
	result = GetCacheEntryDescriptor(url, getter_AddRefs(cacheEntryDescriptor));

	if (NS_SUCCEEDED(result) && cacheEntryDescriptor)
	{
		PRUint32 expiry = 0, dataSize = 0;
		nsXPIDLCString source;

		cacheEntryDescriptor->GetExpirationTime (&expiry);
		cacheEntryDescriptor->GetDataSize (&dataSize);
		cacheEntryDescriptor->GetDeviceID (getter_Copies (source));

		props->expiration_time = expiry;
		props->size = dataSize;

		if (source.Equals("disk"))
		{
			props->page_source = EMBED_SOURCE_DISK_CACHE;
		}
		else if (source.Equals("memory"))
		{
			props->page_source = EMBED_SOURCE_MEMORY_CACHE;
		}
		else
		{
			props->page_source = EMBED_SOURCE_UNKNOWN_CACHE;
		}
	}
	else
	{
		props->page_source = EMBED_SOURCE_NOT_CACHED;
		props->size = -1;
		props->expiration_time = 0;
	}

	GetPageSecurityInfo (props);

	GetMetaTags(&(props->metatags));
	GetForms(&(props->forms));
	GetLinks(&(props->links));
	GetImages(&(props->images));

	return NS_OK;
}

nsresult GaleonWrapper::GetMetaTags(GList **ret)
{
	nsresult result;

	nsCOMPtr<nsIDOMDocument> doc;
	result = GetDOMDocument (getter_AddRefs(doc));
	if (NS_FAILED(result) || !doc) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMNodeList> nodes;
	result = doc->GetElementsByTagName(NS_LITERAL_STRING("meta"),
					   getter_AddRefs(nodes));
	if (NS_FAILED(result) || !nodes) return NS_ERROR_FAILURE;

	PRUint32 count(0);
	result = nodes->GetLength(&count);
	if (NS_FAILED(result)) return NS_ERROR_FAILURE;

	for (PRUint32 i = 0; i < count; i++)
	{
		nsCOMPtr<nsIDOMNode> node;
		result = nodes->Item(i, getter_AddRefs(node));
		if (NS_FAILED(result) || !node) continue;

		nsCOMPtr<nsIDOMHTMLMetaElement> element;
		element = do_QueryInterface(node, &result);
		if (NS_FAILED(result) || !element) continue;
		
		EmbedPageMetaTag *tag = g_new0(EmbedPageMetaTag, 1);
		
		nsAutoString tmp;

		result = element->GetName(tmp);
		if (NS_FAILED(result) || tmp.IsEmpty())
		{
			result = element->GetHttpEquiv(tmp);
			if (NS_FAILED(result) || tmp.IsEmpty())
				continue;
		}
		tag->name = g_strdup(NS_ConvertUCS2toUTF8(tmp).get());

		result = element->GetContent(tmp);
		if (NS_SUCCEEDED(result))
		{
			tag->content =
				gul_string_strip_newline(NS_ConvertUCS2toUTF8(tmp).get());
		}

		*ret = g_list_append (*ret, tag);
	}
	return NS_OK;
}

nsresult GaleonWrapper::GetImages(GList **ret)
{
	nsresult rv;
	GHashTable *hash = g_hash_table_new(g_str_hash, g_str_equal);

	nsCOMPtr<nsIDOMDocument> doc;
	rv = GetDOMDocument(getter_AddRefs(doc));
	if (NS_FAILED(rv) || !doc) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMHTMLDocument> htmlDoc;
	htmlDoc = do_QueryInterface(doc, &rv);
	if(NS_FAILED(rv) || !doc) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMHTMLCollection> nodes;
	htmlDoc->GetImages(getter_AddRefs(nodes));

	PRUint32 count(0);
	nodes->GetLength(&count);
	for (PRUint32 i = 0; i < count; i++)
	{
		nsCOMPtr<nsIDOMNode> node;
		rv = nodes->Item(i, getter_AddRefs(node));
		if (NS_FAILED(rv) || !node) continue;

		nsCOMPtr<nsIDOMHTMLImageElement> element;
		element = do_QueryInterface(node, &rv);
		if (NS_FAILED(rv) || !element) continue;

		EmbedPageImage *image = g_new0(EmbedPageImage, 1);
		
		nsAutoString tmp;
		rv = element->GetSrc(tmp);
		if (NS_SUCCEEDED(rv))
		{
			const nsACString &c = NS_ConvertUCS2toUTF8(tmp);
			if (g_hash_table_lookup(hash, PromiseFlatCString(c).get()))
			{
				g_free (image);
				continue;
			}
			image->url = g_strdup(PromiseFlatCString(c).get());
			g_hash_table_insert(hash, image->url,
					    GINT_TO_POINTER(TRUE));
		}

		rv = element->GetAlt(tmp);
		if (NS_SUCCEEDED(rv))
		{
			image->alt =
				gul_string_strip_newline(NS_ConvertUCS2toUTF8(tmp).get());
		}
		rv = element->GetTitle(tmp);
		if (NS_SUCCEEDED(rv))
		{
			image->title =
				gul_string_strip_newline(NS_ConvertUCS2toUTF8(tmp).get());
		}
		rv = element->GetWidth(&(image->width));
		rv = element->GetHeight(&(image->height));

		*ret = g_list_append(*ret, image);
	}
	g_hash_table_destroy (hash);

	return NS_OK;
}

nsresult GaleonWrapper::GetForms (GList **ret)
{
	nsresult rv;

	nsCOMPtr<nsIDOMDocument> doc;
	rv = GetDOMDocument (getter_AddRefs(doc));
	if (NS_FAILED(rv) || !doc) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMHTMLDocument> htmlDoc;
	htmlDoc = do_QueryInterface(doc, &rv);
	if (NS_FAILED(rv) || ! htmlDoc) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMHTMLCollection> nodes;
	rv = htmlDoc->GetForms(getter_AddRefs(nodes));
	if (NS_FAILED(rv) || !nodes) return NS_ERROR_FAILURE;

	PRUint32 count(0);
	rv = nodes->GetLength(&count);
	if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

	for (PRUint32 i = 0; i < count; i++)
	{
		nsCOMPtr<nsIDOMNode> node;
		rv = nodes->Item(i, getter_AddRefs(node));
		if (NS_FAILED(rv) || !node) continue;

		nsCOMPtr<nsIDOMHTMLFormElement> element;
		element = do_QueryInterface(node, &rv);
		if (NS_FAILED(rv) || !element) continue;
		
		EmbedPageForm *form = g_new0(EmbedPageForm, 1);
		
		nsAutoString tmp;

		rv = element->GetAction(tmp);
		if (NS_SUCCEEDED(rv) && tmp.Length())
		{
			nsCOMPtr<nsIDocument> document;
			document = do_QueryInterface(doc, &rv);
			if (NS_FAILED(rv))
			{
				g_free(form);
				continue;
			}

#if MOZILLA_SNAPSHOT > 13
			nsIURI *uri = document->GetDocumentURI();
#elif MOZILLA_SNAPSHOT > 11
			nsIURI *uri = document->GetDocumentURL();
#else
			nsCOMPtr<nsIURI> uri;
			rv = document->GetDocumentURL (getter_AddRefs(uri));
			if (NS_FAILED(rv)) return rv;
#endif

			const nsACString &s = NS_ConvertUCS2toUTF8(tmp);
			nsCAutoString c;
			rv = uri->Resolve(s, c);

			form->action = c.Length() ?
				       g_strdup (c.get()) :
				       g_strdup (PromiseFlatCString(s).get());
		}

		rv = element->GetName(tmp);
		if (NS_SUCCEEDED(rv) && tmp.Length())
		{
			form->name = g_strdup(NS_ConvertUCS2toUTF8(tmp).get());
		}

		rv = element->GetMethod(tmp);
		if (NS_SUCCEEDED(rv) && tmp.Length())
		{
			form->method = g_strdup(NS_ConvertUCS2toUTF8(tmp).get());
		}

		*ret = g_list_append (*ret, form);
	}
	return NS_OK;
}

/*
 * This is a template function because we are calling
 * methods with identical signatures on two different
 * interfaces.
 */
template <class T>
static nsresult sProcessLinkNode(nsIDOMNode *node,
				 nsIDOMDocument *doc,
				 GList **ret)
{
	nsresult rv;

	T element;
	element = do_QueryInterface(node, &rv);
	if (NS_FAILED(rv) || !element) return NS_ERROR_FAILURE;

	nsAutoString tmp;
	rv = element->GetHref(tmp);
	if (NS_FAILED(rv) || tmp.IsEmpty()) return NS_ERROR_FAILURE;

	//Resolve relative href
	nsCOMPtr<nsIDocument> document = 
		do_QueryInterface(doc, &rv);
	if(NS_FAILED(rv) || !document) return NS_ERROR_FAILURE;
		
#if MOZILLA_SNAPSHOT > 13
	nsIURI* uri = document->GetDocumentURI();
	if (!uri) return NS_ERROR_FAILURE;
#elif MOZILLA_SNAPSHOT > 11
	nsIURI* uri = document->GetDocumentURL();
	if (!uri) return NS_ERROR_FAILURE;
#else
	nsCOMPtr<nsIURI> uri;
	rv = document->GetDocumentURL (getter_AddRefs(uri));
	if (NS_FAILED(rv) || !uri) return rv;
#endif
	const nsACString &href = NS_ConvertUCS2toUTF8(tmp);
	nsCAutoString fullHref;
	rv = uri->Resolve (href, fullHref);
	
	EmbedPageLink *link = g_new0(EmbedPageLink, 1);

	if (fullHref.Length())
	{
		link->url = g_strdup(fullHref.get());
	}
	else
	{
		link->url = g_strdup(PromiseFlatCString(href).get());
	}

	rv = element->GetTitle(tmp);
	if (NS_SUCCEEDED(rv) && tmp.Length())
	{
		link->title =
			gul_string_strip_newline(NS_ConvertUCS2toUTF8(tmp).get());
	}

	rv = element->GetRel(tmp);
	if (NS_SUCCEEDED(rv) && tmp.Length())
	{
		link->rel =
			g_strdup(NS_ConvertUCS2toUTF8(tmp).get());
	}
	if (!link->rel || strlen(link->rel) == 0)
	{
		element->GetRev(tmp);
		if (NS_SUCCEEDED(rv) && tmp.Length())
		{
			g_free(link->rel);
			link->rel =
				g_strdup(NS_ConvertUCS2toUTF8(tmp).get());
		}
	}
	
	*ret = g_list_append(*ret, link);

	return NS_OK;
}

nsresult GaleonWrapper::GetLinks (GList **ret)
{
	nsresult rv;

	nsCOMPtr<nsIDOMDocument> doc;
	rv = GetMainDOMDocument(getter_AddRefs(doc));
	if (NS_FAILED(rv) || !doc) return NS_ERROR_FAILURE;

	/* first, get a list of <link> elements */
	nsCOMPtr<nsIDOMNodeList> links;
	rv = doc->GetElementsByTagName(NS_LITERAL_STRING("link"),
				       getter_AddRefs(links));
	if (NS_FAILED(rv) || !links) return NS_ERROR_FAILURE;

	PRUint32 links_count;
	rv = links->GetLength(&links_count);
	if (NS_FAILED (rv)) return NS_ERROR_FAILURE;

	for (PRUint32 i = 0; i < links_count; i++)
	{
		nsCOMPtr<nsIDOMNode> node;
		rv = links->Item(i, getter_AddRefs(node));
		if (NS_FAILED(rv) || !node) continue;

		sProcessLinkNode<nsCOMPtr<nsIDOMHTMLLinkElement> >(node, doc, ret);
	}

	/* next, get a list of anchors */
	nsCOMPtr<nsIDOMHTMLDocument> htmlDoc;
	htmlDoc = do_QueryInterface(doc, &rv);
	if(NS_FAILED(rv) || !htmlDoc) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMHTMLCollection> anchors;
	htmlDoc->GetLinks(getter_AddRefs(anchors));

	PRUint32 anchor_count;
	anchors->GetLength(&anchor_count);
	for (PRUint32 i = 0; i < anchor_count; i++)
	{
		nsCOMPtr<nsIDOMNode> node;
		rv = anchors->Item(i, getter_AddRefs(node));
		if (NS_FAILED(rv) || !node) continue;

		sProcessLinkNode<nsCOMPtr<nsIDOMHTMLAnchorElement> >(node, doc, ret);
	}

	return NS_OK;
}

nsresult GaleonWrapper::EvaluateJS (char *script)
{
	nsresult rv;

	nsCOMPtr<nsIDOMWindow> DOMWindow;
	rv = mWebBrowser->GetContentDOMWindow(getter_AddRefs(DOMWindow));

	nsCOMPtr<nsIScriptGlobalObject> globalObject;
	globalObject = do_QueryInterface (DOMWindow);
	if (!globalObject) return NS_ERROR_FAILURE;

#if MOZILLA_SNAPSHOT > 13
	nsIScriptContext *context = globalObject->GetContext();
	if (!context) return NS_ERROR_FAILURE;
#else
	nsCOMPtr<nsIScriptContext> context;
	rv = globalObject->GetContext(getter_AddRefs(context));
	if (NS_FAILED(rv) || !context) {
		return NS_ERROR_FAILURE;
	}
#endif

	context->SetProcessingScriptTag(PR_TRUE);

	PRBool isUndefined;
	nsAutoString ret;
	const nsAString &aScript = NS_ConvertUTF8toUCS2(script);
	context->EvaluateString(aScript, nsnull, nsnull, nsnull,
				0, nsnull, 
				ret, &isUndefined);  

	context->SetProcessingScriptTag(PR_FALSE);

	return NS_OK;
}

#define NUM_TEXTFIELDS_REQUIRED	3

nsresult 
GaleonWrapper::GetDocumentHasModifiedForms (nsIDOMDocument *aDomDoc, 
					    PRUint32 *aNumTextFields,
					    PRBool *aIsModified)
{
	nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(aDomDoc);
	NS_ENSURE_TRUE (htmlDoc, NS_ERROR_FAILURE);

	nsCOMPtr<nsIDOMHTMLCollection> forms;
	htmlDoc->GetForms (getter_AddRefs (forms));
	if (!forms) return NS_OK; /* it's ok not to have any forms */

	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;

		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<nsIDOMHTMLTextAreaElement> areaElement = do_QueryInterface (domNode);
			if (areaElement)
			{
				/* Treat a text area like NUM_TEXTFIELDS_REQUIRED text boxes,
				 * that way you only need one textare for the modified
				 * flag to properly work */
				(*aNumTextFields) += NUM_TEXTFIELDS_REQUIRED;
				nsAutoString default_text, user_text;
				areaElement->GetDefaultValue (default_text);
				areaElement->GetValue (user_text);
				
				/* Mozilla Bug 218277, 195946 and others */
				default_text.ReplaceChar(0xa0, ' ');

				if (!user_text.Equals (default_text))
				{
					*aIsModified = PR_TRUE;
					return NS_OK;
				}

				continue;
			}

			nsCOMPtr<nsIDOMHTMLInputElement> inputElement = do_QueryInterface(domNode);
			if (!inputElement) continue;
	
			nsAutoString type;
			inputElement->GetType(type);

			if (type.EqualsIgnoreCase("text"))
			{
				(*aNumTextFields)++;
				nsAutoString default_text, user_text;
				PRInt32 max_length;
				inputElement->GetDefaultValue (default_text);
				inputElement->GetValue (user_text);
				inputElement->GetMaxLength (&max_length);

				/* Guard against arguably broken forms where
				 * default_text is longer than maxlength
				 * (user_text is cropped, default_text is not)
				 */
				if (default_text.Length() > (PRUint32)max_length)
				{
					default_text.Truncate (max_length);
				}

				/* Mozilla Bug 218277, 195946 and others */
				default_text.ReplaceChar(0xa0, ' ');

				if (!user_text.Equals (default_text))
				{
					*aIsModified = PR_TRUE;
				}

				if ((*aIsModified) && *aNumTextFields >= 
				    NUM_TEXTFIELDS_REQUIRED)
				{
					return NS_OK;
				}
			}
		}
	}

	return NS_OK;
}

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

	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 isModified = 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 result;
		result = GetDocumentHasModifiedForms (domDoc, &numTextFields, &isModified);
		if (NS_SUCCEEDED (result) &&
		    numTextFields >= NUM_TEXTFIELDS_REQUIRED && isModified)
		{
			*modified = PR_TRUE;
			break;
		}
	}

	return NS_OK;
}
nsresult GaleonWrapper::PushTargetDocument (nsIDOMDocument *domDoc)
{
	mTargetDocument = domDoc;

	return NS_OK;
}

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

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////
NS_IMPL_ISUPPORTS1(GDOMEventListener, nsIDOMEventListener)

GDOMEventListener::GDOMEventListener() : mEmbed(0)
{
}

GDOMEventListener::~GDOMEventListener()
{
}

nsresult
GDOMEventListener::Init(GaleonEmbed *aEmbed)
{
	mEmbed = aEmbed;
	return NS_OK;
}

static nsresult
ResolveURI (nsIDocument* doc, const nsAString& relative_uri,
	    nsACString& full_uri)
{
	g_return_val_if_fail (doc != NULL, NS_ERROR_FAILURE);

#if MOZILLA_SNAPSHOT > 13
	nsIURI *docUri = doc->GetDocumentURI ();
	if (!docUri) return NS_ERROR_FAILURE;
#elif MOZILLA_SNAPSHOT > 11
	nsIURI *docUri = doc->GetDocumentURL ();
	if (!docUri) return NS_ERROR_FAILURE;
#else
	nsresult rv;
	nsCOMPtr<nsIURI> docUri;
	rv = doc->GetDocumentURL (getter_AddRefs(docUri));
	if (NS_FAILED(rv)) return rv;
#endif
	
	const nsACString &link = NS_ConvertUCS2toUTF8 (relative_uri);
	return docUri->Resolve (link, full_uri);
}

NS_IMETHODIMP
GDOMEventListener::HandleEvent (nsIDOMEvent* aDOMEvent)
{
	nsresult rv;

	nsCOMPtr<nsIDOMEventTarget> target;
	rv = aDOMEvent->GetTarget(getter_AddRefs(target));
	if (NS_FAILED(rv)) return rv;

	nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(target, &rv);
	if (NS_FAILED(rv)) return rv;

	nsAutoString value;
	rv = elem->GetAttribute(NS_LITERAL_STRING("rel"), value);
	if (NS_FAILED(rv)) return rv;

	if (value.EqualsIgnoreCase("SHORTCUT ICON") ||
	    value.EqualsIgnoreCase("ICON"))
	{
		rv = elem->GetAttribute (NS_LITERAL_STRING("href"), value);
		if (NS_FAILED(rv)) return rv;
		if (value.IsEmpty()) return NS_ERROR_FAILURE;

		nsCOMPtr<nsIDOMDocument> domDoc;
		rv = elem->GetOwnerDocument (getter_AddRefs(domDoc));
		if (NS_FAILED(rv)) return rv;

		nsCOMPtr<nsIDocument> doc = do_QueryInterface (domDoc, &rv);
		if (NS_FAILED(rv)) return rv;

		nsCAutoString favicon;
		rv = ::ResolveURI (doc, value, favicon);
		if (NS_SUCCEEDED(rv))
		{
			g_signal_emit_by_name (mEmbed, "ge_favicon", favicon.get());
		}
	}

	return NS_OK;
}

//------------------------------------------------------------------------------
NS_IMPL_ISUPPORTS1(GDOMPopupEventListener, nsIDOMEventListener)

NS_IMETHODIMP
GDOMPopupEventListener::HandleEvent (nsIDOMEvent* aDOMEvent)
{
	nsresult rv;

	nsCOMPtr<nsIDOMPopupBlockedEvent> popupEvent =
		do_QueryInterface(aDOMEvent, &rv);
	if (NS_FAILED(rv) || !popupEvent) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIURI> requesterURI;
	rv = popupEvent->GetRequestingWindowURI(getter_AddRefs(requesterURI));
	if (NS_FAILED(rv) || !popupEvent) return NS_ERROR_FAILURE;

	nsCAutoString spec;
	rv = requesterURI->GetSpec(spec);
	if (NS_FAILED(rv) || spec.IsEmpty()) return NS_ERROR_FAILURE;

	g_signal_emit_by_name (mEmbed, "ge_popupblocked", spec.get());

	return rv;
}
