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

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

// Some codes are taken from Epiphany 0.8.2
// Copyright (C) 2000-2003 Marco Pesenti Gritti

// samples of nsISelection are:
//   content/base/src/nsDocumentViewer.cpp
//   layout/base/src/nsCaret.cpp


#include <kz-mozwrapper.h>
#include <gtkmozembed_internal.h>

#include <nsIInterfaceRequestorUtils.h>
#include <nsILocalFile.h>
#include <docshell/nsIDocShell.h>
#include <docshell/nsIDocShellTreeItem.h>
#include <docshell/nsIDocShellTreeOwner.h>
#include <docshell/nsIContentViewer.h>
#include <content/nsIContent.h>
#include <nsIDOMHTMLDocument.h>
#include <nsIDOMHTMLElement.h>
#include <nsIDOMHTMLImageElement.h>
#include <nsIDOMDocumentRange.h>
#include <nsIDOMNamedNodeMap.h>
#include <nsIDOMText.h>
#include <nsIDOMNodeList.h>
#include <nsIClipboardCommands.h>
#include <nsLiteralString.h>
#include <nsString.h>
#include <nsEscape.h>
#include <nsIDOMElement.h>
#include <nsIDocument.h>
#include <nsIURI.h>
#include <nsIWebBrowserFocus.h>
#include <nsIWebPageDescriptor.h>
#include <nsIMarkupDocumentViewer.h>
#include <nsIDOMWindowInternal.h>
#include <nsIChromeEventHandler.h>
#include <nsIDocShellHistory.h>
#include <nsIScriptGlobalObject.h>
#include <nsISHistoryInternal.h>
#include <nsIHistoryEntry.h>
#include <nsICacheService.h>
#include <nsICacheSession.h>
#include <nsNetCID.h>
#include <nsIServiceManager.h>
#include <htmlparser/nsIParser.h>
#include <chardet/nsIDocumentCharsetInfo.h>
#include <nsIOutputStream.h>
#include <nsReadableUtils.h>
#include <necko/nsNetUtil.h>
#include <nsIWebBrowserPrint.h>

#include "nsUnicharUtils.h"
#include "kz-mozprogresslistener.h"
#include "utils.h"

#ifdef HAVE_CONFIG_H
#	include "config.h"
#endif /* HAVE_CONFIG_H */

#define SCRAP_IMAGE_DIR "scrap_images/"

KzMozWrapper::KzMozWrapper () :
	mWebBrowser (nsnull),
	mEventReceiver (nsnull),
	mKzMozEmbed (nsnull),
	mKzMozEventListener (nsnull),
	mSelectionWasCollapsed (PR_TRUE)
{
}


KzMozWrapper::~KzMozWrapper ()
{
}


nsresult
KzMozWrapper::Init (KzMozEmbed *kzembed)
{
	nsresult rv;

	mKzMozEmbed = kzembed;
	gtk_moz_embed_get_nsIWebBrowser(GTK_MOZ_EMBED(mKzMozEmbed),
					getter_AddRefs(mWebBrowser));
	if (!mWebBrowser) return NS_ERROR_FAILURE;

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

	nsCOMPtr<nsIDocShellHistory> dsHistory = do_QueryInterface (DocShell);
	if (!dsHistory) return NS_ERROR_FAILURE;

	mWebBrowser->GetContentDOMWindow (getter_AddRefs (mDOMWindow));

	mKzMozEventListener = new KzMozEventListener();
	
	rv = mKzMozEventListener->Init (kzembed);
	if (NS_FAILED (rv)) return NS_ERROR_FAILURE;
	GetListener();
	AttachListeners();

	return NS_OK;
}


nsresult
KzMozWrapper::GetListener (void)
{
  	if (mEventReceiver) return NS_ERROR_FAILURE;
	
  	nsCOMPtr<nsIDOMWindow> domWindowExternal;
  	mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindowExternal));
  
  	nsCOMPtr<nsIDOMWindowInternal> domWindow;
        domWindow = do_QueryInterface(domWindowExternal);
	
	nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(domWindow));
  	if (!piWin) return NS_ERROR_FAILURE;

  	nsCOMPtr<nsIChromeEventHandler> chromeHandler;
#if MOZILLA_SNAPSHOT > 15
	chromeHandler = piWin->GetChromeEventHandler();
#else
	piWin->GetChromeEventHandler(getter_AddRefs(chromeHandler));	
#endif
  	mEventReceiver = do_QueryInterface(chromeHandler);
	if (!mEventReceiver) return NS_ERROR_FAILURE;

	return NS_OK;
}


nsresult
KzMozWrapper::AttachListeners(void)
{
  	if (!mEventReceiver) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMEventTarget> target;
	target = do_QueryInterface (mEventReceiver);

	return target->AddEventListener(NS_LITERAL_STRING("DOMLinkAdded"),
			                mKzMozEventListener, PR_FALSE);
}


nsresult
KzMozWrapper::DetachListeners(void)
{
	if (!mEventReceiver) return NS_ERROR_FAILURE;
	
	nsCOMPtr<nsIDOMEventTarget> target;
	target = do_QueryInterface (mEventReceiver);

	return target->RemoveEventListener(NS_LITERAL_STRING("DOMLinkAdded"),
					   mKzMozEventListener, PR_FALSE);
}


void
KzMozWrapper::Destroy (void)
{
	DetachListeners ();
	mKzMozEmbed = nsnull;
	mWebBrowser = nsnull;
}


//
// Our own methods
//

nsresult
KzMozWrapper::GetDocShell (nsIDocShell **aDocShell)
{
        if (!mWebBrowser) return 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
KzMozWrapper::GetDocument (nsIDOMDocument **aDOMDocument)
{
	nsCOMPtr<nsIDOMDocument> domDocument;
	
	return mDOMWindow->GetDocument (aDOMDocument);
}


nsresult
KzMozWrapper::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
KzMozWrapper::GetDOMWindow (nsIDOMWindow **aDOMWindow)
{
	nsresult rv;
	
	rv = mWebBrowser->GetContentDOMWindow (aDOMWindow);
	
	return rv;
}


nsresult
KzMozWrapper::GetContentViewer (nsIContentViewer **aViewer)
{
        g_return_val_if_fail(mWebBrowser, NS_ERROR_FAILURE);

        nsCOMPtr<nsIDocShell> ourDocShell(do_GetInterface(mWebBrowser));
        NS_ENSURE_TRUE(ourDocShell, NS_ERROR_FAILURE);
        return ourDocShell->GetContentViewer(aViewer);
}


/* this function picked from galeon-1.3.11a. */
nsresult
KzMozWrapper::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
KzMozWrapper::GetSelection (nsISelection **selection)
{
	if (!mDOMWindow) return NS_ERROR_FAILURE;
	return mDOMWindow->GetSelection(selection);
}


nsresult
KzMozWrapper::GetSelectionSource (nsISelection *selection, 
			 	  PRBool useLocalImage,
				  nsAString &string)
{
	nsCOMPtr<nsIDOMNode> parentNode;
	nsCOMPtr<nsIDOMRange> range;

	selection->GetRangeAt(0, getter_AddRefs(range));
	if (!range)
		return NS_ERROR_FAILURE;
	range->GetCommonAncestorContainer(getter_AddRefs(parentNode));

	HTMLSourceFromNode(parentNode, selection, range, useLocalImage, string);

	return NS_OK;
}


nsresult
KzMozWrapper::GetBodyString (nsAString &string)
{
	nsresult rv;
	nsCOMPtr<nsIDOMDocument> domDoc;
        rv = GetMainDomDocument(getter_AddRefs(domDoc));
        if (NS_FAILED(rv) || !domDoc) return NS_ERROR_FAILURE;
	
	nsCOMPtr<nsIContent> rootContent;
	nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(domDoc);
	if (htmlDoc)
	{
		nsCOMPtr<nsIDOMHTMLElement> bodyElement;
		htmlDoc->GetBody(getter_AddRefs(bodyElement));
		rootContent = do_QueryInterface(bodyElement);
	}
	//if (!rootContent)
	//	rootContent = domDoc->GetRootContent();
	
	nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootContent));
	nsCOMPtr<nsIDOMDocumentRange> docRange = do_QueryInterface(domDoc);
	if (!docRange)
		return NS_ERROR_FAILURE;

	nsCOMPtr<nsIDOMRange> range;
	docRange->CreateRange(getter_AddRefs(range));
	if (!range)
		return NS_ERROR_FAILURE;

#if MOZILLA_SNAPSHOT > 11
	PRUint32 childcount;
	childcount = rootContent->GetChildCount();
#else
	PRInt32 childcount;
	rv = rootContent->ChildCount(childcount);
#endif
	range->SetStart(rootNode,0);
	range->SetEnd(rootNode,childcount);
	range->ToString(string);
	
	return NS_OK;
}


nsresult
KzMozWrapper::CanCutSelection (PRBool *result)
{
	nsCOMPtr<nsIClipboardCommands> clipboard(do_GetInterface(mWebBrowser));
	return clipboard->CanCutSelection(result);
}


nsresult 
KzMozWrapper::CanCopySelection (PRBool *result)
{
	nsCOMPtr<nsIClipboardCommands> clipboard(do_GetInterface(mWebBrowser));
	return clipboard->CanCopySelection(result);
}


nsresult
KzMozWrapper::CanPaste (PRBool *result)
{
	nsCOMPtr<nsIClipboardCommands> clipboard(do_GetInterface(mWebBrowser));
	return clipboard->CanPaste(result);
}


nsresult
KzMozWrapper::CutSelection (void)
{
	nsCOMPtr<nsIClipboardCommands> clipboard(do_GetInterface(mWebBrowser));
	return clipboard->CutSelection();
}


nsresult
KzMozWrapper::CopySelection (void)
{
	nsCOMPtr<nsIClipboardCommands> clipboard(do_GetInterface(mWebBrowser));
	return clipboard->CopySelection();
}


nsresult
KzMozWrapper::Paste (void)
{
	nsCOMPtr<nsIClipboardCommands> clipboard(do_GetInterface(mWebBrowser));
	return clipboard->Paste();
}


nsresult
KzMozWrapper::SelectAll (void)
{
	nsCOMPtr<nsIClipboardCommands> clipboard(do_GetInterface(mWebBrowser));
	return clipboard->SelectAll();
}


/* this function picked from galeon-1.3.11a. */
nsresult
KzMozWrapper::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;
}


/* this function picked from galeon-1.3.11a. */
nsresult
KzMozWrapper::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 > 12
	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;
}


/* this function picked from galeon-1.3.11a. */
nsresult
KzMozWrapper::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);
}


/* this function picked from galeon-1.3.11a. */
nsresult
KzMozWrapper::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);
}


/* this function picked from galeon-1.3.11a. */
nsresult
KzMozWrapper::CopyHistoryTo (KzMozWrapper *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;
}


// picked from galeon-1.3.11a 
nsresult
KzMozWrapper::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;
}


// picked from galeon-1.3.11a 
nsresult
KzMozWrapper::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;
}

// picked from galeon-1.3.11a 
nsresult
KzMozWrapper::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
KzMozWrapper::GetDocumentUrl (nsCString &url)
{
	nsresult result;
	nsCOMPtr<nsIDOMWindow> domWindow;
	nsCOMPtr<nsIDOMDocument> DOMDocument;

	mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));

	result = domWindow->GetDocument(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 > 12
	nsIURI *uri;
	uri = doc->GetDocumentURI();
#elif MOZILLA_SNAPSHOT > 11
	nsIURI *uri;
	uri = doc->GetDocumentURL();
#else
	nsCOMPtr<nsIURI> uri;
	doc->GetDocumentURL(getter_AddRefs(uri));
#endif

	return uri->GetSpec(url);
}


nsresult
KzMozWrapper::ForceEncoding (const char *encoding)
{
        nsresult result;

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

        nsCOMPtr<nsIMarkupDocumentViewer> mdv = do_QueryInterface(contentViewer);
        if (!mdv) return NS_ERROR_FAILURE;

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

        return result;
}

/*
 * This function is derived from galeon-1.3.12(mozilla/GaleonWrapper.cpp).
 *  Copyright (C) 2000 Marco Pesenti Gritti
 */

nsresult 
KzMozWrapper::GetEncoding (char **encoding, PRBool &forced)
{
	nsresult result;

	nsCOMPtr<nsIDOMDocument> domDoc;
	result = GetDocument (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;

	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)
	{
		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())
		{
			forced = TRUE;
		}
	}

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

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

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

	*encoding = g_strdup (NS_ConvertUCS2toUTF8(charsetEnc).get());
#endif

	return NS_OK;
}


nsresult
KzMozWrapper::ResolveURI (const nsAString &origURI, nsACString &URI)
{
        nsresult result;
	nsCOMPtr<nsIDOMDocument> domDoc;
	GetMainDomDocument(getter_AddRefs(domDoc));

	nsIURI *baseURI;
	nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
#if MOZILLA_SNAPSHOT > 12
	baseURI = doc->GetBaseURI();
#elif MOZILLA_SNAPSHOT > 11
	baseURI = doc->GetBaseURL();
#elif MOZILLA_SNAPSHOT > 9
	result = doc->GetBaseURL(&baseURI);
#else
	result = doc->GetBaseURL(baseURI);
#endif	
	result = baseURI->Resolve(NS_ConvertUCS2toUTF8(origURI), URI);
	
	return result;
}


void
KzMozWrapper::HTMLSourceFromNode(nsIDOMNode *node,
				 nsISelection *selection,
				 nsIDOMRange *range,
				 PRBool useLocalImage,
				 nsAString &string)
{
	nsAutoString tagname;
	NS_NAMED_LITERAL_STRING(lt, "<");
	NS_NAMED_LITERAL_STRING(gt, ">");
	NS_NAMED_LITERAL_STRING(eq, "=");
	NS_NAMED_LITERAL_STRING(dq, "\"");
	NS_NAMED_LITERAL_STRING(sp, " ");
	NS_NAMED_LITERAL_STRING(sl, "/");

	PRBool contains;
	selection->ContainsNode(node, PR_TRUE, &contains);
	// if node is not contained in seletion, ignore.
	if(!contains)
		return;

	nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(node);
	if (element)
	{
		element->GetTagName(tagname);
		if (tagname.EqualsIgnoreCase("body"))
			tagname.AssignWithConversion("div");
		ToLowerCase(tagname);
		string += lt + tagname;
	
		nsCOMPtr<nsIDOMNamedNodeMap> attrs;
		node->GetAttributes(getter_AddRefs(attrs));
		if (attrs)
		{
			PRUint32 index, length;
			attrs->GetLength(&length);
			for (index = 0; index < length; index++)
			{
				nsCOMPtr<nsIDOMNode> attr;
				attrs->Item(index, getter_AddRefs(attr));
				nsAutoString name, value;
				attr->GetNodeName(name);
				attr->GetNodeValue(value);
				value.Assign(nsEscapeHTML2(value.get(),
							   value.Length()));
				
				string += sp + name + eq;

				// convert img src address to local file name
				if (tagname.EqualsIgnoreCase("img") && 
				    name.EqualsWithConversion("src"))
				{
					nsCAutoString uri; 
					nsAutoString localfile;
					ResolveURI(value, uri);
					MoveImageToLocal(uri, localfile);
					
					string += dq + localfile + dq;
				}
				else if (tagname.EqualsIgnoreCase("a") && 
					 name.EqualsWithConversion("href"))
				{
					nsCAutoString uri;
					ResolveURI(value, uri);
					string += dq + NS_ConvertUTF8toUCS2(uri) + dq;
				}
				else
				{
					string += dq + value + dq;
				}
			}
		}
		// img,hr,br,input -> <img .. />,<hr />, <br />, <input .. /> 
		if (tagname.EqualsIgnoreCase("img") ||
		    tagname.EqualsIgnoreCase("hr") ||
		    tagname.EqualsIgnoreCase("br") ||
		    tagname.EqualsIgnoreCase("input"))
			string += sp + sl + gt;
		else
			string += gt;
	}
	
	nsAutoString text;
	nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(node);
	if (textNode) // if it's a text node, get the text
	{
		nsCOMPtr<nsIDOMNode> startNode;
		nsCOMPtr<nsIDOMNode> endNode;

		range->GetStartContainer(getter_AddRefs(startNode));
		range->GetEndContainer(getter_AddRefs(endNode));
		
		if (node == startNode && node == endNode)
		{
			PRInt32 startOffset, endOffset;
			range->GetStartOffset(&startOffset);
			range->GetEndOffset(&endOffset);
			textNode->SubstringData(startOffset,
						endOffset-startOffset,
						text);
		}
		else if (node == startNode)
		{
			PRInt32 startOffset;
			PRUint32 strLength;
			textNode->GetLength(&strLength);
			range->GetStartOffset(&startOffset);
			textNode->SubstringData(startOffset,
						strLength-startOffset,
						text);
		}
		else if (node == endNode)
		{
			PRInt32 endOffset;
			range->GetEndOffset(&endOffset);
			textNode->SubstringData(0,
						endOffset,
						text);
		}
		else 
		{
			textNode->GetData(text);
		}
		text.Assign(nsEscapeHTML2(text.get(),
					  text.Length()));
				
		string += text;
	}

	nsCOMPtr<nsIDOMNodeList> childNodeList;
	node->GetChildNodes(getter_AddRefs(childNodeList));
	if (childNodeList)
	{
		PRUint32 index, length;
		childNodeList->GetLength(&length);
		for (index = 0; index < length; index++)
		{
			nsCOMPtr<nsIDOMNode> child;
			childNodeList->Item(index, getter_AddRefs(child));
			selection->ContainsNode(child, PR_TRUE, &contains);
			if (contains)
				HTMLSourceFromNode(child, 
						   selection,
						   range,
						   useLocalImage,
						   string);
		}
	}

	if (element)
	{
		if (!tagname.EqualsIgnoreCase("br") &&
		    !tagname.EqualsIgnoreCase("hr") &&
		    !tagname.EqualsIgnoreCase("input") &&
		    !tagname.EqualsIgnoreCase("img"))
			string += lt + sl + tagname + gt;
	}
}


// this function is picked from galeon-1.3.13
nsresult
KzMozWrapper::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,
					 nsICache::STREAM_BASED,
					 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,
					  nsICache::NON_BLOCKING,
					  aCacheEntryDescriptor);

	if (NS_FAILED(rv) || !aCacheEntryDescriptor)
	{
		rv = cacheService->CreateSession("FTP",
						 nsICache::STORE_ANYWHERE,
						 nsICache::STREAM_BASED,
						 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,
						    nsICache::NON_BLOCKING,
						    aCacheEntryDescriptor);
	}

	return NS_OK;
}


void
KzMozWrapper::MoveImageToLocal (const nsACString &URI, nsAString &LocalFile)
{
	nsresult rv;

	nsCOMPtr<nsICacheEntryDescriptor> cacheEntryDescriptor;
	rv = GetCacheEntryDescriptor(NS_ConvertUTF8toUCS2(URI),
				     getter_AddRefs(cacheEntryDescriptor));

	if (NS_SUCCEEDED(rv) && cacheEntryDescriptor)
	{
		PRUint32 dataSize = 0;
		nsCOMPtr<nsIInputStream> inStream;

		gchar *dir, *buf;
		gchar *uri = ToNewCString(URI);
		gchar *filename = create_filename_from_uri(uri);

		dir = g_strconcat(g_get_home_dir(),
				  "/.kazehakase/",
				  SCRAP_IMAGE_DIR,
				  filename,
				  NULL);

		cacheEntryDescriptor->GetDataSize(&dataSize);
		cacheEntryDescriptor->OpenInputStream(0, getter_AddRefs(inStream));

		buf = g_new0(gchar, dataSize);

		inStream->Read(buf, dataSize, &rv);
		inStream->Close();
		
		LocalFile.Assign(NS_ConvertUTF8toUCS2(SCRAP_IMAGE_DIR));
		LocalFile.Append(NS_ConvertUTF8toUCS2(filename));

		make_dir(SCRAP_IMAGE_DIR);
		
		nsCOMPtr<nsILocalFile> imageFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
		imageFile->InitWithPath(NS_ConvertUTF8toUCS2(dir));
		imageFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
		nsCOMPtr<nsIOutputStream> outStream;
		NS_NewLocalFileOutputStream(getter_AddRefs(outStream),
					    imageFile,
					    -1,
					    0600);
		outStream->Write(buf, dataSize, &rv);
		outStream->Close();

		g_free(filename);
		g_free(dir);
		g_free(buf);
		g_free(uri);
	}
}


nsresult
KzMozWrapper::Print (void)
{
	nsresult result;
        nsCOMPtr<nsIPrintSettings> options;

	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;

	print->GetGlobalPrintSettings(getter_AddRefs(options));
	options->SetPaperSize(nsIPrintSettings::kPaperSizeNativeData);
	
	KzMozProgressListener *aProgress = new KzMozProgressListener();
	result = print->Print(options, aProgress);

	return result;
}

nsresult
KzMozWrapper::PrintPreview (void)
{
	nsresult result;
        nsCOMPtr<nsIPrintSettings> options;

	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;

	print->GetGlobalPrintSettings(getter_AddRefs(options));
	options->SetPaperSize(nsIPrintSettings::kPaperSizeNativeData);
	result = print->PrintPreview(options, mDOMWindow, nsnull);

	return result;
}

#if 0
nsresult
KzMozWrapper::StoreHistoryFile (const nsACString &URI)
{
	nsresult rv;
	nsCOMPtr<nsICacheEntryDescriptor> cacheEntryDescriptor;
	rv = GetCacheEntryDescriptor(NS_ConvertUTF8toUCS2(URI),
				     getter_AddRefs(cacheEntryDescriptor));

	if (NS_SUCCEEDED(rv) && cacheEntryDescriptor)
	{
		PRUint32 dataSize = 0;
		nsCOMPtr<nsIInputStream> inStream;

		gchar *dir, *buf;
		gchar *uri = ToNewCString(URI);
		gchar *filename;
		filename = create_filename_with_path_from_uri(uri);
		
		if (!filename)
			filename = g_strdup("index.html");

		dir = g_strconcat(g_get_home_dir(),
				  "/.kazehakase/history/",
				  filename,
				  NULL);
		g_free(filename);
		g_free(uri);

		cacheEntryDescriptor->GetDataSize(&dataSize);
		cacheEntryDescriptor->OpenInputStream(0, getter_AddRefs(inStream));

		buf = g_new0(gchar, dataSize);

		inStream->Read(buf, dataSize, &rv);
		inStream->Close();
		
		make_dir("history");
		
		nsCOMPtr<nsILocalFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);

		rv = localFile->InitWithPath(NS_ConvertUTF8toUCS2(dir));
		g_free(dir);

		if (NS_SUCCEEDED(rv))
		{
			rv = localFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
			if (NS_SUCCEEDED(rv))
			{
				nsCOMPtr<nsIOutputStream> outStream;
				NS_NewLocalFileOutputStream(getter_AddRefs(outStream),
							    localFile,
							    -1,
							    0600);
				outStream->Write(buf, dataSize, &rv);
				outStream->Close();
			}
		}
		g_free(buf);
	}

	return rv;
}
#endif
