/*
 *  Copyright (C) 2001 Philip Langdale, Matthew Aubury
 *
 *  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.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "ProgressListener.h"

#include "gul-general.h"
#include "eel-gconf-extensions.h"
#include "prefs-strings.h"
#include "gul-gui.h"
#include "galeon-debug.h"

#include <unistd.h>
#include <libgnome/gnome-exec.h>
#include <libgnomevfs/gnome-vfs-utils.h>
#include <glib/gi18n.h>

#include <nsIWebBrowserPersist.h>
#include <nsCWebBrowserPersist.h>

#include <nsIFileURL.h>
#include "nsXPIDLString.h"
#include "nsIFTPChannel.h"
#include "nsIHttpChannel.h"
#include "nsIMIMEInfo.h"
#include "nsNetError.h"                 // for NS_BINDING_ABORTED, see FIXME
#include "nsString.h"

static const PRInt32 kInterval            = 500000;     /* in microsecs == 500ms == 0.5s */
static const PRInt32 kUnknownSizeInterval = 150000;     /* in microsecs == 150ms == 0.15s */

NS_IMPL_ADDREF(GProgressListener);
NS_IMPL_RELEASE(GProgressListener);

/*
 * If nsISupports is mapped through nsIDownload, then aborting right-click
 * downloads will crash out. I assume it's because nsIDownload and nsIProgressDialog
 * both inherit from nsITransfer.
 */
NS_INTERFACE_MAP_BEGIN(GProgressListener)
	NS_INTERFACE_MAP_ENTRY(nsIDownload)
	NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
	NS_INTERFACE_MAP_ENTRY(nsIProgressDialog)
	NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
	NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebProgressListener)
#if MOZILLA_SNAPSHOT > 15
	NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsITransfer, nsIDownload)
#endif
NS_INTERFACE_MAP_END

//---------------------------------------------------------------------------

static void
download_cancel_cb (Download *download, GProgressListener *self)
{
	self->Abort();
}

static void
download_resume_cb (Download *download, GProgressListener *self)
{
	self->Resume();
}

static void
download_pause_cb (Download *download, GProgressListener *self)
{
	self->Pause();
}

//---------------------------------------------------------------------------

GProgressListener::GProgressListener () : mPersist(nsnull),
					  mGaleonPersist(nsnull),
					  mDownload(nsnull),
					  mContentLength(-1),
					  mObserver(nsnull),
					  mMIMEInfo(nsnull)
{
}

GProgressListener::~GProgressListener ()
{
}

NS_METHOD GProgressListener::InitForPersist (nsIWebBrowserPersist *aPersist,
					      nsIURI *aURI,
					      nsIFile *aFile,
					      GaleonEmbedPersist *galeonPersist)
{
	/* fill in download details */
	mUri = aURI;
	mFile = aFile;
	mPersist = aPersist;
	mGaleonPersist = (GaleonEmbedPersist *) g_object_ref (galeonPersist);

	mStartTime = PR_Now();

	/* do remaining init */
	nsresult rv = PrivateInit ();

	/* pick up progress messages */
	mPersist->SetProgressListener (this);

	/* done */
	return rv;
}

NS_METHOD GProgressListener::PrivateInit (void)
{
	nsresult rv;

	/* setup this download */
	mCanPause            = PR_FALSE;
	mIsPaused            = PR_FALSE;
	mLastUpdate          = PR_Now ();

	EmbedPersistFlags flags = EMBED_PERSIST_SHOW_PROGRESS;
        if (mGaleonPersist)
        {
                g_object_get (mGaleonPersist, "flags", &flags, NULL);
        }
 
        if (flags & EMBED_PERSIST_SHOW_PROGRESS)
	{
		nsAutoString dest;
		nsCAutoString source;

		rv = mFile->GetPath (dest);

		rv = mUri->GetSpec (source);

		DownloaderView *dv;
		dv = galeon_embed_shell_get_downloader_view (embed_shell);
		mDownload = downloader_view_add_download (dv, source.get(),
							  NS_ConvertUCS2toUTF8(dest).get());
		g_signal_connect (G_OBJECT (mDownload), "cancel",
				  G_CALLBACK (download_cancel_cb), this);
		g_signal_connect (G_OBJECT (mDownload), "pause",
				  G_CALLBACK (download_pause_cb), this);
		g_signal_connect (G_OBJECT (mDownload), "resume",
				  G_CALLBACK (download_resume_cb), this);
		g_object_add_weak_pointer (G_OBJECT (mDownload), (void **) &mDownload);
	}

	/* done */
	return NS_OK;
}

NS_IMETHODIMP GProgressListener::Init(nsIURI *aSource,
#if MOZILLA_SNAPSHOT > 15
				      nsIURI *aTarget,
#else
                                      nsILocalFile *aTarget,
#endif
                                      const PRUnichar *aDisplayName,
				      nsIMIMEInfo *aMIMEInfo,
                                      PRInt64 aStartTime,
                                      nsIWebBrowserPersist *aPersist)
{
        mUri = aSource;
        mStartTime = aStartTime;
        mPersist = aPersist;

#if MOZILLA_SNAPSHOT > 15
	mTarget = aTarget;
	nsCOMPtr<nsIFileURL> tFileUrl = do_QueryInterface(mTarget);
	if (tFileUrl)
	{
        	nsCOMPtr<nsIFile> tFileRef;
        	tFileUrl->GetFile(getter_AddRefs(tFileRef));
		tFileRef->Clone(getter_AddRefs(mFile));
	}
#else
        mFile = aTarget;
#endif

	if (aMIMEInfo)
	{
		nsMIMEInfoHandleAction action;
		if (NS_SUCCEEDED(aMIMEInfo->GetPreferredAction(&action)))
		{
			if (action == nsIMIMEInfo::useHelperApp)
			{
				mMIMEInfo = aMIMEInfo;
			}

			/* HACK, stop mozilla from opening the application, we
			 * do it ourselves */
			aMIMEInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
		}
	}

        return PrivateInit();
}

NS_IMETHODIMP GProgressListener::Open(nsIDOMWindow *aParent)
{
        return NS_OK;
}

/* attribute long long startTime; */
NS_IMETHODIMP GProgressListener::GetStartTime(PRInt64 *aStartTime)
{
        *aStartTime = mStartTime;
        return NS_OK;
}

/* attribute nsIURI source; */
NS_IMETHODIMP GProgressListener::GetSource(nsIURI * *aSource)
{
        NS_IF_ADDREF(*aSource = mUri);
        return NS_OK;
}

#if MOZILLA_SNAPSHOT <= 15
/* attribute nsILocalFile target; */
NS_IMETHODIMP GProgressListener::GetTarget(nsILocalFile * *aTarget)
#else
/* attribute nsIURI target; */
NS_IMETHODIMP GProgressListener::GetTarget(nsIURI * *aTarget)
{
	NS_IF_ADDREF(*aTarget = mTarget);
	return NS_OK;
}

/* attribute nsILocalFile targetFile; */
NS_IMETHODIMP GProgressListener::GetTargetFile(nsILocalFile * *aTarget)
#endif
{
        nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(mFile);
        NS_IF_ADDREF(*aTarget = localFile);
        return NS_OK;
}

NS_IMETHODIMP GProgressListener::GetMIMEInfo(nsIMIMEInfo * *aMIMEInfo)
{
	NS_IF_ADDREF(*aMIMEInfo = mMIMEInfo);
	return NS_OK;
}

/* attribute nsIObserver observer; */
NS_IMETHODIMP GProgressListener::GetObserver(nsIObserver * *aObserver)
{
        NS_IF_ADDREF(*aObserver = mObserver);
        return NS_OK;
}
NS_IMETHODIMP GProgressListener::SetObserver(nsIObserver * aObserver)
{
        mObserver = aObserver;
        return NS_OK;
}

/* attribute nsIWebProgressListener listener; */
NS_IMETHODIMP GProgressListener::GetListener(nsIWebProgressListener * *aListener)
{
        *aListener = nsnull;
        return NS_OK;
}
NS_IMETHODIMP GProgressListener::SetListener(nsIWebProgressListener * aListener)
{
        return NS_OK;
}

/* readonly attribute PRInt32 percentComplete; */
NS_IMETHODIMP GProgressListener::GetPercentComplete(PRInt32 *aPercentComplete)
{
	*aPercentComplete = 0;
        return NS_OK;
}

/* attribute wstring displayName; */
NS_IMETHODIMP GProgressListener::GetDisplayName(PRUnichar * *aDisplayName)
{
        *aDisplayName = nsnull;
        return NS_OK;
}
NS_IMETHODIMP GProgressListener::SetDisplayName(const PRUnichar * aDisplayName)
{
        return NS_OK;
}

NS_IMETHODIMP GProgressListener::GetPersist(nsIWebBrowserPersist * *aPersist)
{
        NS_IF_ADDREF(*aPersist = mPersist);
        return NS_OK;
}

NS_IMETHODIMP GProgressListener::SetDialog(nsIDOMWindow *aDialog)
{
        return NS_OK;
}

NS_IMETHODIMP GProgressListener::GetDialog(nsIDOMWindow * *aDialog)
{
        *aDialog = nsnull;
        return NS_OK;
}

/* attribute PRBool cancelDownloadOnClose; */
NS_IMETHODIMP  GProgressListener::GetCancelDownloadOnClose(PRBool *aCancelDownloadOnClose)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP  GProgressListener::SetCancelDownloadOnClose(PRBool aCancelDownloadOnClose)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static void LaunchHandler (PersistHandlerInfo *aHandler, nsIFile *aFile)
{
	nsCOMPtr<nsIExternalHelperAppService> helperService =
		do_GetService (NS_EXTERNALHELPERAPPSERVICE_CONTRACTID);

	nsCOMPtr<nsPIExternalAppLauncher> appLauncher =
		do_QueryInterface (helperService);
	if (appLauncher)
	{
		appLauncher->DeleteTemporaryFileOnExit(aFile);
	}

	nsAutoString filename;
	aFile->GetPath(filename);
	
	gul_general_launch_application (aHandler->command,
				        NS_ConvertUCS2toUTF8(filename).get(),
					aHandler->need_terminal);
}

/*
 * void onStateChange (in nsIWebProgress aWebProgress, 
 *                     in nsIRequest aRequest, 
 *		       in long aStateFlags, 
 *		       in unsigned long aStatus);
 */
NS_IMETHODIMP GProgressListener::OnStateChange (nsIWebProgress *aWebProgress,
						 nsIRequest *aRequest,
						 PRUint32 aStateFlags,
						 PRUint32 aStatus)
{
	if (aStateFlags & nsIWebProgressListener::STATE_START &&
	    aStateFlags & nsIWebProgressListener::STATE_IS_REQUEST)
	{
		// HTTP is special as the download as a byte stream can
		// complete successfully, but what you get instead of the
		// desired file is a 404 error document (or whatever depending
		// on the error.)
		//
		// Only relevant when coming from mozilla-embed-persist.cpp
		nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
		if (http)
		{
			PRBool succeeded = PR_TRUE;
			http->GetRequestSucceeded(&succeeded);
			if (!succeeded)
			{
				Abort ();
				return NS_OK;
			}
		}
	}

	if ((aRequest != mRequest) && !(aStateFlags & nsIWebProgressListener::STATE_STOP))
	{
		SetRequest (aRequest);
	}

	if (aStateFlags & nsIWebProgressListener::STATE_STOP)
	{
		// We need to detach from the nsIWebBrowserPersist or it'll
                // keep us around forever.  Detaching would kill us instantly
                // so kungfu is needed to survive just long enough to do the
                // final clean up.
                nsCOMPtr<nsIDownload> kungFuDeathGrip(this);
                if (mPersist)
                {
                        mPersist->SetProgressListener(nsnull);
                        mPersist = nsnull;
                }
		
		if (NS_SUCCEEDED(aStatus))
		{
			if (mDownload)
			{
				g_object_remove_weak_pointer (G_OBJECT(mDownload), (void **) &mDownload);
				download_completed (mDownload);
			}

			if (G_IS_OBJECT(mGaleonPersist))
			{
				PersistHandlerInfo *handler(nsnull);
				g_object_get (mGaleonPersist,
			      		      "handler", &handler,        
			      		      NULL);
				if (handler)
				{
					LaunchHandler (handler, mFile);
				}

				g_signal_emit_by_name (mGaleonPersist, "completed");
				g_object_unref (mGaleonPersist);
			}
			else if (mMIMEInfo)
			{
				LaunchHelperApp();
			}
		}
		else
		{
			if (mDownload)
			{
				g_object_remove_weak_pointer (G_OBJECT(mDownload), (void **) &mDownload);
				download_cancelled (mDownload);
			}

			if (mGaleonPersist)
			{
				g_signal_emit_by_name (mGaleonPersist, "cancelled", NULL);
				g_object_unref (mGaleonPersist);
			}
		}

		mDownload = NULL;
		mGaleonPersist = NULL;
	}

	/* done */
	return NS_OK;
}

/*
 * void onProgressChange (in nsIWebProgress aWebProgress, 
 *                        in nsIRequest aRequest, 
 *                        in long aCurSelfProgress, 
 *                        in long aMaxSelfProgress, 
 *                        in long aCurTotalProgress, 
 *                        in long aMaxTotalProgress); 
 */
NS_IMETHODIMP GProgressListener::
			OnProgressChange (nsIWebProgress *aWebProgress,
					  nsIRequest *aRequest,
					  PRInt32 aCurSelfProgress,
					  PRInt32 aMaxSelfProgress,
					  PRInt32 aCurTotalProgress,
					  PRInt32 aMaxTotalProgress)
{
	// no point in calculating progress if there's no one watching
	if (!mDownload) return NS_OK;

	if (aRequest != mRequest)
	{
		// FIXME when Mozilla permits
		//
		// http://bugzilla.mozilla.org/show_bug.cgi?id=152224
		//
		// When used via GContentHandler we never get OnStateChange
		// with STATE_START.  As a workaround grab the nsIRequest here
		// as well.  To be removed when sanity is found.

		SetRequest (aRequest);
	}

	/* FIXME maxsize check here */

	if (aCurTotalProgress == -1)
	{
		// This seems to be a special case which we get if the file has
		// already been completely downloaded by the time we get to
		// show the progress dialog.  (Download begins when we click on
		// a link, then the save/open dialog pops up, then we get to
		// pick a file name, and so on...)
		//
		// So lacking other reliable information, let's try the file
		// size.  <insert 32-bits for file size ought to be enough for
		// everyone joke here>
		PRInt64 fileSize;
		if (NS_SUCCEEDED(mFile->GetFileSize(&fileSize)))
		{
			mContentLength    = fileSize;
			aCurTotalProgress = fileSize;
			aMaxTotalProgress = fileSize;
		}
	}
	else if (aMaxTotalProgress == -1 && mContentLength >= 0)
	{
		aMaxTotalProgress = mContentLength;
	}

	PRInt64 now = PR_Now ();

	/* get out if we're updating too quickly, but don't miss EOF */
	if (aCurTotalProgress != aMaxTotalProgress)
	{
		PRInt32 interval = (aMaxTotalProgress == -1) ? kUnknownSizeInterval : kInterval;
		if ((now - mLastUpdate) < interval)
		{
			return NS_OK;
		}
	}

	/* compute elapsed time */
	mLastUpdate = now;

	guint elapsed = (now - mStartTime) / PR_USEC_PER_SEC;

	/* compute download rate */
	guint speed = 0;
	if (elapsed > 0)
	{
		speed = aCurTotalProgress / elapsed;
	}

	/* compute time remaining */
	guint remaining = G_MAXUINT;
	if (speed > 0 && aMaxTotalProgress >= 0)
	{
		remaining = (aMaxTotalProgress - aCurTotalProgress) / speed;
	}

	if (aMaxTotalProgress < 0)
		aMaxTotalProgress = G_MAXULONG;

	download_set_progress (mDownload,
			       elapsed,
			       remaining,
			       speed,
			       aCurTotalProgress,
			       aMaxTotalProgress);

	/* done */
	return NS_OK;
}

/* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location); */
NS_IMETHODIMP GProgressListener::
			OnLocationChange(nsIWebProgress *aWebProgress,
					 nsIRequest *aRequest, nsIURI *location)
{
    return NS_OK;
}

/* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */
NS_IMETHODIMP GProgressListener::
			OnStatusChange(nsIWebProgress *aWebProgress,
				       nsIRequest *aRequest, nsresult aStatus,
				       const PRUnichar *aMessage)
{
    return NS_OK;
}

/* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long state); */
NS_IMETHODIMP GProgressListener::
			OnSecurityChange(nsIWebProgress *aWebProgress,
					 nsIRequest *aRequest,
					 PRUint32 state)
{
    return NS_OK;
}

//---------------------------------------------------------------------------
NS_METHOD GProgressListener::SetRequest (nsIRequest *aRequest)
{
	mRequest = aRequest;

	nsCOMPtr<nsIChannel> channel = do_QueryInterface (mRequest);
	if (channel)
	{
		PRInt32 length;
		if (NS_SUCCEEDED(channel->GetContentLength(&length)) && length >= 0)
		{
			mContentLength = length;
			if (mDownload)
			{
				// Update the download with known content
				// length.  OnProgressChange might never be
				// called with aMaxTotalProgress reflecting the
				// full length, leaving the download "too short"
				download_set_size_total (mDownload, mContentLength);
			}
		}
	}

	nsCOMPtr<nsIFTPChannel> ftp = do_QueryInterface(aRequest);
	mCanPause = ftp ? PR_TRUE : PR_FALSE;
	if (mDownload)
	{
		download_set_can_pause (mDownload, mCanPause);
	}

	return NS_OK;
}

NS_METHOD GProgressListener::LaunchHelperApp (void)
{
	if (!mMIMEInfo)
		return NS_ERROR_FAILURE;

	nsresult rv;

	nsCOMPtr<nsIFile> helperFile;
	rv = mMIMEInfo->GetPreferredApplicationHandler(getter_AddRefs(helperFile));
	if(NS_FAILED(rv)) return NS_ERROR_FAILURE;

	nsCAutoString helperFileName;
	rv = helperFile->GetNativePath(helperFileName);
	if(NS_FAILED(rv)) return NS_ERROR_FAILURE;

	nsMIMEInfoHandleAction mimeAction;
	rv = mMIMEInfo->GetPreferredAction(&mimeAction);
	if(NS_FAILED(rv)) return NS_ERROR_FAILURE;

	nsCOMPtr<nsIExternalHelperAppService> helperService =
		do_GetService (NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
	if (NS_SUCCEEDED(rv))
	{
		nsCOMPtr<nsPIExternalAppLauncher> appLauncher =
			do_QueryInterface (helperService, &rv);
		if (NS_SUCCEEDED(rv))
		{
			appLauncher->DeleteTemporaryFileOnExit(mFile);
		}
	}

	nsCAutoString cFileName;
	mFile->GetNativePath(cFileName);
	if(NS_FAILED(rv)) return NS_ERROR_FAILURE;

#if MOZILLA_SNAPSHOT > 16
	nsAutoString helperDesc;
	rv = mMIMEInfo->GetApplicationDescription(helperDesc);
#else
	nsXPIDLString helperDesc;
	rv = mMIMEInfo->GetApplicationDescription(getter_Copies(helperDesc));
#endif
	if(NS_FAILED(rv)) return NS_ERROR_FAILURE;

	gboolean terminalHelper = 
		helperDesc.Equals(NS_LITERAL_STRING("runInTerminal")) ?
		TRUE : FALSE;

	gul_general_launch_application (helperFileName.get(),
					cFileName.get(),
					terminalHelper);

	return NS_OK;
}

nsresult GProgressListener::Pause (void)
{
	NS_ENSURE_TRUE(mCanPause && !mIsPaused, NS_ERROR_FAILURE);

        nsresult rv = mRequest->Suspend ();
	if (NS_FAILED(rv))
	{
		mCanPause = PR_FALSE;
		download_set_can_pause (mDownload, FALSE);
		return rv;
	}

	mIsPaused = PR_TRUE;
	download_paused (mDownload);
	return NS_OK;
}

nsresult GProgressListener::Resume (void)
{
	NS_ENSURE_TRUE(mCanPause && mIsPaused, NS_ERROR_FAILURE);

        nsresult rv = mRequest->Resume ();
	NS_ENSURE_SUCCESS(rv, rv);

	mIsPaused = PR_FALSE;
	download_resumed (mDownload);
        return NS_OK;
}

nsresult GProgressListener::Abort (void)
{
	// Keep ourselves alive
	nsCOMPtr<nsIDownload> kungFuDeathGrip(this);

	// Suspended request doesn't seem to reach cancelled state until it's
	// resumed.  Better resume it first so that we get the right away.
        if (mIsPaused)
        {
                mRequest->Resume ();
        }

        if (mObserver)
        {
                mObserver->Observe(NS_ISUPPORTS_CAST(nsIProgressDialog*, this),
				   "oncancel", nsnull);
        }

        if (mPersist)
        {
                return mPersist->CancelSave ();
        }

	// Working assumption is that if mPersist is set CancelSave correctly
	// emits proper events to OnStateChange.  At the time of writing that
	// seems to be the case; mPersist is set only when the download is
	// initiated from mozilla-embed-persist.cpp
	//
	// This part is reached only if the download was initiated via
	// GContentHandler


	// FIXME remove workaround when Mozilla permits
	//
	// http://bugzilla.mozilla.org/show_bug.cgi?id=152224
	//
	// Mozilla isn't calling OnStateChange with STATE_STOP when the
	// download is cancelled, so we need to simulate it or mDownload and
	// mGaleonPersist "listeners" are left unaware of the cancellation.

	OnStateChange (nsnull, mRequest, nsIWebProgressListener::STATE_STOP, NS_BINDING_ABORTED);

	// XXX memory leak courtesy of Mozilla
	//
	// http://bugzilla.mozilla.org/show_bug.cgi?id=152224
	//
	// Mozilla is holding us hostage until someone calls
	// nsIHelperAppLauncher::CloseProgressWindow -- no one ever does.
	//
	// We could do 'delete this;' right here and plug the memory leak.
	// We'd still be leaking if the download is finished successfully.
	// We'd also crash and burn once Mozilla gets fixed.

	return NS_OK;
}

nsresult 
InitiateMozillaDownload (nsIURI *sourceURI, nsILocalFile* inDestFile,
			 GaleonEmbedPersist *embedPersist,
			 nsIDOMDocument *domDocument, 
			 nsISupports *cacheKey,
			 nsIInputStream *postData,
			 PRBool decode,
			 nsIURI *displayURI)
{
	nsresult rv = NS_OK;

	EmbedPersistFlags flags;
	galeon_embed_persist_get_flags (GALEON_EMBED_PERSIST (embedPersist), &flags);

	nsCOMPtr<nsIWebBrowserPersist> webPersist = 
		do_CreateInstance(NS_WEBBROWSERPERSIST_CONTRACTID, &rv);
	NS_ENSURE_SUCCESS (rv, rv);
  
	GProgressListener *listener = new GProgressListener ();
	rv = listener->InitForPersist (webPersist, displayURI, inDestFile, embedPersist);
	NS_ENSURE_SUCCESS (rv, rv);

	PRInt32 persist_flags = nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES;
	if (!domDocument && !decode)
	{
		persist_flags |= nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION;
	}

	if (flags & EMBED_PERSIST_BYPASSCACHE)
	{
		persist_flags |= nsIWebBrowserPersist::PERSIST_FLAGS_BYPASS_CACHE;
	}
	else
	{
		persist_flags |= nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE;
	}

	webPersist->SetPersistFlags(persist_flags);

	if (!domDocument)
	{
		/* TODO, fix this null referrer */
		rv = webPersist->SaveURI (sourceURI, cacheKey, nsnull,
					  postData, nsnull, inDestFile);
	}
	else
	{
		nsCOMPtr<nsILocalFile> filesFolder;
		if (flags & EMBED_PERSIST_SAVE_CONTENT)
		{
			/* Create a directory to hold the content, mozilla
			 * will create the directory as needed. */
			nsCAutoString nativePath;
			inDestFile->GetNativePath(nativePath);
			
			NS_NewNativeLocalFile(nativePath + NS_LITERAL_CSTRING("_content"),
					      PR_TRUE, getter_AddRefs(filesFolder));
		}

		rv = webPersist->SaveDocument (domDocument, inDestFile, filesFolder,
					       nsnull, 0, 0);
	}
  
	return rv;
}

/**
 * Builds the download filename into the default download directory,
 *
 * returns NS_ERROR_FAILURE if the filename couldn't be generated
 */
nsresult
BuildDownloadPath (const char *defaultFileName, GtkWidget *parent,
		   nsILocalFile **_retval)
{
	gchar * dirName = eel_gconf_get_string (CONF_DOWNLOADING_DIR);
	if (dirName)
	{
		char *tmp = g_filename_from_utf8 (dirName, -1, NULL, NULL, NULL);
		g_free (dirName);
		dirName = tmp;
	}

	if (dirName)
	{
		char *tmp = gnome_vfs_expand_initial_tilde (dirName);
		g_free (dirName);
		dirName = tmp;
	}

	if (!dirName || dirName[0] == '\0' || 
	    !g_file_test (dirName, G_FILE_TEST_IS_DIR))
	{
		g_free (dirName);
		dirName = g_strdup (g_get_home_dir ());
	}


	char *filename = g_build_filename (dirName, defaultFileName, NULL);
	g_free (dirName);

	if (gul_gui_confirm_overwrite_file (parent, filename))
	{
		nsCOMPtr <nsILocalFile> saveFile;
		NS_NewNativeLocalFile (nsDependentCString (filename),
				       PR_TRUE,
				       getter_AddRefs(saveFile));
			
		NS_ADDREF (*_retval = saveFile);

		g_free (filename);
		return NS_OK;
	}
	g_free (filename);

	return NS_ERROR_FAILURE;
}
