/* * 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 #endif #include "ProgressListener.h" #include "eel-gconf-extensions.h" #include "ephy-file-helpers.h" #include #include #include #include "nsXPIDLString.h" #include "nsIChannel.h" #include "nsIFTPChannel.h" #include "nsIFactory.h" #include "nsXPComFactory.h" #include "nsIMIMEInfo.h" #include "nsCOMPtr.h" static void download_remove_cb (DownloaderView *dv, GProgressListener *Progress); static void download_resume_cb (DownloaderView *dv, GProgressListener *Progress); static void download_pause_cb (DownloaderView *dv, GProgressListener *Progress); NS_IMPL_ISUPPORTS4 (GProgressListener, nsIDownload, nsIWebProgressListener, nsIProgressDialog, nsISupportsWeakReference) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- GProgressListener::GProgressListener () : mLauncher(nsnull), mPersist(nsnull), mHandler(nsnull), mObserver(nsnull), mMIMEInfo(nsnull), mPercentComplete(0) { NS_INIT_ISUPPORTS (); } GProgressListener::~GProgressListener () { /* destructor code */ } NS_METHOD GProgressListener::InitForPersist (nsIWebBrowserPersist *aPersist, nsIDOMWindow *aParent, nsIURI *aURI, nsIFile *aFile, DownloadAction aAction, EphyEmbedPersist *ephyPersist, PRBool noDialog, PRInt64 aTimeDownloadStarted) { nsresult rv; /* fill in download details */ mAction = aAction; mParent = aParent; mNoDialog = noDialog; mUri = aURI; mFile = aFile; mPersist = aPersist; mTimeDownloadStarted = aTimeDownloadStarted; mEphyPersist = ephyPersist; /* do remaining init */ rv = PrivateInit (); /* pick up progress messages */ mPersist->SetProgressListener (this); /* done */ return rv; } NS_METHOD GProgressListener::InitForDownload (nsIHelperAppLauncher *aLauncher, nsISupports *aContext, GContentHandler *aHandler, DownloadAction aAction) { nsresult rv; mNoDialog = 0; /* fill in download details */ mAction = aAction; mParent = do_QueryInterface (aContext); mNoDialog = PR_TRUE; mHandler = aHandler; mLauncher = aLauncher; rv = mLauncher->GetDownloadInfo (getter_AddRefs (mUri), &mTimeDownloadStarted, getter_AddRefs (mFile)); /* do remaining init */ rv = PrivateInit (); /* pick up progress messages */ mLauncher->SetWebProgressListener (this); /* done */ return rv; } NS_METHOD GProgressListener::PrivateInit (void) { nsresult rv; /* setup this download */ mInterval = 500000; /* in microsecs == 500ms == 0.5s */ mPriorKRate = 0; mRateChanges = 0; mRateChangeLimit = 2; /* only update rate every second */ mCheckedCanPause = PR_FALSE; mCanPause = PR_FALSE; mIsPaused = PR_FALSE; mAbort = PR_FALSE; PRInt64 now = PR_Now (); mLastUpdate = now; // mStartTime = (mTimeDownloadStarted != 0 ? // mTimeDownloadStarted : now); mStartTime = now; //Stupid mozilla race condition mElapsed = now - mStartTime; if (!mNoDialog) { gchar *filename, *source, *dest; nsAutoString uTmp; nsCAutoString cTmp; rv = mFile->GetLeafName (uTmp); filename = g_strdup (NS_ConvertUCS2toUTF8(uTmp).get()); rv = mFile->GetPath (uTmp); dest = g_strdup (NS_ConvertUCS2toUTF8(uTmp).get()); rv = mUri->GetSpec (cTmp); source = g_strdup (cTmp.get()); DownloaderView *dv; dv = ephy_embed_shell_get_downloader_view (embed_shell); downloader_view_add_download (dv, filename, source, dest, (gpointer)this); g_signal_connect (G_OBJECT (dv), "download_remove", G_CALLBACK (download_remove_cb), this); g_signal_connect (G_OBJECT (dv), "download_pause", G_CALLBACK (download_pause_cb), this); g_signal_connect (G_OBJECT (dv), "download_resume", G_CALLBACK (download_resume_cb), this); mDownloaderView = dv; } /* done */ return NS_OK; } NS_IMETHODIMP GProgressListener::Init(nsIURI *aSource, nsILocalFile *aTarget, const PRUnichar *aDisplayName, nsIMIMEInfo *aMIMEInfo, PRInt64 aStartTime, nsIWebBrowserPersist *aPersist) { mUri = aSource; mFile = aTarget; mTimeDownloadStarted = aStartTime; mStartTime = aStartTime; mPersist = aPersist; mMIMEInfo = aMIMEInfo; mAction = ACTION_NONE; if(mMIMEInfo) { nsMIMEInfoHandleAction mimeAction; if(NS_SUCCEEDED(mMIMEInfo->GetPreferredAction(&mimeAction))) { mAction = (mimeAction == nsIMIMEInfo::useHelperApp) ? ACTION_SAVEFORHELPER : ACTION_NONE; } } mNoDialog = 0; return PrivateInit(); } NS_IMETHODIMP GProgressListener::Open(nsIDOMWindow *aParent) { mParent = aParent; mNoDialog = 0; 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; } /* attribute nsILocalFile target; */ NS_IMETHODIMP GProgressListener::GetTarget(nsILocalFile * *aTarget) { nsCOMPtr 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) { return *aPercentComplete = mPercentComplete; } /* 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; } NS_IMETHODIMP GProgressListener::LaunchHandler (PersistHandlerInfo *handler) { nsresult rv; nsCOMPtr helperService = do_GetService (NS_EXTERNALHELPERAPPSERVICE_CONTRACTID); nsCOMPtr appLauncher = do_QueryInterface (helperService, &rv); if (NS_SUCCEEDED(rv)) { appLauncher->DeleteTemporaryFileOnExit(mFile); } nsAutoString uFileName; mFile->GetPath(uFileName); const nsACString &cFileName = NS_ConvertUCS2toUTF8(uFileName); char *fname = g_strdup(PromiseFlatCString(cFileName).get()); ephy_file_launch_application (handler->command, fname, handler->need_terminal); g_free (fname); return NS_OK; } /* * 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 (mAbort) return NS_ERROR_FAILURE; if (aStateFlags & nsIWebProgressListener::STATE_STOP) { switch (mAction) { case ACTION_SAVEFORHELPER: LaunchHelperApp(); break; case ACTION_NONE: if (mLauncher) { mLauncher->CloseProgressWindow (); } break; case ACTION_OBJECT_NOTIFY: g_return_val_if_fail (IS_EPHY_EMBED_PERSIST (mEphyPersist), NS_ERROR_FAILURE); PersistHandlerInfo *handler; g_object_get (mEphyPersist, "handler", &handler, NULL); if (handler) { LaunchHandler (handler); } g_signal_emit_by_name (mEphyPersist, "completed"); } if (!mNoDialog) { downloader_view_set_download_status (mDownloaderView, DOWNLOAD_STATUS_COMPLETED, (gpointer)this); } } /* 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) { if (mAbort) return NS_ERROR_FAILURE; /* FIXME maxsize check here */ if (mNoDialog) return NS_OK; if (!mCheckedCanPause) { mCheckedCanPause = PR_TRUE; nsresult rv; nsCOMPtr channel = do_QueryInterface (aRequest, &rv); mCanPause = (NS_SUCCEEDED (rv) ? PR_TRUE : PR_FALSE); } mRequest = aRequest; PRInt64 now = PR_Now (); /* get out if we're updating too quickly */ if ((now - mLastUpdate < mInterval) && (aMaxTotalProgress != -1) && (aCurTotalProgress < aMaxTotalProgress)) { return NS_OK; } /* compute elapsed time */ mLastUpdate = now; mElapsed = now - mStartTime; /* compute size done */ PRInt32 currentKBytes = (PRInt32)(aCurTotalProgress / 1024.0 + 0.5); /* compute total size */ PRInt32 totalKBytes = (PRInt32)(aMaxTotalProgress / 1024.0 + 0.5); /* compute progress value */ gfloat progress = -1; if (aMaxTotalProgress > 0) { progress = (gfloat)aCurTotalProgress / (gfloat)aMaxTotalProgress; } /* compute download rate */ gfloat speed = -1; PRInt64 currentRate; if (mElapsed) { currentRate = ((PRInt64)(aCurTotalProgress)) * 1000000 / mElapsed; } else { currentRate = 0; } if (!mIsPaused) { if (currentRate) { PRFloat64 currentKRate = ((PRFloat64)currentRate)/1024; if (currentKRate != mPriorKRate) { if (mRateChanges++ == mRateChangeLimit) { mPriorKRate = currentKRate; mRateChanges = 0; } else { currentKRate = mPriorKRate; } } else { mRateChanges = 0; } speed = currentKRate; } } /* compute time remaining */ gint remaining = -1; if (currentRate && (aMaxTotalProgress > 0)) { remaining = (gint)((aMaxTotalProgress - aCurTotalProgress) /currentRate +.5); } downloader_view_set_download_progress (mDownloaderView, mElapsed, remaining, speed, totalKBytes, currentKBytes, progress, mCanPause, (gpointer)this); /* 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::LaunchHelperApp (void) { if (!mMIMEInfo) return NS_ERROR_FAILURE; nsresult rv; nsCOMPtr 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 helperService = do_GetService (NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr 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; nsXPIDLString helperDesc; rv = mMIMEInfo->GetApplicationDescription(getter_Copies(helperDesc)); if(NS_FAILED(rv)) return NS_ERROR_FAILURE; gboolean terminalHelper = helperDesc.Equals(NS_LITERAL_STRING("runInTerminal")) ? TRUE : FALSE; ephy_file_launch_application (helperFileName.get(), cFileName.get(), terminalHelper); return NS_OK; } nsresult GProgressListener::Pause (void) { nsresult rv; if (mCanPause && !mIsPaused) { rv = mRequest->Suspend (); if (NS_SUCCEEDED (rv)) { mIsPaused = PR_TRUE; } } else { rv = NS_ERROR_FAILURE; } return rv; } nsresult GProgressListener::Resume (void) { nsresult rv; if (mCanPause && mIsPaused) { rv = mRequest->Resume (); if (NS_SUCCEEDED (rv)) { mIsPaused = PR_FALSE; } } else { rv = NS_ERROR_FAILURE; } return rv; } nsresult GProgressListener::Abort (void) { mAction = ACTION_NONE; if (mIsPaused) { Resume (); } mAbort = PR_TRUE; if (mObserver) { mObserver->Observe(NS_ISUPPORTS_CAST(nsIProgressDialog*, this), "oncancel", nsnull); OnStateChange(nsnull, nsnull, nsIWebProgressListener::STATE_STOP, 0); } if (mPersist) { return mPersist->CancelSave (); } if (mLauncher) { return mLauncher->Cancel (); } else { return NS_ERROR_FAILURE; } } NS_DEF_FACTORY (GProgressListener, GProgressListener); /** * NS_NewProgressListenerFactory: */ nsresult NS_NewProgressListenerFactory(nsIFactory** aFactory) { NS_ENSURE_ARG_POINTER(aFactory); *aFactory = nsnull; nsGProgressListenerFactory *result = new nsGProgressListenerFactory; if (result == NULL) { return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(result); *aFactory = result; return NS_OK; } static void download_remove_cb (DownloaderView *dv, GProgressListener *Progress) { Progress->Abort(); } static void download_resume_cb (DownloaderView *dv, GProgressListener *Progress) { Progress->Resume(); } static void download_pause_cb (DownloaderView *dv, GProgressListener *Progress) { Progress->Pause(); }