Back to index

lightning-sunbird  0.9+nobinonly
nsDownloadManager.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Blake Ross <blaker@netscape.com> (Original Author)
00024  *   Ben Goodger <ben@netscape.com> (Original Author)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039  
00040 #include "nsDownloadManager.h"
00041 #include "nsIWebProgress.h"
00042 #include "nsIRDFLiteral.h"
00043 #include "rdf.h"
00044 #include "nsNetUtil.h"
00045 #include "nsIURI.h"
00046 #include "nsIURL.h"
00047 #include "nsIDOMChromeWindow.h"
00048 #include "nsIDOMWindow.h"
00049 #include "nsIDOMWindowInternal.h"
00050 #include "nsIDOMEvent.h"
00051 #include "nsIDOMEventTarget.h"
00052 #include "nsRDFCID.h"
00053 #include "nsAppDirectoryServiceDefs.h"
00054 #include "nsIWebBrowserPersist.h"
00055 #include "nsIObserver.h"
00056 #include "nsIProgressDialog.h"
00057 #include "nsIWebBrowserPersist.h"
00058 #include "nsIWindowWatcher.h"
00059 #include "nsIStringBundle.h"
00060 #include "nsCRT.h"
00061 #include "nsIWindowMediator.h"
00062 #include "nsIPromptService.h"
00063 #include "nsIObserverService.h"
00064 #include "nsIPrefBranch.h"
00065 #include "nsIPrefService.h"
00066 #include "nsVoidArray.h"
00067 #include "nsEnumeratorUtils.h"
00068 #include "nsIFileURL.h"
00069 #include "nsEmbedCID.h"
00070 #include "nsInt64.h"
00071 #include "nsAutoPtr.h"
00072 
00073 #ifdef XP_WIN
00074 #include <shlobj.h>
00075 #endif
00076 
00077 #if defined(_MSC_VER) && _MSC_VER < 1300
00078 #define BYTES_TO_KBYTES(bytes) ((PRFloat64)((PRInt64)(bytes >> 8) / 4 + .5))
00079 #else
00080 #define BYTES_TO_KBYTES(bytes) ((PRFloat64)bytes / 1024.0 + .5)
00081 #endif
00082 
00083 /* Outstanding issues/todo:
00084  * 1. Implement pause/resume.
00085  */
00086   
00087 static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
00088 static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID);
00089 static PRBool gStoppingDownloads = PR_FALSE;
00090 
00091 #define DOWNLOAD_MANAGER_FE_URL "chrome://mozapps/content/downloads/downloads.xul"
00092 #define DOWNLOAD_MANAGER_BUNDLE "chrome://mozapps/locale/downloads/downloads.properties"
00093 #define DOWNLOAD_MANAGER_ALERT_ICON "chrome://mozapps/skin/downloads/downloadIcon.png"
00094 #define PREF_BDM_SHOWALERTONCOMPLETE "browser.download.manager.showAlertOnComplete"
00095 #define PREF_BDM_SHOWALERTINTERVAL "browser.download.manager.showAlertInterval"
00096 #define PREF_BDM_RETENTION "browser.download.manager.retention"
00097 #define PREF_BDM_OPENDELAY "browser.download.manager.openDelay"
00098 #define PREF_BDM_SHOWWHENSTARTING "browser.download.manager.showWhenStarting"
00099 #define PREF_BDM_FOCUSWHENSTARTING "browser.download.manager.focusWhenStarting"
00100 #define PREF_BDM_CLOSEWHENDONE "browser.download.manager.closeWhenDone"
00101 #define PREF_BDM_FLASHCOUNT "browser.download.manager.flashCount"
00102 #define PREF_BDM_ADDTORECENTDOCS "browser.download.manager.addToRecentDocs"
00103 
00104 static const nsInt64 gInterval((PRUint32)(400 * PR_USEC_PER_MSEC));
00105 
00106 static nsIRDFResource* gNC_DownloadsRoot = nsnull;
00107 static nsIRDFResource* gNC_File = nsnull;
00108 static nsIRDFResource* gNC_URL = nsnull;
00109 static nsIRDFResource* gNC_IconURL = nsnull;
00110 static nsIRDFResource* gNC_Name = nsnull;
00111 static nsIRDFResource* gNC_ProgressPercent = nsnull;
00112 static nsIRDFResource* gNC_Transferred = nsnull;
00113 static nsIRDFResource* gNC_DownloadState = nsnull;
00114 static nsIRDFResource* gNC_StatusText = nsnull;
00115 static nsIRDFResource* gNC_DateStarted = nsnull;
00116 static nsIRDFResource* gNC_DateEnded = nsnull;
00117 
00118 static nsIRDFService* gRDFService = nsnull;
00119 static nsIObserverService* gObserverService = nsnull;
00120 static PRInt32 gRefCnt = 0;
00121 
00128 static nsresult
00129 GetFilePathFromURI(nsIURI *aURI, nsAString &aPath)
00130 {
00131   nsresult rv;
00132 
00133   nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
00134   if (NS_FAILED(rv)) return rv;
00135 
00136   nsCOMPtr<nsIFile> file;
00137   rv = fileURL->GetFile(getter_AddRefs(file));
00138   if (NS_SUCCEEDED(rv))
00139     rv = file->GetPath(aPath);
00140 
00141   return rv;
00142 }
00143 
00145 // nsDownloadManager
00146 
00147 NS_IMPL_ISUPPORTS4(nsDownloadManager, nsIDownloadManager, nsIXPInstallManagerUI, nsIObserver,
00148                    nsISupportsWeakReference)
00149 
00150 nsDownloadManager::nsDownloadManager() : mBatches(0)
00151 {
00152 }
00153 
00154 nsDownloadManager::~nsDownloadManager()
00155 {
00156   if (--gRefCnt != 0 || !gRDFService  || !gObserverService)
00157     // Either somebody tried to use |CreateInstance| instead of
00158     // |GetService| or |Init| failed very early, so there's nothing to
00159     // do here.
00160     return;
00161 
00162   gRDFService->UnregisterDataSource(mDataSource);
00163 
00164   gObserverService->RemoveObserver(this, "quit-application");
00165   gObserverService->RemoveObserver(this, "quit-application-requested");
00166   gObserverService->RemoveObserver(this, "offline-requested");
00167 
00168   NS_IF_RELEASE(gNC_DownloadsRoot);                                             
00169   NS_IF_RELEASE(gNC_File);                                                      
00170   NS_IF_RELEASE(gNC_URL);
00171   NS_IF_RELEASE(gNC_IconURL);
00172   NS_IF_RELEASE(gNC_Name);                                                      
00173   NS_IF_RELEASE(gNC_ProgressPercent);
00174   NS_IF_RELEASE(gNC_Transferred);
00175   NS_IF_RELEASE(gNC_DownloadState);
00176   NS_IF_RELEASE(gNC_StatusText);
00177   NS_IF_RELEASE(gNC_DateStarted);
00178   NS_IF_RELEASE(gNC_DateEnded);
00179 
00180   NS_RELEASE(gRDFService);
00181   NS_RELEASE(gObserverService);
00182 }
00183 
00184 PRInt32 PR_CALLBACK nsDownloadManager::CancelAllDownloads(nsHashKey* aKey, void* aData, void* aClosure)
00185 {
00186   nsStringKey* key = (nsStringKey*)aKey;
00187   nsresult rv;
00188 
00189   nsCOMPtr<nsIDownloadManager> manager = do_QueryInterface((nsISupports*)aClosure, &rv);
00190   if (NS_FAILED(rv)) return kHashEnumerateRemove;  
00191   
00192   if (IsInProgress(NS_STATIC_CAST(nsDownload*, aData)->GetDownloadState()))  
00193     manager->CancelDownload(key->GetString());
00194   else
00195     NS_STATIC_CAST(nsDownloadManager*, aClosure)->DownloadEnded(key->GetString(), nsnull);
00196  
00197   return kHashEnumerateRemove;
00198 }
00199 
00200 nsresult
00201 nsDownloadManager::Init()
00202 {
00203   if (gRefCnt++ != 0) {
00204     NS_NOTREACHED("download manager should be used as a service");
00205     return NS_ERROR_UNEXPECTED; // This will make the |CreateInstance| fail.
00206   }
00207 
00208   nsresult rv;
00209   mRDFContainerUtils = do_GetService("@mozilla.org/rdf/container-utils;1", &rv);
00210   if (NS_FAILED(rv)) return rv;
00211 
00212   rv = CallGetService("@mozilla.org/observer-service;1", &gObserverService);
00213   if (NS_FAILED(rv)) return rv;
00214   
00215   rv = CallGetService(kRDFServiceCID, &gRDFService);
00216   if (NS_FAILED(rv)) return rv;                                                 
00217 
00218   gRDFService->GetResource(NS_LITERAL_CSTRING("NC:DownloadsRoot"), &gNC_DownloadsRoot);
00219   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "File"), &gNC_File);
00220   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "URL"), &gNC_URL);
00221   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "IconURL"), &gNC_IconURL);
00222   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Name"), &gNC_Name);
00223   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "ProgressPercent"), &gNC_ProgressPercent);
00224   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Transferred"), &gNC_Transferred);
00225   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "DownloadState"), &gNC_DownloadState);
00226   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "StatusText"), &gNC_StatusText);
00227   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "DateStarted"), &gNC_DateStarted);
00228   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "DateEnded"), &gNC_DateEnded);
00229 
00230   mDataSource = new nsDownloadsDataSource();
00231   if (!mDataSource)
00232     return NS_ERROR_OUT_OF_MEMORY;
00233 
00234   rv = NS_STATIC_CAST(nsDownloadsDataSource*, (nsIRDFDataSource*)mDataSource.get())->LoadDataSource();
00235   if (NS_FAILED(rv)) {
00236     mDataSource = nsnull; // so we don't UnregisterDataSource on it
00237     return rv;
00238   }
00239 
00240   nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(kStringBundleServiceCID, &rv);
00241   if (NS_FAILED(rv)) return rv;
00242   
00243   rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, getter_AddRefs(mBundle));
00244   if (NS_FAILED(rv))
00245     return rv;
00246 
00247   // The following three AddObserver calls must be the last lines in this function,
00248   // because otherwise, this function may fail (and thus, this object would be not
00249   // completely initialized), but the observerservice would still keep a reference
00250   // to us and notify us about shutdown, which may cause crashes.
00251   // failure to add an observer is not critical
00252   // Note also that we're assuming that the service manager will hold on
00253   // to this object until after the "quit-application" notification so
00254   // that we actually get notified.
00255   gObserverService->AddObserver(this, "quit-application", PR_TRUE);
00256   gObserverService->AddObserver(this, "quit-application-requested", PR_TRUE);
00257   gObserverService->AddObserver(this, "offline-requested", PR_TRUE);
00258 
00259   return NS_OK;
00260 }
00261 
00262 PRInt32 
00263 nsDownloadManager::GetRetentionBehavior()
00264 {
00265   PRInt32 val = 0;
00266   nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID));
00267   if (pref) {
00268     nsresult rv = pref->GetIntPref(PREF_BDM_RETENTION, &val);
00269     if (NS_FAILED(rv))
00270       val = 0; // Use 0 as the default ("remove when done")
00271   }
00272 
00273   return val;
00274 }
00275 
00276 nsresult
00277 nsDownloadManager::DownloadStarted(const PRUnichar* aPath)
00278 {
00279   nsStringKey key(aPath);
00280   if (mCurrDownloads.Exists(&key)) {
00281   
00282     // Assert the date and time that the download ended.    
00283     nsCOMPtr<nsIRDFDate> dateLiteral;
00284     if (NS_SUCCEEDED(gRDFService->GetDateLiteral(PR_Now(), getter_AddRefs(dateLiteral)))) {    
00285       nsCOMPtr<nsIRDFResource> res;
00286       nsCOMPtr<nsIRDFNode> node;
00287       
00288       gRDFService->GetUnicodeResource(nsDependentString(aPath), getter_AddRefs(res));
00289       
00290       mDataSource->GetTarget(res, gNC_DateStarted, PR_TRUE, getter_AddRefs(node));
00291       if (node)
00292         mDataSource->Change(res, gNC_DateStarted, node, dateLiteral);
00293       else
00294         mDataSource->Assert(res, gNC_DateStarted, dateLiteral, PR_TRUE);
00295     }
00296   
00297     AssertProgressInfoFor(aPath);
00298   }
00299 
00300   return NS_OK;
00301 }
00302 
00303 nsresult
00304 nsDownloadManager::DownloadEnded(const PRUnichar* aPath, const PRUnichar* aMessage)
00305 {
00306   nsStringKey key(aPath);
00307   if (mCurrDownloads.Exists(&key)) {
00308 
00309     // Assert the date and time that the download ended.    
00310     nsCOMPtr<nsIRDFDate> dateLiteral;
00311     if (NS_SUCCEEDED(gRDFService->GetDateLiteral(PR_Now(), getter_AddRefs(dateLiteral)))) {    
00312       nsCOMPtr<nsIRDFResource> res;
00313       nsCOMPtr<nsIRDFNode> node;
00314       
00315       gRDFService->GetUnicodeResource(nsDependentString(aPath), getter_AddRefs(res));
00316       
00317       mDataSource->GetTarget(res, gNC_DateEnded, PR_TRUE, getter_AddRefs(node));
00318       if (node)
00319         mDataSource->Change(res, gNC_DateEnded, node, dateLiteral);
00320       else
00321         mDataSource->Assert(res, gNC_DateEnded, dateLiteral, PR_TRUE);
00322     }
00323 
00324     AssertProgressInfoFor(aPath);
00325     
00326     nsDownload* download = NS_STATIC_CAST(nsDownload*, mCurrDownloads.Get(&key));
00327     NS_RELEASE(download);
00328 
00329     if (!gStoppingDownloads)
00330       mCurrDownloads.Remove(&key);
00331   }
00332 
00333   return NS_OK;
00334 }
00335 
00336 nsresult
00337 nsDownloadManager::GetDownloadsContainer(nsIRDFContainer** aResult)
00338 {
00339   if (mDownloadsContainer) {
00340     *aResult = mDownloadsContainer;
00341     NS_ADDREF(*aResult);
00342     return NS_OK;
00343   }
00344 
00345   PRBool isContainer;
00346   nsresult rv = mRDFContainerUtils->IsContainer(mDataSource, gNC_DownloadsRoot, &isContainer);
00347   if (NS_FAILED(rv)) return rv;
00348 
00349   if (!isContainer) {
00350     rv = mRDFContainerUtils->MakeSeq(mDataSource, gNC_DownloadsRoot, getter_AddRefs(mDownloadsContainer));
00351     if (NS_FAILED(rv)) return rv;
00352   }
00353   else {
00354     mDownloadsContainer = do_CreateInstance(NS_RDF_CONTRACTID "/container;1", &rv);
00355     if (NS_FAILED(rv)) return rv;
00356     rv = mDownloadsContainer->Init(mDataSource, gNC_DownloadsRoot);
00357     if (NS_FAILED(rv)) return rv;
00358   }
00359 
00360   *aResult = mDownloadsContainer;
00361   NS_IF_ADDREF(*aResult);
00362 
00363   return rv;
00364 }
00365 
00366 nsresult
00367 nsDownloadManager::GetInternalListener(nsIDownloadProgressListener** aListener)
00368 {
00369   *aListener = mListener;
00370   NS_IF_ADDREF(*aListener);
00371   return NS_OK;
00372 }
00373 
00374 NS_IMETHODIMP
00375 nsDownloadManager::GetActiveDownloadCount(PRInt32* aResult)
00376 {
00377   *aResult = mCurrDownloads.Count();
00378   
00379   return NS_OK;
00380 }
00381 
00382 NS_IMETHODIMP
00383 nsDownloadManager::GetActiveDownloads(nsISupportsArray** aResult)
00384 {
00385   nsCOMPtr<nsISupportsArray> ary;
00386   NS_NewISupportsArray(getter_AddRefs(ary));
00387   mCurrDownloads.Enumerate(BuildActiveDownloadsList, (void*)ary);
00388 
00389   NS_ADDREF(*aResult = ary);
00390 
00391   return NS_OK;
00392 }
00393 
00394 PRInt32 PR_CALLBACK 
00395 nsDownloadManager::BuildActiveDownloadsList(nsHashKey* aKey, void* aData, void* aClosure)
00396 {
00397   nsCOMPtr<nsISupportsArray> ary(do_QueryInterface((nsISupports*)aClosure));
00398   nsCOMPtr<nsIDownload> dl(do_QueryInterface((nsISupports*)aData));  
00399 
00400   ary->AppendElement(dl);
00401  
00402   return kHashEnumerateNext;
00403 }
00404 
00405 NS_IMETHODIMP
00406 nsDownloadManager::SaveState()
00407 {
00408   nsCOMPtr<nsISupports> supports;
00409   nsCOMPtr<nsIRDFResource> res;
00410   nsCOMPtr<nsIRDFInt> intLiteral;
00411 
00412   DownloadState states[] = { nsIDownloadManager::DOWNLOAD_DOWNLOADING, 
00413                              nsIDownloadManager::DOWNLOAD_PAUSED,
00414                              nsIXPInstallManagerUI::INSTALL_DOWNLOADING,
00415                              nsIXPInstallManagerUI::INSTALL_INSTALLING };
00416 
00417   for (int i = 0; i < 4; ++i) {
00418     gRDFService->GetIntLiteral(states[i], getter_AddRefs(intLiteral));
00419     nsCOMPtr<nsISimpleEnumerator> downloads;
00420     nsresult rv = mDataSource->GetSources(gNC_DownloadState, intLiteral, PR_TRUE, getter_AddRefs(downloads));
00421     if (NS_FAILED(rv)) return rv;
00422   
00423     PRBool hasMoreElements;
00424     downloads->HasMoreElements(&hasMoreElements);
00425 
00426     while (hasMoreElements) {
00427       const char* uri;
00428       downloads->GetNext(getter_AddRefs(supports));
00429       res = do_QueryInterface(supports);
00430       res->GetValueConst(&uri);
00431       AssertProgressInfoFor(NS_ConvertASCIItoUTF16(uri).get());
00432       downloads->HasMoreElements(&hasMoreElements);
00433     }
00434   }
00435 
00436   return NS_OK;
00437 }
00438 
00439 nsresult
00440 nsDownloadManager::AssertProgressInfoFor(const PRUnichar* aPath)
00441 {
00442   nsStringKey key(aPath);
00443   if (!mCurrDownloads.Exists(&key))
00444     return NS_ERROR_FAILURE;
00445  
00446   nsDownload* internalDownload = NS_STATIC_CAST(nsDownload*, mCurrDownloads.Get(&key));
00447   if (!internalDownload)
00448     return NS_ERROR_FAILURE;
00449   
00450   nsresult rv;
00451   PRInt32 percentComplete;
00452   nsCOMPtr<nsIRDFNode> oldTarget;
00453   nsCOMPtr<nsIRDFInt> intLiteral;
00454   nsCOMPtr<nsIRDFResource> res;
00455   nsCOMPtr<nsIRDFLiteral> literal;
00456 
00457   gRDFService->GetUnicodeResource(nsDependentString(aPath), getter_AddRefs(res));
00458 
00459   DownloadState state = internalDownload->GetDownloadState();
00460  
00461   // update download state (not started, downloading, queued, finished, etc...)
00462   gRDFService->GetIntLiteral(state, getter_AddRefs(intLiteral));
00463 
00464   mDataSource->GetTarget(res, gNC_DownloadState, PR_TRUE, getter_AddRefs(oldTarget));
00465   
00466   if (oldTarget)
00467     rv = mDataSource->Change(res, gNC_DownloadState, oldTarget, intLiteral);
00468   else
00469     rv = mDataSource->Assert(res, gNC_DownloadState, intLiteral, PR_TRUE);
00470   if (NS_FAILED(rv)) return rv;
00471 
00472   // update percentage
00473   internalDownload->GetPercentComplete(&percentComplete);
00474 
00475   mDataSource->GetTarget(res, gNC_ProgressPercent, PR_TRUE, getter_AddRefs(oldTarget));
00476   gRDFService->GetIntLiteral(percentComplete, getter_AddRefs(intLiteral));
00477 
00478   if (oldTarget)
00479     rv = mDataSource->Change(res, gNC_ProgressPercent, oldTarget, intLiteral);
00480   else
00481     rv = mDataSource->Assert(res, gNC_ProgressPercent, intLiteral, PR_TRUE);
00482   if (NS_FAILED(rv)) return rv;
00483 
00484   // update transferred
00485   nsDownload::TransferInformation transferInfo =
00486                                  internalDownload->GetTransferInformation();
00487 
00488   // convert from bytes to kbytes for progress display
00489   PRInt64 current = BYTES_TO_KBYTES(transferInfo.mCurrBytes);
00490   PRInt64 max = BYTES_TO_KBYTES(transferInfo.mMaxBytes);
00491  
00492   nsAutoString currBytes; currBytes.AppendInt(current);
00493   nsAutoString maxBytes; maxBytes.AppendInt(max);
00494   const PRUnichar *strings[] = {
00495     currBytes.get(),
00496     maxBytes.get()
00497   };
00498 
00499   nsXPIDLString value; 
00500   rv = mBundle->FormatStringFromName(NS_LITERAL_STRING("transferred").get(),
00501                                      strings, 2, getter_Copies(value));
00502   if (NS_FAILED(rv)) return rv;
00503 
00504   gRDFService->GetLiteral(value, getter_AddRefs(literal));
00505  
00506   mDataSource->GetTarget(res, gNC_Transferred, PR_TRUE, getter_AddRefs(oldTarget));
00507  
00508   if (oldTarget)
00509     rv = mDataSource->Change(res, gNC_Transferred, oldTarget, literal);
00510   else
00511     rv = mDataSource->Assert(res, gNC_Transferred, literal, PR_TRUE);
00512   if (NS_FAILED(rv)) return rv;
00513 
00514   // XXX should also store and update time elapsed
00515   return Flush();
00516 }  
00517 
00519 // nsIDownloadManager
00520 
00521 NS_IMETHODIMP
00522 nsDownloadManager::AddDownload(DownloadType aDownloadType, 
00523                                nsIURI* aSource,
00524                                nsIURI* aTarget,
00525                                const nsAString& aDisplayName,
00526                                const nsAString& aIconURL, 
00527                                nsIMIMEInfo *aMIMEInfo,
00528                                PRTime aStartTime,
00529                                nsILocalFile* aTempFile,
00530                                nsICancelable* aCancelable,
00531                                nsIDownload** aDownload)
00532 {
00533   NS_ENSURE_ARG_POINTER(aSource);
00534   NS_ENSURE_ARG_POINTER(aTarget);
00535   NS_ENSURE_ARG_POINTER(aDownload);
00536 
00537   nsresult rv;
00538 
00539   // target must be on the local filesystem
00540   nsCOMPtr<nsIFileURL> targetFileURL = do_QueryInterface(aTarget, &rv);
00541   NS_ENSURE_SUCCESS(rv, rv);
00542 
00543   nsCOMPtr<nsIFile> targetFile;
00544   rv = targetFileURL->GetFile(getter_AddRefs(targetFile));
00545   NS_ENSURE_SUCCESS(rv, rv);
00546 
00547   nsCOMPtr<nsIRDFContainer> downloads;
00548   rv = GetDownloadsContainer(getter_AddRefs(downloads));
00549   if (NS_FAILED(rv)) return rv;
00550 
00551   nsDownload* internalDownload = new nsDownload();
00552   if (!internalDownload)
00553     return NS_ERROR_OUT_OF_MEMORY;
00554 
00555   internalDownload->QueryInterface(NS_GET_IID(nsIDownload), (void**) aDownload);
00556   if (!aDownload)
00557     return NS_ERROR_FAILURE;
00558 
00559   NS_ADDREF(*aDownload);
00560 
00561   // give our new nsIDownload some info so it's ready to go off into the world
00562   internalDownload->SetDownloadManager(this);
00563   internalDownload->SetTarget(aTarget);
00564   internalDownload->SetSource(aSource);
00565   internalDownload->SetTempFile(aTempFile);
00566 
00567   // The path is the uniquifier of the download resource. 
00568   // XXXben - this is a little risky - really we should be using anonymous
00569   // resources because of duplicate file paths on MacOSX. We can't use persistent
00570   // descriptor here because in most cases the file doesn't exist on the local disk
00571   // yet (it's being downloaded) and persistentDescriptor fails on MacOSX for 
00572   // non-existent files. 
00573 
00574   nsAutoString path;
00575   rv = targetFile->GetPath(path);
00576   if (NS_FAILED(rv)) return rv;
00577 
00578   nsStringKey key(path);
00579   if (mCurrDownloads.Exists(&key))
00580     CancelDownload(path.get());
00581 
00582   nsCOMPtr<nsIRDFResource> downloadRes;
00583   gRDFService->GetUnicodeResource(path, getter_AddRefs(downloadRes));
00584 
00585   // Save state of existing downloads NOW... because inserting the new 
00586   // download resource into the container will cause the FE to rebuild and all 
00587   // the active downloads will have their progress reset (since progress is held
00588   // mostly by the FE and not the datasource) until they get another progress
00589   // notification (which could be a while for slow downloads).
00590   SaveState();
00591 
00592   // if the resource is in the container already (the user has already
00593   // downloaded this file), remove it
00594   PRInt32 itemIndex;
00595   nsCOMPtr<nsIRDFNode> node;
00596   downloads->IndexOf(downloadRes, &itemIndex);
00597   if (itemIndex > 0) {
00598     rv = downloads->RemoveElementAt(itemIndex, PR_TRUE, getter_AddRefs(node));
00599     if (NS_FAILED(rv)) return rv;
00600   }
00601   rv = downloads->InsertElementAt(downloadRes, 1, PR_TRUE);
00602   if (NS_FAILED(rv)) return rv;
00603   
00604   // Assert source url information
00605   nsCAutoString spec;
00606   aSource->GetSpec(spec);
00607 
00608   nsCOMPtr<nsIRDFResource> urlResource;
00609   gRDFService->GetResource(spec, getter_AddRefs(urlResource));
00610   mDataSource->GetTarget(downloadRes, gNC_URL, PR_TRUE, getter_AddRefs(node));
00611   if (node)
00612     rv = mDataSource->Change(downloadRes, gNC_URL, node, urlResource);
00613   else
00614     rv = mDataSource->Assert(downloadRes, gNC_URL, urlResource, PR_TRUE);
00615   if (NS_FAILED(rv)) {
00616     downloads->IndexOf(downloadRes, &itemIndex);
00617     downloads->RemoveElementAt(itemIndex, PR_TRUE, getter_AddRefs(node));
00618     return rv;
00619   }
00620 
00621   // Set and assert the "pretty" (display) name of the download
00622   nsAutoString displayName; displayName.Assign(aDisplayName);
00623   if (displayName.IsEmpty()) {
00624     targetFile->GetLeafName(displayName);
00625   }
00626   internalDownload->SetDisplayName(displayName.get());
00627  
00628   nsCOMPtr<nsIRDFLiteral> nameLiteral;
00629   gRDFService->GetLiteral(displayName.get(), getter_AddRefs(nameLiteral));
00630   mDataSource->GetTarget(downloadRes, gNC_Name, PR_TRUE, getter_AddRefs(node));
00631   if (node)
00632     rv = mDataSource->Change(downloadRes, gNC_Name, node, nameLiteral);
00633   else
00634     rv = mDataSource->Assert(downloadRes, gNC_Name, nameLiteral, PR_TRUE);
00635   if (NS_FAILED(rv)) {
00636     downloads->IndexOf(downloadRes, &itemIndex);
00637     downloads->RemoveElementAt(itemIndex, PR_TRUE, getter_AddRefs(node));
00638     return rv;
00639   }
00640   
00641   // Assert icon information
00642   if (!aIconURL.IsEmpty()) {
00643     nsCOMPtr<nsIRDFResource> iconURIRes;
00644     gRDFService->GetUnicodeResource(aIconURL, getter_AddRefs(iconURIRes));
00645     mDataSource->GetTarget(downloadRes, gNC_IconURL, PR_TRUE, getter_AddRefs(node));
00646     if (node)
00647       rv = mDataSource->Change(downloadRes, gNC_IconURL, node, iconURIRes);
00648     else
00649       rv = mDataSource->Assert(downloadRes, gNC_IconURL, iconURIRes, PR_TRUE);
00650   }
00651 
00652   internalDownload->SetMIMEInfo(aMIMEInfo);
00653   internalDownload->SetStartTime(aStartTime);
00654 
00655   // Assert file information
00656   nsCOMPtr<nsIRDFResource> fileResource;
00657   gRDFService->GetUnicodeResource(path, getter_AddRefs(fileResource));
00658   rv = mDataSource->Assert(downloadRes, gNC_File, fileResource, PR_TRUE);
00659   if (NS_FAILED(rv)) {
00660     downloads->IndexOf(downloadRes, &itemIndex);
00661     downloads->RemoveElementAt(itemIndex, PR_TRUE, getter_AddRefs(node));
00662     return rv;
00663   }
00664   
00665   // Assert download state information (NOTSTARTED, since it's just now being added)
00666   nsCOMPtr<nsIRDFInt> intLiteral;
00667   gRDFService->GetIntLiteral(nsIDownloadManager::DOWNLOAD_NOTSTARTED, getter_AddRefs(intLiteral));
00668   mDataSource->GetTarget(downloadRes, gNC_DownloadState, PR_TRUE, getter_AddRefs(node));
00669   if (node)
00670     rv = mDataSource->Change(downloadRes, gNC_DownloadState, node, intLiteral);
00671   else
00672     rv = mDataSource->Assert(downloadRes, gNC_DownloadState, intLiteral, PR_TRUE);
00673   if (NS_FAILED(rv)) {
00674     downloads->IndexOf(downloadRes, &itemIndex);
00675     downloads->RemoveElementAt(itemIndex, PR_TRUE, getter_AddRefs(node));
00676     return rv;
00677   }
00678 
00679   // Now flush all this to disk
00680   if (NS_FAILED(Flush())) {
00681     downloads->IndexOf(downloadRes, &itemIndex);
00682     downloads->RemoveElementAt(itemIndex, PR_TRUE, getter_AddRefs(node));
00683     return rv;
00684   }
00685 
00686   // this will create a cycle that will be broken in nsDownload::OnStateChange
00687   internalDownload->SetCancelable(aCancelable);
00688 
00689   // If this is an install operation, ensure we have a progress listener for the
00690   // install and track this download separately. 
00691   if (aDownloadType == nsIXPInstallManagerUI::DOWNLOAD_TYPE_INSTALL) {
00692     if (!mXPIProgress)
00693       mXPIProgress = new nsXPIProgressListener(this);
00694 
00695     nsIXPIProgressDialog* dialog = mXPIProgress.get();
00696     nsXPIProgressListener* listener = NS_STATIC_CAST(nsXPIProgressListener*, dialog);
00697     listener->AddDownload(*aDownload);
00698   }
00699 
00700   mCurrDownloads.Put(&key, *aDownload);
00701   gObserverService->NotifyObservers(*aDownload, "dl-start", nsnull);
00702 
00703   return rv;
00704 }
00705 
00706 NS_IMETHODIMP
00707 nsDownloadManager::GetDownload(const PRUnichar* aPath, nsIDownload** aDownloadItem)
00708 {
00709   NS_ENSURE_ARG_POINTER(aDownloadItem);
00710 
00711   // if it's currently downloading we can get it from the table
00712   // XXX otherwise we should look for it in the datasource and
00713   //     create a new nsIDownload with the resource's properties
00714   nsStringKey key(aPath);
00715   if (mCurrDownloads.Exists(&key)) {
00716     *aDownloadItem = NS_STATIC_CAST(nsIDownload*, mCurrDownloads.Get(&key));
00717     NS_ADDREF(*aDownloadItem);
00718     return NS_OK;
00719   }
00720 
00721   *aDownloadItem = nsnull;
00722   return NS_OK;
00723 }
00724 
00725 NS_IMETHODIMP
00726 nsDownloadManager::CancelDownload(const PRUnichar* aPath)
00727 {
00728   nsresult rv = NS_OK;
00729   nsStringKey key(aPath);
00730   if (!mCurrDownloads.Exists(&key))
00731     return RemoveDownload(aPath); // XXXBlake for now, to provide a workaround for stuck downloads
00732   
00733   // Take a strong reference to the download object as we may remove it from
00734   // mCurrDownloads in DownloadEnded.
00735   nsRefPtr<nsDownload> internalDownload =
00736       NS_STATIC_CAST(nsDownload*, mCurrDownloads.Get(&key));
00737   if (!internalDownload)
00738     return NS_ERROR_FAILURE;
00739 
00740   // Don't cancel if download is already finished
00741   if (CompletedSuccessfully(internalDownload->mDownloadState))
00742     return NS_OK;
00743 
00744   internalDownload->SetDownloadState(nsIDownloadManager::DOWNLOAD_CANCELED);
00745 
00746   // Cancel using the provided object
00747   nsCOMPtr<nsICancelable> cancelable;
00748   internalDownload->GetCancelable(getter_AddRefs(cancelable));
00749   if (cancelable)
00750     cancelable->Cancel(NS_BINDING_ABORTED);
00751 
00752   DownloadEnded(aPath, nsnull);
00753 
00754   // dump the temp file.  This should really be done when the transfer
00755   // is cancelled, but there's other cancelallation causes that shouldn't 
00756   // remove this, we need to improve those bits
00757   nsCOMPtr<nsILocalFile> tempFile;
00758   internalDownload->GetTempFile(getter_AddRefs(tempFile));
00759   if (tempFile) {
00760     PRBool exists;
00761     tempFile->Exists(&exists);
00762     if (exists)
00763       tempFile->Remove(PR_FALSE);
00764   }
00765 
00766   gObserverService->NotifyObservers(internalDownload, "dl-cancel", nsnull);
00767 
00768   // if there's a progress dialog open for the item,
00769   // we have to notify it that we're cancelling
00770   nsCOMPtr<nsIProgressDialog> dialog;
00771   internalDownload->GetDialog(getter_AddRefs(dialog));
00772   if (dialog) {
00773     nsCOMPtr<nsIObserver> observer = do_QueryInterface(dialog);
00774     rv = observer->Observe(internalDownload, "oncancel", nsnull);
00775     if (NS_FAILED(rv)) return rv;
00776   }
00777 
00778   return rv;
00779 }
00780 
00781 NS_IMETHODIMP
00782 nsDownloadManager::RemoveDownload(const PRUnichar* aPath)
00783 {
00784   nsStringKey key(aPath);
00785   
00786   // RemoveDownload is for downloads not currently in progress. Having it
00787   // cancel in-progress downloads would make things complicated, so just return.
00788   PRBool inProgress = mCurrDownloads.Exists(&key);
00789   NS_ASSERTION(!inProgress, "Can't call RemoveDownload on a download in progress!");
00790   if (inProgress)
00791     return NS_ERROR_FAILURE;
00792 
00793   nsCOMPtr<nsIRDFResource> res;
00794   gRDFService->GetUnicodeResource(nsDependentString(aPath), getter_AddRefs(res));
00795 
00796   return RemoveDownload(res);
00797 }
00798 
00799 // This is an internal method only because it does NOT check to see whether
00800 // or not a download is in progress or not before removing it. The FE should use
00801 // RemoveDownload(const PRUnichar* aPath) as supplied by nsIDownloadManager which
00802 // does the appropriate checking. 
00803 nsresult
00804 nsDownloadManager::RemoveDownload(nsIRDFResource* aDownload)
00805 {
00806   nsCOMPtr<nsIRDFContainer> downloads;
00807   nsresult rv = GetDownloadsContainer(getter_AddRefs(downloads));
00808   if (NS_FAILED(rv)) return rv;
00809   
00810   // remove all the arcs for this resource, and then remove it from the Seq
00811   nsCOMPtr<nsISimpleEnumerator> arcs;
00812   rv = mDataSource->ArcLabelsOut(aDownload, getter_AddRefs(arcs));
00813   if (NS_FAILED(rv)) return rv;
00814 
00815   PRBool moreArcs;
00816   rv = arcs->HasMoreElements(&moreArcs);
00817   if (NS_FAILED(rv)) return rv;
00818 
00819   while (moreArcs) {
00820     nsCOMPtr<nsISupports> supports;
00821     rv = arcs->GetNext(getter_AddRefs(supports));
00822     if (NS_FAILED(rv)) return rv;
00823 
00824     nsCOMPtr<nsIRDFResource> arc(do_QueryInterface(supports, &rv));
00825     if (NS_FAILED(rv)) return rv;
00826 
00827     nsCOMPtr<nsISimpleEnumerator> targets;
00828     rv = mDataSource->GetTargets(aDownload, arc, PR_TRUE, getter_AddRefs(targets));
00829     if (NS_FAILED(rv)) return rv;
00830 
00831     PRBool moreTargets;
00832     rv = targets->HasMoreElements(&moreTargets);
00833     if (NS_FAILED(rv)) return rv;
00834 
00835     while (moreTargets) {
00836       rv = targets->GetNext(getter_AddRefs(supports));
00837       if (NS_FAILED(rv)) return rv;
00838 
00839       nsCOMPtr<nsIRDFNode> target(do_QueryInterface(supports, &rv));
00840       if (NS_FAILED(rv)) return rv;
00841 
00842       // and now drop this assertion from the graph
00843       rv = mDataSource->Unassert(aDownload, arc, target);
00844       if (NS_FAILED(rv)) return rv;
00845 
00846       rv = targets->HasMoreElements(&moreTargets);
00847       if (NS_FAILED(rv)) return rv;
00848     }
00849     rv = arcs->HasMoreElements(&moreArcs);
00850     if (NS_FAILED(rv)) return rv;
00851   }
00852 
00853   PRInt32 itemIndex;
00854   downloads->IndexOf(aDownload, &itemIndex);
00855   if (itemIndex <= 0)
00856     return NS_ERROR_FAILURE;
00857   
00858   nsCOMPtr<nsIRDFNode> node;
00859   rv = downloads->RemoveElementAt(itemIndex, PR_TRUE, getter_AddRefs(node));
00860   if (NS_FAILED(rv)) return rv;
00861   
00862   // if a mass removal is being done, we don't want to flush every time
00863   if (mBatches) return rv;
00864 
00865   return Flush();
00866 }  
00867 
00868 // We implement this here in the download manager service as inspecting 
00869 // datasource directly is a more reliable way of clearing things up, rather
00870 // than relying on the template builder's fuzzy facsimile of the contents
00871 // of the datasource. The template builder filters out bad entries, which would
00872 // remain and accumulate if we did not happen to remove them here. 
00873 NS_IMETHODIMP
00874 nsDownloadManager::CleanUp()
00875 {
00876   nsCOMPtr<nsIRDFResource> downloadRes;
00877   nsCOMPtr<nsIRDFInt> intLiteral;
00878   nsCOMPtr<nsISimpleEnumerator> downloads;
00879 
00880   // Coalesce operations so that we don't write to disk for every removal, or
00881   // attempt to update the UI too much. 
00882   StartBatchUpdate();
00883   mDataSource->BeginUpdateBatch();
00884 
00885   // 1). First, clean out the usual suspects - downloads that are
00886   //     finished, failed or canceled. 
00887   DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
00888                              nsIDownloadManager::DOWNLOAD_FAILED,
00889                              nsIDownloadManager::DOWNLOAD_CANCELED,
00890                              nsIXPInstallManagerUI::INSTALL_FINISHED };
00891 
00892   for (int i = 0; i < 4; ++i) {
00893     gRDFService->GetIntLiteral(states[i], getter_AddRefs(intLiteral));
00894     nsresult rv = mDataSource->GetSources(gNC_DownloadState, intLiteral, PR_TRUE, getter_AddRefs(downloads));
00895     if (NS_FAILED(rv)) return rv;
00896   
00897     PRBool hasMoreElements;
00898     downloads->HasMoreElements(&hasMoreElements);
00899 
00900     while (hasMoreElements) {
00901       downloads->GetNext(getter_AddRefs(downloadRes));
00902       
00903       // Use the internal method because we know what we're doing! (We hope!)
00904       RemoveDownload(downloadRes);
00905       
00906       downloads->HasMoreElements(&hasMoreElements);
00907     }
00908   }
00909 
00910   mDataSource->EndUpdateBatch();
00911   EndBatchUpdate();
00912 
00913   return NS_OK;
00914 }
00915 
00916 NS_IMETHODIMP
00917 nsDownloadManager::GetCanCleanUp(PRBool* aResult)
00918 {
00919   nsCOMPtr<nsIRDFResource> downloadRes;
00920   nsCOMPtr<nsIRDFInt> intLiteral;
00921 
00922   *aResult = PR_FALSE;
00923 
00924   // 1). Downloads that can be cleaned up include those that are finished, 
00925   //     failed or canceled.
00926   DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
00927                              nsIDownloadManager::DOWNLOAD_FAILED,
00928                              nsIDownloadManager::DOWNLOAD_CANCELED,
00929                              nsIXPInstallManagerUI::INSTALL_FINISHED };
00930 
00931   for (int i = 0; i < 4; ++i) {
00932     gRDFService->GetIntLiteral(states[i], getter_AddRefs(intLiteral));
00933     
00934     mDataSource->GetSource(gNC_DownloadState, intLiteral, PR_TRUE, getter_AddRefs(downloadRes));
00935     if (downloadRes) {
00936       *aResult = PR_TRUE;
00937       break;
00938     }
00939   }
00940   return NS_OK;
00941 }
00942 
00943 nsresult
00944 nsDownloadManager::ValidateDownloadsContainer()
00945 {
00946   // None of the function calls here should need error checking or their results
00947   // null checked because they should always succeed. If they fail, and there's 
00948   // a crash, it's a sign that this function is being called after the download 
00949   // manager or services that it rely on have been shut down, and there's a 
00950   // problem in some other code, somewhere.
00951   nsCOMPtr<nsIRDFContainer> downloads;
00952   GetDownloadsContainer(getter_AddRefs(downloads));
00953 
00954   nsCOMPtr<nsISimpleEnumerator> e;
00955   downloads->GetElements(getter_AddRefs(e));
00956 
00957   // This is our list of bad download entries. 
00958   nsCOMPtr<nsISupportsArray> ary;
00959   NS_NewISupportsArray(getter_AddRefs(ary));
00960 
00961   PRBool hasMore;
00962   e->HasMoreElements(&hasMore);
00963   nsCOMPtr<nsIRDFResource> downloadRes;
00964   while (hasMore) {
00965     e->GetNext(getter_AddRefs(downloadRes));
00966 
00967     PRBool hasProperty;
00968 
00969     // A valid download entry in the datasource will have at least the following
00970     // properties:
00971     // - NC:DownloadState -- the current state of the download, defined as
00972     //                       an integer value, see enumeration in 
00973     //                       nsDownloadManager.h, e.g. |0| (|DOWNLOADING|)
00974     // - NC:File          -- where the file is to be saved, e.g. 
00975     //                       "C:\\FirebirdSetup.exe"
00976     // - NC:Name          -- the visual display name of the download, e.g.
00977     //                       "FirebirdSetup.exe"
00978 
00979     nsIRDFResource* properties[] = { gNC_DownloadState, gNC_File, gNC_Name };
00980     for (PRInt32 i = 0; i < 3; ++i) {
00981       mDataSource->HasArcOut(downloadRes, properties[i], &hasProperty);
00982       if (!hasProperty) {
00983         ary->AppendElement(downloadRes);
00984         break;
00985       }
00986     }
00987 
00988     e->HasMoreElements(&hasMore);
00989   }
00990 
00991   // Coalesce notifications
00992   mDataSource->BeginUpdateBatch();
00993 
00994   // Now Remove all the bad downloads. 
00995   PRUint32 cnt;
00996   ary->Count(&cnt);
00997   for (PRUint32 i = 0; i < cnt; ++i) {
00998     nsCOMPtr<nsIRDFResource> download(do_QueryElementAt(ary, i));
00999 
01000     // Use the internal method because we know what we're doing! (We hope!)
01001     RemoveDownload(download);
01002   }
01003 
01004   mDataSource->EndUpdateBatch();
01005 
01006   return NS_OK;
01007 }
01008 
01009 NS_IMETHODIMP
01010 nsDownloadManager::SetListener(nsIDownloadProgressListener* aListener)
01011 {
01012   mListener = aListener;
01013   return NS_OK;
01014 }
01015 
01016 NS_IMETHODIMP
01017 nsDownloadManager::GetListener(nsIDownloadProgressListener** aListener)
01018 {
01019   *aListener = mListener;
01020   NS_IF_ADDREF(*aListener);
01021   return NS_OK;
01022 }
01023 
01024 NS_IMETHODIMP
01025 nsDownloadManager::StartBatchUpdate()
01026 {
01027   ++mBatches;
01028   return NS_OK;
01029 }
01030 
01031 NS_IMETHODIMP
01032 nsDownloadManager::EndBatchUpdate()
01033 {
01034   return --mBatches == 0 ? Flush() : NS_OK;
01035 }
01036 
01037 NS_IMETHODIMP
01038 nsDownloadManager::PauseDownload(const PRUnichar* aPath)
01039 {
01040   return PauseResumeDownload(aPath, PR_TRUE);
01041 }
01042 
01043 NS_IMETHODIMP
01044 nsDownloadManager::ResumeDownload(const PRUnichar* aPath)
01045 {
01046   return PauseResumeDownload(aPath, PR_FALSE);
01047 }
01048 
01049 nsresult
01050 nsDownloadManager::PauseResumeDownload(const PRUnichar* aPath, PRBool aPause)
01051 {
01052   nsresult rv;
01053 
01054   nsStringKey key(aPath);
01055   if (!mCurrDownloads.Exists(&key))
01056     return NS_ERROR_FAILURE;
01057 
01058   nsDownload* internalDownload = NS_STATIC_CAST(nsDownload*, mCurrDownloads.Get(&key));
01059   if (!internalDownload)
01060     return NS_ERROR_FAILURE;
01061 
01062   // Update download state in the DataSource
01063   nsCOMPtr<nsIRDFInt> intLiteral;
01064 
01065   gRDFService->GetIntLiteral(
01066     aPause ? 
01067     (PRInt32)nsIDownloadManager::DOWNLOAD_PAUSED : 
01068     (PRInt32)nsIDownloadManager::DOWNLOAD_DOWNLOADING, getter_AddRefs(intLiteral));
01069 
01070   nsCOMPtr<nsIRDFResource> res;
01071   gRDFService->GetUnicodeResource(nsDependentString(aPath), getter_AddRefs(res));
01072 
01073   nsCOMPtr<nsIRDFNode> oldTarget;
01074   mDataSource->GetTarget(res, gNC_DownloadState, PR_TRUE, getter_AddRefs(oldTarget));
01075 
01076   if (oldTarget) {
01077     rv = mDataSource->Change(res, gNC_DownloadState, oldTarget, intLiteral);
01078     if (NS_FAILED(rv)) return rv;
01079   }
01080   else {
01081     rv = mDataSource->Assert(res, gNC_DownloadState, intLiteral, PR_TRUE);
01082     if (NS_FAILED(rv)) return rv;
01083   }
01084 
01085   // Pause the actual download
01086   internalDownload->Pause(aPause);
01087 
01088   return NS_OK;
01089 }
01090 
01091 NS_IMETHODIMP
01092 nsDownloadManager::Flush()
01093 {
01094   // Before writing, always be sure what we're about to write is good data.
01095   ValidateDownloadsContainer();
01096 
01097   nsCOMPtr<nsIRDFRemoteDataSource> remote(do_QueryInterface(mDataSource));
01098   return remote->Flush();
01099 }
01100 
01101 NS_IMETHODIMP
01102 nsDownloadManager::GetDatasource(nsIRDFDataSource** aDatasource)
01103 {
01104   *aDatasource = mDataSource;
01105   NS_IF_ADDREF(*aDatasource);
01106   return NS_OK;
01107 }
01108   
01109 NS_IMETHODIMP
01110 nsDownloadManager::Open(nsIDOMWindow* aParent, const PRUnichar* aPath)
01111 {
01112   // 1). Retrieve the download object for the supplied path. 
01113   nsStringKey key(aPath);
01114   if (!mCurrDownloads.Exists(&key))
01115     return NS_ERROR_FAILURE;
01116 
01117   nsDownload* internalDownload = NS_STATIC_CAST(nsDownload*, mCurrDownloads.Get(&key));
01118   if (!internalDownload)
01119     return NS_ERROR_FAILURE;
01120 
01121   // 2). Update the DataSource. 
01122   AssertProgressInfoFor(aPath);
01123 
01124   nsVoidArray* params = new nsVoidArray();
01125   if (!params)
01126     return NS_ERROR_OUT_OF_MEMORY;
01127 
01128   NS_IF_ADDREF(aParent);
01129   NS_ADDREF(internalDownload);
01130 
01131   params->AppendElement((void*)aParent);
01132   params->AppendElement((void*)internalDownload);
01133 
01134   PRInt32 delay = 0;
01135   nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID));
01136   if (pref)
01137     pref->GetIntPref(PREF_BDM_OPENDELAY, &delay);
01138 
01139   // 3). Look for an existing Download Manager window, if we find one we just 
01140   //     tell it that a new download has begun (we don't focus, that's 
01141   //     annoying), otherwise we need to open the window. We do this on a timer 
01142   //     so that we can see if the download has already completed, if so, don't 
01143   //     bother opening the window. 
01144   mDMOpenTimer = do_CreateInstance("@mozilla.org/timer;1");
01145   return mDMOpenTimer->InitWithFuncCallback(OpenTimerCallback, 
01146                                        (void*)params, delay, 
01147                                        nsITimer::TYPE_ONE_SHOT);
01148 }
01149 
01150 void
01151 nsDownloadManager::OpenTimerCallback(nsITimer* aTimer, void* aClosure)
01152 {
01153   nsVoidArray* params = (nsVoidArray*)aClosure;
01154   nsIDOMWindow* parent = (nsIDOMWindow*)params->ElementAt(0);
01155   nsDownload* download = (nsDownload*)params->ElementAt(1);
01156   
01157   PRInt32 complete;
01158   download->GetPercentComplete(&complete);
01159   
01160   PRBool closeDM = PR_FALSE;
01161   nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID));
01162   if (pref)
01163     pref->GetBoolPref(PREF_BDM_CLOSEWHENDONE, &closeDM);
01164 
01165   // Check closeWhenDone pref before opening download manager
01166   if (!closeDM || complete < 100) {
01167     PRBool focusDM = PR_FALSE;
01168     PRBool showDM = PR_TRUE;
01169     PRInt32 flashCount = -1;
01170 
01171     if (pref) {
01172       pref->GetBoolPref(PREF_BDM_FOCUSWHENSTARTING, &focusDM);
01173 
01174       // We only flash the download manager if the user has the download manager show
01175       pref->GetBoolPref(PREF_BDM_SHOWWHENSTARTING, &showDM);
01176       if (showDM) 
01177         pref->GetIntPref(PREF_BDM_FLASHCOUNT, &flashCount);
01178       else
01179         flashCount = 0;
01180     }
01181 
01182     nsDownloadManager::OpenDownloadManager(focusDM, flashCount, download, parent);
01183   }
01184 
01185   NS_RELEASE(download);
01186   NS_IF_RELEASE(parent);
01187 
01188   delete params;
01189 }
01190 
01191 nsresult
01192 nsDownloadManager::OpenDownloadManager(PRBool aShouldFocus, PRInt32 aFlashCount, nsIDownload* aDownload, nsIDOMWindow* aParent)
01193 {
01194   nsresult rv = NS_OK;
01195 
01196   nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
01197   if (NS_FAILED(rv)) return rv;
01198 
01199   nsCOMPtr<nsIDOMWindowInternal> recentWindow;
01200   wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(), getter_AddRefs(recentWindow));
01201   if (recentWindow) {
01202     nsCOMPtr<nsIObserverService> obsService = do_GetService("@mozilla.org/observer-service;1", &rv);
01203     if (NS_FAILED(rv)) return rv;
01204     
01205     if (aShouldFocus)
01206       recentWindow->Focus();
01207     else {
01208       nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(recentWindow));
01209       chromeWindow->GetAttentionWithCycleCount(aFlashCount);
01210     }
01211   }
01212   else {
01213     // If we ever have the capability to display the UI of third party dl managers,
01214     // we'll open their UI here instead.
01215     nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
01216     if (NS_FAILED(rv)) return rv;
01217 
01218     // pass the datasource to the window
01219     nsCOMPtr<nsISupportsArray> params;
01220     NS_NewISupportsArray(getter_AddRefs(params));
01221 
01222     // I love static members. 
01223     nsCOMPtr<nsIDownloadManager> dlMgr(do_GetService("@mozilla.org/download-manager;1"));
01224     nsCOMPtr<nsIRDFDataSource> ds;
01225     dlMgr->GetDatasource(getter_AddRefs(ds));
01226 
01227     params->AppendElement(ds);
01228     params->AppendElement(aDownload);
01229     
01230     nsCOMPtr<nsIDOMWindow> newWindow;
01231     rv = ww->OpenWindow(aParent,
01232                         DOWNLOAD_MANAGER_FE_URL,
01233                         "_blank",
01234                         "chrome,dialog=no,resizable",
01235                         params,
01236                         getter_AddRefs(newWindow));
01237   }
01238   return rv;
01239 }
01240 
01242 // nsIObserver
01243 
01244 NS_IMETHODIMP
01245 nsDownloadManager::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
01246 {
01247   nsresult rv;
01248   PRInt32 currDownloadCount = 0;
01249 
01250   if (nsCRT::strcmp(aTopic, "oncancel") == 0) {
01251     nsCOMPtr<nsIDownload> dl = do_QueryInterface(aSubject);
01252     nsCOMPtr<nsIURI> target;
01253     dl->GetTarget(getter_AddRefs(target));
01254 
01255     nsAutoString path;
01256     rv = GetFilePathFromURI(target, path);
01257     if (NS_FAILED(rv)) return rv;
01258     
01259     nsStringKey key(path);
01260     if (mCurrDownloads.Exists(&key)) {
01261       // unset dialog since it's closing
01262       nsDownload* download = NS_STATIC_CAST(nsDownload*, mCurrDownloads.Get(&key));
01263       download->SetDialog(nsnull);
01264       
01265       return CancelDownload(path.get());  
01266     }
01267   }
01268   else if (nsCRT::strcmp(aTopic, "quit-application") == 0) {
01269     gStoppingDownloads = PR_TRUE;
01270     if (mCurrDownloads.Count()) {
01271       mCurrDownloads.Enumerate(CancelAllDownloads, this);
01272 
01273       // Download Manager is shutting down! Tell the XPInstallManager to stop
01274       // transferring any files that may have been being downloaded. 
01275       gObserverService->NotifyObservers(mXPIProgress, "xpinstall-progress", NS_LITERAL_STRING("cancel").get());
01276 
01277       // Now go and update the datasource so that we "cancel" all paused downloads. 
01278       SaveState();
01279     }
01280 
01281     // Now that active downloads have been canceled, remove all downloads if 
01282     // the user's retention policy specifies it. 
01283     if (GetRetentionBehavior() == 1) {
01284       nsCOMPtr<nsIRDFContainer> ctr;
01285       GetDownloadsContainer(getter_AddRefs(ctr));
01286 
01287       StartBatchUpdate();
01288 
01289       nsCOMPtr<nsISupportsArray> ary;
01290       NS_NewISupportsArray(getter_AddRefs(ary));
01291 
01292       if (ary) {
01293         nsCOMPtr<nsISimpleEnumerator> e;
01294         ctr->GetElements(getter_AddRefs(e));
01295 
01296         PRBool hasMore;
01297         e->HasMoreElements(&hasMore);
01298         while(hasMore) {
01299           nsCOMPtr<nsIRDFResource> curr;
01300           e->GetNext(getter_AddRefs(curr));
01301           
01302           ary->AppendElement(curr);
01303           
01304           e->HasMoreElements(&hasMore);
01305         }
01306 
01307         // Now Remove all the downloads. 
01308         PRUint32 cnt;
01309         ary->Count(&cnt);
01310         for (PRUint32 i = 0; i < cnt; ++i) {
01311           nsCOMPtr<nsIRDFResource> download(do_QueryElementAt(ary, i));
01312           // Here we use the internal RemoveDownload method, and only here
01313           // because this is _after_ the download table |mCurrDownloads| has been 
01314           // cleared of active downloads, so we know we're not accidentally
01315           // clearing active downloads. 
01316           RemoveDownload(download);
01317         }
01318       }
01319 
01320       EndBatchUpdate();
01321     }
01322   }
01323   else if (nsCRT::strcmp(aTopic, "quit-application-requested") == 0 && 
01324            (currDownloadCount = mCurrDownloads.Count())) {
01325     nsCOMPtr<nsISupportsPRBool> cancelDownloads(do_QueryInterface(aSubject));
01326 #ifndef XP_MACOSX
01327     ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
01328                            NS_LITERAL_STRING("quitCancelDownloadsAlertTitle").get(),
01329                            NS_LITERAL_STRING("quitCancelDownloadsAlertMsgMultiple").get(),
01330                            NS_LITERAL_STRING("quitCancelDownloadsAlertMsg").get(),
01331                            NS_LITERAL_STRING("dontQuitButtonWin").get());
01332 #else
01333     ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
01334                            NS_LITERAL_STRING("quitCancelDownloadsAlertTitle").get(),
01335                            NS_LITERAL_STRING("quitCancelDownloadsAlertMsgMacMultiple").get(),
01336                            NS_LITERAL_STRING("quitCancelDownloadsAlertMsgMac").get(),
01337                            NS_LITERAL_STRING("dontQuitButtonMac").get());
01338 #endif
01339   }
01340   else if (nsCRT::strcmp(aTopic, "offline-requested") == 0 && 
01341            (currDownloadCount = mCurrDownloads.Count())) {
01342     nsCOMPtr<nsISupportsPRBool> cancelDownloads(do_QueryInterface(aSubject));
01343     ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
01344                            NS_LITERAL_STRING("offlineCancelDownloadsAlertTitle").get(),
01345                            NS_LITERAL_STRING("offlineCancelDownloadsAlertMsgMultiple").get(),
01346                            NS_LITERAL_STRING("offlineCancelDownloadsAlertMsg").get(),
01347                            NS_LITERAL_STRING("dontGoOfflineButton").get());
01348     PRBool data;
01349     cancelDownloads->GetData(&data);
01350     if (!data) {
01351       gStoppingDownloads = PR_TRUE;
01352 
01353       // Network is going down! Tell the XPInstallManager to stop
01354       // transferring any files that may have been being downloaded. 
01355       gObserverService->NotifyObservers(mXPIProgress, "xpinstall-progress", NS_LITERAL_STRING("cancel").get());
01356     
01357       mCurrDownloads.Enumerate(CancelAllDownloads, this);
01358       gStoppingDownloads = PR_FALSE;
01359     }
01360   }
01361   else if (nsCRT::strcmp(aTopic, "alertclickcallback") == 0)
01362   {
01363     // Attempt to locate a browser window to parent the download manager to
01364     nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
01365     nsCOMPtr<nsIDOMWindowInternal> browserWindow;
01366     if (wm)
01367       wm->GetMostRecentWindow(NS_LITERAL_STRING("navigator:browser").get(), getter_AddRefs(browserWindow));
01368 
01369     return OpenDownloadManager(PR_TRUE, -1, nsnull, browserWindow);
01370   }
01371   return NS_OK;
01372 }
01373 
01374 void
01375 nsDownloadManager::ConfirmCancelDownloads(PRInt32 aCount, nsISupportsPRBool* aCancelDownloads,
01376                                           const PRUnichar* aTitle, 
01377                                           const PRUnichar* aCancelMessageMultiple, 
01378                                           const PRUnichar* aCancelMessageSingle,
01379                                           const PRUnichar* aDontCancelButton)
01380 {
01381   nsXPIDLString title, message, quitButton, dontQuitButton;
01382   
01383   nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(kStringBundleServiceCID);
01384   nsCOMPtr<nsIStringBundle> bundle;
01385   if (bundleService)
01386     bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, getter_AddRefs(bundle));
01387   if (bundle) {
01388     bundle->GetStringFromName(aTitle, getter_Copies(title));    
01389 
01390     nsAutoString countString;
01391     countString.AppendInt(aCount);
01392     const PRUnichar* strings[1] = { countString.get() };
01393     if (aCount > 1) {
01394       bundle->FormatStringFromName(aCancelMessageMultiple, strings, 1, getter_Copies(message));
01395       bundle->FormatStringFromName(NS_LITERAL_STRING("cancelDownloadsOKTextMultiple").get(), strings, 1, getter_Copies(quitButton));
01396     }
01397     else {
01398       bundle->GetStringFromName(aCancelMessageSingle, getter_Copies(message));
01399       bundle->GetStringFromName(NS_LITERAL_STRING("cancelDownloadsOKText").get(), getter_Copies(quitButton));
01400     }
01401 
01402     bundle->GetStringFromName(aDontCancelButton, getter_Copies(dontQuitButton));
01403   }
01404 
01405   // Get Download Manager window, to be parent of alert.
01406   nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
01407   nsCOMPtr<nsIDOMWindowInternal> dmWindow;
01408   if (wm)
01409     wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(), getter_AddRefs(dmWindow));
01410 
01411   // Show alert.
01412   nsCOMPtr<nsIPromptService> prompter(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
01413   if (prompter) {
01414     PRInt32 flags = (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_0) + (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_1);
01415     PRBool nothing = PR_FALSE;
01416     PRInt32 button;
01417     prompter->ConfirmEx(dmWindow, title, message, flags, quitButton.get(), dontQuitButton.get(), nsnull, nsnull, &nothing, &button);
01418 
01419     aCancelDownloads->SetData(button == 1);
01420   }
01421 }
01422 
01424 // nsIXPInstallManagerUI
01425 NS_IMETHODIMP
01426 nsDownloadManager::GetXpiProgress(nsIXPIProgressDialog** aProgress)
01427 {
01428   *aProgress = mXPIProgress;
01429   NS_IF_ADDREF(*aProgress);
01430   return NS_OK;
01431 }
01432 
01433 NS_IMETHODIMP
01434 nsDownloadManager::GetHasActiveXPIOperations(PRBool* aHasOps)
01435 {
01436   nsIXPIProgressDialog* dialog = mXPIProgress.get();
01437   nsXPIProgressListener* listener = NS_STATIC_CAST(nsXPIProgressListener*, dialog);
01438   *aHasOps = !mXPIProgress ? PR_FALSE : listener->HasActiveXPIOperations();
01439   return NS_OK;
01440 }
01441 
01443 // nsXPIProgressListener
01444 
01445 NS_IMPL_ISUPPORTS1(nsXPIProgressListener, nsIXPIProgressDialog)
01446 
01447 nsXPIProgressListener::nsXPIProgressListener(nsDownloadManager* aDownloadManager)
01448 {
01449   NS_NewISupportsArray(getter_AddRefs(mDownloads));
01450 
01451   mDownloadManager = aDownloadManager;
01452 }
01453 
01454 nsXPIProgressListener::~nsXPIProgressListener()
01455 {
01456   // Release any remaining references to objects held by the downloads array
01457   mDownloads->Clear();
01458 
01459   mDownloadManager = nsnull;
01460 }
01461 
01462 void
01463 nsXPIProgressListener::AddDownload(nsIDownload* aDownload)
01464 {
01465   PRUint32 cnt;
01466   mDownloads->Count(&cnt);
01467   PRBool foundMatch = PR_FALSE;
01468 
01469   nsCOMPtr<nsIURI> uri1, uri2;
01470   for (PRUint32 i = 0; i < cnt; ++i) {
01471     nsCOMPtr<nsIDownload> download(do_QueryElementAt(mDownloads, i));
01472     download->GetSource(getter_AddRefs(uri1));
01473     aDownload->GetSource(getter_AddRefs(uri2));
01474 
01475     uri1->Equals(uri2, &foundMatch);
01476     if (foundMatch)
01477       break;
01478   }
01479   if (!foundMatch)
01480     mDownloads->AppendElement(aDownload);
01481 }
01482 
01483 void 
01484 nsXPIProgressListener::RemoveDownloadAtIndex(PRUint32 aIndex)
01485 {
01486   mDownloads->RemoveElementAt(aIndex);
01487 }
01488 
01489 PRBool
01490 nsXPIProgressListener::HasActiveXPIOperations()
01491 {
01492   PRUint32 count;
01493   mDownloads->Count(&count);
01494   return count != 0;
01495 }
01496 
01498 // nsIXPIProgressDialog
01499 NS_IMETHODIMP
01500 nsXPIProgressListener::OnStateChange(PRUint32 aIndex, PRInt16 aState, PRInt32 aValue)
01501 {
01502   nsCOMPtr<nsIWebProgressListener> wpl(do_QueryElementAt(mDownloads, aIndex));
01503   nsIWebProgressListener* temp = wpl.get();
01504   nsDownload* dl = NS_STATIC_CAST(nsDownload*, temp);
01505   // Sometimes we get XPInstall progress notifications after everything is done, and there's
01506   // no more active downloads... this null check is to prevent a crash in this case.
01507   if (!dl) 
01508     return NS_ERROR_FAILURE;
01509 
01510   switch (aState) {
01511   case nsIXPIProgressDialog::DOWNLOAD_START:
01512     wpl->OnStateChange(nsnull, nsnull, nsIWebProgressListener::STATE_START, 0);
01513 
01514     dl->SetDownloadState(nsIXPInstallManagerUI::INSTALL_DOWNLOADING);
01515     AssertProgressInfoForDownload(dl);
01516     break;
01517   case nsIXPIProgressDialog::DOWNLOAD_DONE:
01518     break;
01519   case nsIXPIProgressDialog::INSTALL_START:
01520     dl->SetDownloadState(nsIXPInstallManagerUI::INSTALL_INSTALLING);
01521     AssertProgressInfoForDownload(dl);
01522     break;
01523   case nsIXPIProgressDialog::INSTALL_DONE:
01524     wpl->OnStateChange(nsnull, nsnull, nsIWebProgressListener::STATE_STOP, 0);
01525     
01526     dl->SetDownloadState(nsIXPInstallManagerUI::INSTALL_FINISHED);
01527     AssertProgressInfoForDownload(dl);
01528     
01529     // Now, remove it from our internal bookkeeping list. 
01530     RemoveDownloadAtIndex(aIndex);
01531     break;
01532   case nsIXPIProgressDialog::DIALOG_CLOSE:
01533     // Close now, if we're allowed to. 
01534     gObserverService->NotifyObservers(nsnull, "xpinstall-dialog-close", nsnull);
01535 
01536     if (!gStoppingDownloads) {
01537       nsCOMPtr<nsIStringBundleService> sbs(do_GetService("@mozilla.org/intl/stringbundle;1"));
01538       nsCOMPtr<nsIStringBundle> brandBundle, xpinstallBundle;
01539       sbs->CreateBundle("chrome://branding/locale/brand.properties", getter_AddRefs(brandBundle));
01540       sbs->CreateBundle("chrome://mozapps/locale/xpinstall/xpinstallConfirm.properties", getter_AddRefs(xpinstallBundle));
01541 
01542       nsXPIDLString brandShortName, message, title;
01543       brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(), getter_Copies(brandShortName));
01544       const PRUnichar* strings[1] = { brandShortName.get() };
01545       xpinstallBundle->FormatStringFromName(NS_LITERAL_STRING("installComplete").get(), strings, 1, getter_Copies(message));
01546       xpinstallBundle->GetStringFromName(NS_LITERAL_STRING("installCompleteTitle").get(), getter_Copies(title));
01547 
01548       nsCOMPtr<nsIPromptService> ps(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
01549       ps->Alert(nsnull, title, message);
01550     }
01551 
01552     break;
01553   }
01554 
01555   return NS_OK;
01556 }
01557 
01558 NS_IMETHODIMP
01559 nsXPIProgressListener::OnProgress(PRUint32 aIndex, PRUint64 aValue, PRUint64 aMaxValue)
01560 {
01561   nsCOMPtr<nsIWebProgressListener> wpl(do_QueryElementAt(mDownloads, aIndex));
01562   // XXX truncates 64-bit to 32 bit
01563   if (wpl) 
01564     return wpl->OnProgressChange(nsnull, nsnull, 0, 0, nsUint64(aValue),
01565                                  nsUint64(aMaxValue));
01566   return NS_OK;
01567 }
01568 
01569 void 
01570 nsXPIProgressListener::AssertProgressInfoForDownload(nsDownload* aDownload)
01571 {
01572   nsCOMPtr<nsIURI> target;
01573   aDownload->GetTarget(getter_AddRefs(target));
01574 
01575   nsAutoString path;
01576   GetFilePathFromURI(target, path);
01577 
01578   mDownloadManager->AssertProgressInfoFor(path.get());
01579 }
01580 
01582 // nsDownloadsDataSource
01583 //   XXXben I'm making this datasource proxy its own object now and passing 
01584 //          IconURL requests through it so that the framework is in place by
01585 //          0.9 when the time comes to upgrade to the Magical World of Mork.
01586 //          
01587 //          Eventually I want to abstract away almost all direct dealings with 
01588 //          this datasource into functions on this object, to simplify the 
01589 //          code in the download manager service and front end.
01590 
01591 NS_IMPL_ISUPPORTS2(nsDownloadsDataSource, nsIRDFDataSource, nsIRDFRemoteDataSource)
01592 
01593 nsresult
01594 nsDownloadsDataSource::LoadDataSource()
01595 {
01596   nsCOMPtr<nsIFile> downloadsFile;
01597   nsresult rv = NS_GetSpecialDirectory(NS_APP_DOWNLOADS_50_FILE, getter_AddRefs(downloadsFile));
01598   if (NS_FAILED(rv)) return rv;
01599 
01600   nsCAutoString downloadsDB;
01601   NS_GetURLSpecFromFile(downloadsFile, downloadsDB);
01602 
01603   return gRDFService->GetDataSourceBlocking(downloadsDB.get(), getter_AddRefs(mInner));
01604 }
01605 
01607 // nsIRDFDataSource
01608 /* readonly attribute string URI; */
01609 NS_IMETHODIMP 
01610 nsDownloadsDataSource::GetURI(char** aURI)
01611 {
01612   return mInner->GetURI(aURI);
01613 }
01614 
01615 NS_IMETHODIMP 
01616 nsDownloadsDataSource::GetSource(nsIRDFResource* aProperty, nsIRDFNode* aTarget, PRBool aTruthValue, nsIRDFResource** aResult)
01617 {
01618   return mInner->GetSource(aProperty, aTarget, aTruthValue, aResult);
01619 }
01620 
01621 NS_IMETHODIMP 
01622 nsDownloadsDataSource::GetSources(nsIRDFResource* aProperty, nsIRDFNode* aTarget, PRBool aTruthValue, nsISimpleEnumerator** aResult)
01623 {
01624   return mInner->GetSources(aProperty, aTarget, aTruthValue, aResult);
01625 }
01626 
01627 NS_IMETHODIMP 
01628 nsDownloadsDataSource::GetTarget(nsIRDFResource* aSource, nsIRDFResource* aProperty, PRBool aTruthValue, nsIRDFNode** aResult)
01629 {
01630   if (aProperty == gNC_IconURL) {
01631     PRBool hasIconURLArc;
01632     nsresult rv = mInner->HasArcOut(aSource, aProperty, &hasIconURLArc);
01633     if (NS_FAILED(rv)) return rv;
01634 
01635     // If the download entry doesn't have a IconURL property, use the 
01636     // File property instead, so that moz-icon works. 
01637     if (!hasIconURLArc) {
01638       nsCOMPtr<nsIRDFNode> target;
01639       rv = mInner->GetTarget(aSource, gNC_File, aTruthValue, getter_AddRefs(target));
01640       if (NS_SUCCEEDED(rv) && target) {
01641         nsXPIDLCString path;
01642         nsCOMPtr<nsIRDFResource> res(do_QueryInterface(target));
01643         res->GetValue(getter_Copies(path));
01644 
01645         // XXXmano: See bug 239948 and bug 335725, we need to do this
01646         // until we use real URLs all the time.
01647         PRBool alreadyURL = PR_FALSE;
01648         nsCOMPtr<nsIURI> fileURI;
01649         NS_NewURI(getter_AddRefs(fileURI), path);
01650         if (fileURI) {
01651           nsCOMPtr<nsIURL> url(do_QueryInterface(fileURI, &rv));
01652           if (NS_SUCCEEDED(rv))
01653             alreadyURL = PR_TRUE;
01654         }
01655         
01656         nsCAutoString fileURL;
01657         if (alreadyURL) {
01658           fileURL.Assign(path);
01659         }
01660         else {
01661           nsCOMPtr<nsILocalFile> lf(do_CreateInstance("@mozilla.org/file/local;1"));
01662           lf->InitWithNativePath(path);
01663           nsCOMPtr<nsIIOService> ios(do_GetService("@mozilla.org/network/io-service;1"));
01664           nsCOMPtr<nsIProtocolHandler> ph;
01665           ios->GetProtocolHandler("file", getter_AddRefs(ph));
01666           nsCOMPtr<nsIFileProtocolHandler> fph(do_QueryInterface(ph));
01667 
01668           fph->GetURLSpecFromFile(lf, fileURL);
01669         }
01670 
01671         nsAutoString iconURL(NS_LITERAL_STRING("moz-icon://"));
01672         AppendUTF8toUTF16(fileURL, iconURL);
01673         iconURL.AppendLiteral("?size=32");
01674 
01675         nsCOMPtr<nsIRDFResource> result;
01676         gRDFService->GetUnicodeResource(iconURL, getter_AddRefs(result));
01677 
01678         *aResult = result;
01679         NS_IF_ADDREF(*aResult);
01680 
01681         return NS_OK;
01682       }
01683     }
01684   }
01685   // Either it's some other property, or we DO have an IconURL property
01686   // and we just need to get the value from the real datasource. 
01687   return mInner->GetTarget(aSource, aProperty, aTruthValue, aResult);
01688 }
01689 
01690 NS_IMETHODIMP 
01691 nsDownloadsDataSource::GetTargets(nsIRDFResource* aSource, nsIRDFResource* aProperty, PRBool aTruthValue, nsISimpleEnumerator** aResult)
01692 {
01693   if (aProperty == gNC_IconURL) {
01694     nsCOMPtr<nsIRDFNode> target;
01695     nsresult rv = GetTarget(aSource, aProperty, aTruthValue, getter_AddRefs(target));
01696     if (NS_FAILED(rv)) return rv;
01697 
01698     if (NS_SUCCEEDED(rv)) 
01699       return NS_NewSingletonEnumerator(aResult, target);
01700   }
01701   return mInner->GetTargets(aSource, aProperty, aTruthValue, aResult);
01702 }
01703 
01704 NS_IMETHODIMP 
01705 nsDownloadsDataSource::Assert(nsIRDFResource* aSource, nsIRDFResource* aProperty, nsIRDFNode* aTarget, PRBool aTruthValue)
01706 {
01707   return mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
01708 }
01709 
01710 NS_IMETHODIMP 
01711 nsDownloadsDataSource::Unassert(nsIRDFResource* aSource, nsIRDFResource* aProperty, nsIRDFNode* aTarget)
01712 {
01713   return mInner->Unassert(aSource, aProperty, aTarget);
01714 }
01715 
01716 NS_IMETHODIMP 
01717 nsDownloadsDataSource::Change(nsIRDFResource* aSource, nsIRDFResource* aProperty, nsIRDFNode* aOldTarget, nsIRDFNode* aNewTarget)
01718 {
01719   return mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
01720 }
01721 
01722 NS_IMETHODIMP 
01723 nsDownloadsDataSource::Move(nsIRDFResource* aOldSource, nsIRDFResource* aNewSource, nsIRDFResource* aProperty, nsIRDFNode* aTarget)
01724 {
01725   return mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
01726 }
01727 
01728 NS_IMETHODIMP 
01729 nsDownloadsDataSource::HasAssertion(nsIRDFResource* aSource, nsIRDFResource* aProperty, nsIRDFNode* aTarget, PRBool aTruthValue, PRBool* aResult)
01730 {
01731   return mInner->HasAssertion(aSource, aProperty, aTarget, aTruthValue, aResult);
01732 }
01733 
01734 NS_IMETHODIMP 
01735 nsDownloadsDataSource::AddObserver(nsIRDFObserver* aObserver)
01736 {
01737   return mInner->AddObserver(aObserver);
01738 }
01739 
01740 NS_IMETHODIMP 
01741 nsDownloadsDataSource::RemoveObserver(nsIRDFObserver* aObserver)
01742 {
01743   return mInner->RemoveObserver(aObserver);
01744 }
01745 
01746 NS_IMETHODIMP 
01747 nsDownloadsDataSource::ArcLabelsIn(nsIRDFNode* aNode, nsISimpleEnumerator** aResult)
01748 {
01749   return mInner->ArcLabelsIn(aNode, aResult);
01750 }
01751 
01752 NS_IMETHODIMP 
01753 nsDownloadsDataSource::ArcLabelsOut(nsIRDFResource* aSource, nsISimpleEnumerator** aResult)
01754 {
01755   return mInner->ArcLabelsOut(aSource, aResult);
01756 }
01757 
01758 NS_IMETHODIMP 
01759 nsDownloadsDataSource::GetAllResources(nsISimpleEnumerator** aResult)
01760 {
01761   return mInner->GetAllResources(aResult);
01762 }
01763 
01764 NS_IMETHODIMP 
01765 nsDownloadsDataSource::IsCommandEnabled(nsISupportsArray* aSources, nsIRDFResource* aCommand, nsISupportsArray* aArguments, PRBool* aResult)
01766 {
01767   return mInner->IsCommandEnabled(aSources, aCommand, aArguments, aResult);
01768 }
01769 
01770 NS_IMETHODIMP 
01771 nsDownloadsDataSource::DoCommand(nsISupportsArray* aSources, nsIRDFResource* aCommand, nsISupportsArray* aArguments)
01772 {
01773   return mInner->DoCommand(aSources, aCommand, aArguments);
01774 }
01775 
01776 NS_IMETHODIMP 
01777 nsDownloadsDataSource::GetAllCmds(nsIRDFResource* aSource, nsISimpleEnumerator** aResult)
01778 {
01779   return mInner->GetAllCmds(aSource, aResult);
01780 }
01781 
01782 NS_IMETHODIMP 
01783 nsDownloadsDataSource::HasArcIn(nsIRDFNode* aNode, nsIRDFResource* aArc, PRBool* aResult)
01784 {
01785   return mInner->HasArcIn(aNode, aArc, aResult);
01786 }
01787 
01788 NS_IMETHODIMP 
01789 nsDownloadsDataSource::HasArcOut(nsIRDFResource* aSource, nsIRDFResource* aArc, PRBool* aResult)
01790 {
01791   return mInner->HasArcOut(aSource, aArc, aResult);
01792 }
01793 
01794 NS_IMETHODIMP 
01795 nsDownloadsDataSource::BeginUpdateBatch()
01796 {
01797   return mInner->BeginUpdateBatch();
01798 }
01799 
01800 NS_IMETHODIMP 
01801 nsDownloadsDataSource::EndUpdateBatch()
01802 {
01803   return mInner->EndUpdateBatch();
01804 }
01805 
01807 // nsIRDFRemoteDataSource
01808 
01809 NS_IMETHODIMP
01810 nsDownloadsDataSource::GetLoaded(PRBool* aResult)
01811 {
01812   nsCOMPtr<nsIRDFRemoteDataSource> rds(do_QueryInterface(mInner));
01813   return rds->GetLoaded(aResult);
01814 }
01815 
01816 NS_IMETHODIMP
01817 nsDownloadsDataSource::Init(const char* aURI)
01818 {
01819   nsCOMPtr<nsIRDFRemoteDataSource> rds(do_QueryInterface(mInner));
01820   return rds->Init(aURI);
01821 }
01822 
01823 NS_IMETHODIMP
01824 nsDownloadsDataSource::Refresh(PRBool aBlocking)
01825 {
01826   nsCOMPtr<nsIRDFRemoteDataSource> rds(do_QueryInterface(mInner));
01827   return rds->Refresh(aBlocking);
01828 }
01829 
01830 NS_IMETHODIMP
01831 nsDownloadsDataSource::Flush()
01832 {
01833   nsCOMPtr<nsIRDFRemoteDataSource> rds(do_QueryInterface(mInner));
01834   return rds->Flush();
01835 }
01836 
01837 NS_IMETHODIMP
01838 nsDownloadsDataSource::FlushTo(const char* aURI)
01839 {
01840   nsCOMPtr<nsIRDFRemoteDataSource> rds(do_QueryInterface(mInner));
01841   return rds->FlushTo(aURI);
01842 }
01843 
01845 // nsDownload
01846 
01847 NS_IMPL_ISUPPORTS5(nsDownload, nsIDownload, nsIDownload_MOZILLA_1_8_BRANCH,
01848                    nsITransfer, nsIWebProgressListener, nsIWebProgressListener2)
01849 
01850 nsDownload::nsDownload():mDownloadState(nsIDownloadManager::DOWNLOAD_NOTSTARTED),
01851                          mPercentComplete(0),
01852                          mCurrBytes(LL_ZERO),
01853                          mMaxBytes(LL_ZERO),
01854                          mStartTime(LL_ZERO),
01855                          mLastUpdate(PR_Now() - (PRUint32)gInterval),
01856                          mPaused(PR_FALSE),
01857                          mSpeed(0)
01858 {
01859 }
01860 
01861 nsDownload::~nsDownload()
01862 {  
01863 }
01864 
01865 nsresult
01866 nsDownload::SetDownloadManager(nsDownloadManager* aDownloadManager)
01867 {
01868   mDownloadManager = aDownloadManager;
01869   return NS_OK;
01870 }
01871 
01872 nsresult
01873 nsDownload::SetDialogListener(nsIWebProgressListener* aDialogListener)
01874 {
01875   mDialogListener = aDialogListener;
01876   return NS_OK;
01877 }
01878 
01879 nsresult
01880 nsDownload::GetDialogListener(nsIWebProgressListener** aDialogListener)
01881 {
01882   *aDialogListener = mDialogListener;
01883   NS_IF_ADDREF(*aDialogListener);
01884   return NS_OK;
01885 }
01886 
01887 nsresult
01888 nsDownload::SetDialog(nsIProgressDialog* aDialog)
01889 {
01890   mDialog = aDialog;
01891   return NS_OK;
01892 }
01893 
01894 nsresult
01895 nsDownload::GetDialog(nsIProgressDialog** aDialog)
01896 {
01897   *aDialog = mDialog;
01898   NS_IF_ADDREF(*aDialog);
01899   return NS_OK;
01900 }
01901 
01902 nsresult
01903 nsDownload::SetTempFile(nsILocalFile* aTempFile)
01904 {
01905   mTempFile = aTempFile;
01906   return NS_OK;
01907 }
01908 
01909 nsresult
01910 nsDownload::GetTempFile(nsILocalFile** aTempFile)
01911 {
01912   *aTempFile = mTempFile;
01913   NS_IF_ADDREF(*aTempFile);
01914   return NS_OK;
01915 }
01916 
01917 DownloadState
01918 nsDownload::GetDownloadState()
01919 {
01920   return mDownloadState;
01921 }
01922 
01923 void
01924 nsDownload::SetDownloadState(DownloadState aState)
01925 {
01926   mDownloadState = aState;
01927 }
01928 
01929 DownloadType
01930 nsDownload::GetDownloadType()
01931 {
01932   return mDownloadType;
01933 }
01934 
01935 void
01936 nsDownload::SetDownloadType(DownloadType aType)
01937 {
01938   mDownloadType = aType;
01939 }
01940 
01941 nsresult
01942 nsDownload::SetCancelable(nsICancelable* aCancelable)
01943 {
01944   mCancelable = aCancelable;
01945   return NS_OK;
01946 }
01947 
01948 nsresult
01949 nsDownload::SetSource(nsIURI* aSource)
01950 {
01951   mSource = aSource;
01952   return NS_OK;
01953 }
01954 
01955 nsresult
01956 nsDownload::SetTarget(nsIURI* aTarget)
01957 {
01958   mTarget = aTarget;
01959   return NS_OK;
01960 }
01961 
01962 
01963 nsresult
01964 nsDownload::SetDisplayName(const PRUnichar* aDisplayName)
01965 {
01966   mDisplayName = aDisplayName;
01967 
01968   nsCOMPtr<nsIRDFDataSource> ds;
01969   mDownloadManager->GetDatasource(getter_AddRefs(ds));
01970 
01971   nsCOMPtr<nsIRDFLiteral> nameLiteral;
01972   nsCOMPtr<nsIRDFResource> res;
01973   nsAutoString path;
01974   nsresult rv = GetFilePathFromURI(mTarget, path);
01975   if (NS_FAILED(rv)) return rv;
01976 
01977   gRDFService->GetUnicodeResource(path, getter_AddRefs(res));
01978   
01979   gRDFService->GetLiteral(aDisplayName, getter_AddRefs(nameLiteral));
01980   ds->Assert(res, gNC_Name, nameLiteral, PR_TRUE);
01981 
01982   return NS_OK;
01983 }
01984 
01985 nsDownload::TransferInformation
01986 nsDownload::GetTransferInformation()
01987 {
01988   return TransferInformation(mCurrBytes, mMaxBytes);
01989 }
01990 
01991 nsresult
01992 nsDownload::SetStartTime(PRInt64 aStartTime)
01993 {
01994   mStartTime = aStartTime;
01995   mLastUpdate = aStartTime;
01996   return NS_OK;
01997 }
01998 
01999 nsresult
02000 nsDownload::SetMIMEInfo(nsIMIMEInfo *aMIMEInfo)
02001 {
02002   mMIMEInfo = aMIMEInfo;
02003   return NS_OK;
02004 }
02005 
02007 // nsIWebProgressListener2
02008 
02009 NS_IMETHODIMP
02010 nsDownload::OnProgressChange64(nsIWebProgress *aWebProgress,
02011                                nsIRequest *aRequest,
02012                                PRInt64 aCurSelfProgress,
02013                                PRInt64 aMaxSelfProgress,
02014                                PRInt64 aCurTotalProgress,
02015                                PRInt64 aMaxTotalProgress)
02016 {
02017   if (!mRequest)
02018     mRequest = aRequest; // used for pause/resume
02019 
02020   // filter notifications since they come in so frequently
02021   PRTime now = PR_Now();
02022   nsInt64 delta = now - mLastUpdate;
02023   if (delta < gInterval)
02024     return NS_OK;
02025 
02026   mLastUpdate = now;
02027 
02028   if (mDownloadState == nsIDownloadManager::DOWNLOAD_NOTSTARTED) {
02029     nsAutoString path;
02030     nsresult rv = GetFilePathFromURI(mTarget, path);
02031     if (NS_FAILED(rv)) return rv;
02032 
02033     mDownloadState = nsIDownloadManager::DOWNLOAD_DOWNLOADING;
02034     mDownloadManager->DownloadStarted(path.get());
02035   }
02036 
02037   // Calculate the speed using the elapsed delta time and bytes downloaded
02038   // during that time for more accuracy.
02039   double elapsedSecs = double(delta) / PR_USEC_PER_SEC;
02040   if (elapsedSecs > 0) {
02041     nsUint64 curTotalProgress = (PRUint64)aCurTotalProgress;
02042     nsUint64 diffBytes = curTotalProgress - nsUint64(mCurrBytes);
02043     double speed = double(diffBytes) / elapsedSecs;
02044     if (LL_IS_ZERO(mCurrBytes))
02045       mSpeed = speed;
02046     else {
02047       // Calculate 'smoothed average' of 10 readings.
02048       mSpeed = mSpeed * 0.9 + speed * 0.1;
02049     }
02050   }
02051 
02052   if (aMaxTotalProgress > 0)
02053     mPercentComplete = (PRInt32)((PRFloat64)aCurTotalProgress * 100 / aMaxTotalProgress + .5);
02054   else
02055     mPercentComplete = -1;
02056 
02057   mCurrBytes = aCurTotalProgress;
02058   mMaxBytes = aMaxTotalProgress;
02059 
02060   if (mDownloadManager->NeedsUIUpdate()) {
02061     nsCOMPtr<nsIDownloadProgressListener> dpl;
02062     mDownloadManager->GetInternalListener(getter_AddRefs(dpl));
02063     if (dpl) {
02064       dpl->OnProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress,
02065                             aCurTotalProgress, aMaxTotalProgress, this);
02066     }
02067   }
02068 
02069   return NS_OK;
02070 
02071 }
02072 
02074 // nsIWebProgressListener
02075 
02076 NS_IMETHODIMP
02077 nsDownload::OnProgressChange(nsIWebProgress *aWebProgress,
02078                              nsIRequest *aRequest,
02079                              PRInt32 aCurSelfProgress,
02080                              PRInt32 aMaxSelfProgress,
02081                              PRInt32 aCurTotalProgress,
02082                              PRInt32 aMaxTotalProgress)
02083 {
02084   return OnProgressChange64(aWebProgress, aRequest,
02085                             aCurSelfProgress, aMaxSelfProgress,
02086                             aCurTotalProgress, aMaxTotalProgress);
02087 }
02088 
02089 NS_IMETHODIMP
02090 nsDownload::OnLocationChange(nsIWebProgress *aWebProgress,
02091                              nsIRequest *aRequest, nsIURI *aLocation)
02092 {
02093   return NS_OK;
02094 }
02095 
02096 NS_IMETHODIMP
02097 nsDownload::OnStatusChange(nsIWebProgress *aWebProgress,
02098                            nsIRequest *aRequest, nsresult aStatus,
02099                            const PRUnichar *aMessage)
02100 {   
02101   if (NS_FAILED(aStatus)) {
02102     mDownloadState = nsIDownloadManager::DOWNLOAD_FAILED;
02103     nsAutoString path;
02104     nsresult rv = GetFilePathFromURI(mTarget, path);
02105     if (NS_SUCCEEDED(rv)) {
02106       mDownloadManager->DownloadEnded(path.get(), nsnull);
02107       gObserverService->NotifyObservers(NS_STATIC_CAST(nsIDownload *, this), "dl-failed", nsnull);                     
02108     }
02109 
02110     // Get title for alert.
02111     nsXPIDLString title;
02112     
02113     nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(kStringBundleServiceCID, &rv);
02114     nsCOMPtr<nsIStringBundle> bundle;
02115     if (bundleService)
02116       rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, getter_AddRefs(bundle));
02117     if (bundle)
02118       bundle->GetStringFromName(NS_LITERAL_STRING("downloadErrorAlertTitle").get(), getter_Copies(title));    
02119 
02120     // Get Download Manager window, to be parent of alert.
02121     nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
02122     nsCOMPtr<nsIDOMWindowInternal> dmWindow;
02123     if (wm)
02124       wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(), getter_AddRefs(dmWindow));
02125 
02126     // Show alert.
02127     nsCOMPtr<nsIPromptService> prompter(do_GetService("@mozilla.org/embedcomp/prompt-service;1"));
02128     if (prompter)
02129       prompter->Alert(dmWindow, title, aMessage);
02130   }
02131 
02132   return NS_OK;
02133 }
02134 
02135 NS_IMETHODIMP
02136 nsDownload::OnStateChange(nsIWebProgress* aWebProgress,
02137                           nsIRequest* aRequest, PRUint32 aStateFlags,
02138                           nsresult aStatus)
02139 {
02140   // Record the start time only if it hasn't been set.
02141   if (LL_IS_ZERO(mStartTime) && (aStateFlags & STATE_START))
02142     SetStartTime(PR_Now());
02143 
02144   // When we break the ref cycle with mCancelable, we don't want to lose
02145   // access to out member vars!
02146   nsCOMPtr<nsIDownload> kungFuDeathGrip;
02147   CallQueryInterface(this, NS_STATIC_CAST(nsIDownload**,
02148                                           getter_AddRefs(kungFuDeathGrip)));
02149 
02150   // We need to update mDownloadState before updating the dialog, because
02151   // that will close and call CancelDownload if it was the last open window.
02152   nsresult rv = NS_OK;
02153 
02154   nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID));
02155 
02156   if (aStateFlags & STATE_STOP) {
02157     if (nsDownloadManager::IsInFinalStage(mDownloadState)) {
02158       if (mDownloadState != nsIXPInstallManagerUI::INSTALL_INSTALLING)
02159         mDownloadState = nsIDownloadManager::DOWNLOAD_FINISHED;
02160       else
02161         mDownloadState = nsIXPInstallManagerUI::INSTALL_FINISHED;
02162 
02163       // Set file size at the end of a tranfer (for unknown transfer amounts)
02164       if (mMaxBytes == -1)
02165         mMaxBytes = mCurrBytes;
02166 
02167       // Files less than 1Kb shouldn't show up as 0Kb.
02168       if (mMaxBytes < 1024) {
02169         mCurrBytes = 1024;
02170         mMaxBytes  = 1024;
02171       }
02172 
02173       mPercentComplete = 100;
02174 
02175       nsAutoString path;
02176       rv = GetFilePathFromURI(mTarget, path);
02177       // can't do an early return; have to break reference cycle below
02178       if (NS_SUCCEEDED(rv))
02179         mDownloadManager->DownloadEnded(path.get(), nsnull);
02180 
02181       // Master pref to control this function. 
02182       PRBool showTaskbarAlert = PR_FALSE;
02183       if (pref)
02184         pref->GetBoolPref(PREF_BDM_SHOWALERTONCOMPLETE, &showTaskbarAlert);
02185 
02186       if (showTaskbarAlert) {
02187         PRInt32 alertInterval = -1;
02188         if (pref)
02189           pref->GetIntPref(PREF_BDM_SHOWALERTINTERVAL, &alertInterval);
02190 
02191         PRInt64 temp, uSecPerMSec, alertIntervalUSec;
02192         LL_I2L(temp, alertInterval);
02193         LL_I2L(uSecPerMSec, PR_USEC_PER_MSEC);
02194         LL_MUL(alertIntervalUSec, temp, uSecPerMSec);
02195         
02196         PRInt64 goat = PR_Now() - mStartTime;
02197         showTaskbarAlert = goat > alertIntervalUSec;
02198         
02199         if (showTaskbarAlert && (mDownloadManager->mCurrDownloads).Count() == 0) {
02200           nsCOMPtr<nsIAlertsService> alerts(do_GetService("@mozilla.org/alerts-service;1"));
02201           if (alerts) {
02202             nsXPIDLString title, message;
02203 
02204             mDownloadManager->mBundle->GetStringFromName(NS_LITERAL_STRING("downloadsCompleteTitle").get(), getter_Copies(title));
02205             mDownloadManager->mBundle->GetStringFromName(NS_LITERAL_STRING("downloadsCompleteMsg").get(), getter_Copies(message));
02206 
02207             PRBool removeWhenDone = mDownloadManager->GetRetentionBehavior() == 0;
02208 
02209 
02210             // If downloads are automatically removed per the user's retention policy, 
02211             // there's no reason to make the text clickable because if it is, they'll
02212             // click open the download manager and the items they downloaded will have
02213             // been removed. 
02214             alerts->ShowAlertNotification(NS_LITERAL_STRING(DOWNLOAD_MANAGER_ALERT_ICON), title, message, !removeWhenDone, 
02215                                           EmptyString(), mDownloadManager);
02216           }
02217         }
02218       }
02219     }
02220 
02221     nsAutoString path;
02222     rv = GetFilePathFromURI(mTarget, path);
02223     if (NS_FAILED(rv))
02224       return rv;
02225 
02226 #ifdef XP_WIN
02227     PRBool addToRecentDocs = PR_TRUE;
02228     if (pref)
02229       pref->GetBoolPref(PREF_BDM_ADDTORECENTDOCS, &addToRecentDocs);
02230 
02231     if (addToRecentDocs) {
02232       LPSHELLFOLDER lpShellFolder = NULL;
02233 
02234       if (SUCCEEDED(::SHGetDesktopFolder(&lpShellFolder))) {
02235         PRUnichar *filePath = ToNewUnicode(path);
02236         LPITEMIDLIST lpItemIDList = NULL;
02237         if (SUCCEEDED(lpShellFolder->ParseDisplayName(NULL, NULL, filePath, NULL, &lpItemIDList, NULL))) {
02238           ::SHAddToRecentDocs(SHARD_PIDL, lpItemIDList);
02239           ::CoTaskMemFree(lpItemIDList);
02240         }
02241         nsMemory::Free(filePath);
02242         lpShellFolder->Release();
02243       }
02244     }
02245 #endif
02246 
02247     gObserverService->NotifyObservers(NS_STATIC_CAST(nsIDownload *, this), "dl-done", nsnull);
02248 
02249     // break the cycle we created in AddDownload
02250     mCancelable = nsnull;
02251 
02252     // Now remove the download if the user's retention policy is "Remove when Done"
02253     if (mDownloadManager->GetRetentionBehavior() == 0)
02254       mDownloadManager->RemoveDownload(path.get());
02255   }
02256 
02257   if (mDownloadManager->NeedsUIUpdate()) {
02258     nsCOMPtr<nsIDownloadProgressListener> dpl;
02259     mDownloadManager->GetInternalListener(getter_AddRefs(dpl));
02260     if (dpl) {
02261       dpl->OnStateChange(aWebProgress, aRequest, aStateFlags, aStatus, this);
02262     }
02263   }
02264 
02265   return rv;
02266 }
02267 
02268 NS_IMETHODIMP
02269 nsDownload::OnSecurityChange(nsIWebProgress *aWebProgress,
02270                              nsIRequest *aRequest, PRUint32 aState)
02271 {
02272   return NS_OK;
02273 }
02274 
02276 // nsIDownload
02277 
02278 NS_IMETHODIMP
02279 nsDownload::Init(nsIURI* aSource,
02280                  nsIURI* aTarget,
02281                  const nsAString& aDisplayName,
02282                  nsIMIMEInfo *aMIMEInfo,
02283                  PRTime aStartTime,
02284                  nsILocalFile* aTempFile,
02285                  nsICancelable* aCancelable)
02286 {
02287   NS_WARNING("Huh...how did we get here?!");
02288   return NS_OK;
02289 }
02290 
02291 NS_IMETHODIMP
02292 nsDownload::GetDisplayName(PRUnichar** aDisplayName)
02293 {
02294   *aDisplayName = ToNewUnicode(mDisplayName);
02295   return NS_OK;
02296 }
02297 
02298 NS_IMETHODIMP
02299 nsDownload::GetCancelable(nsICancelable** aCancelable)
02300 {
02301   *aCancelable = mCancelable;
02302   NS_IF_ADDREF(*aCancelable);
02303   return NS_OK;
02304 }
02305 
02306 NS_IMETHODIMP
02307 nsDownload::GetTarget(nsIURI** aTarget)
02308 {
02309   *aTarget = mTarget;
02310   NS_IF_ADDREF(*aTarget);
02311   return NS_OK;
02312 }
02313 
02314 NS_IMETHODIMP
02315 nsDownload::GetSource(nsIURI** aSource)
02316 {
02317   *aSource = mSource;
02318   NS_IF_ADDREF(*aSource);
02319   return NS_OK;
02320 }
02321 
02322 NS_IMETHODIMP
02323 nsDownload::GetStartTime(PRInt64* aStartTime)
02324 {
02325   *aStartTime = mStartTime;
02326   return NS_OK;
02327 }
02328 
02329 NS_IMETHODIMP
02330 nsDownload::GetPercentComplete(PRInt32* aPercentComplete)
02331 {
02332   *aPercentComplete = mPercentComplete;
02333   return NS_OK;
02334 }
02335 
02336 NS_IMETHODIMP
02337 nsDownload::GetAmountTransferred(PRUint64* aAmountTransferred)
02338 {
02339   *aAmountTransferred = BYTES_TO_KBYTES(mCurrBytes);
02340   return NS_OK;
02341 }
02342 
02343 NS_IMETHODIMP
02344 nsDownload::GetSize(PRUint64* aSize)
02345 {
02346   *aSize = BYTES_TO_KBYTES(mMaxBytes);
02347   return NS_OK;
02348 }
02349 
02350 NS_IMETHODIMP
02351 nsDownload::GetMIMEInfo(nsIMIMEInfo** aMIMEInfo)
02352 {
02353   *aMIMEInfo = mMIMEInfo;
02354   NS_IF_ADDREF(*aMIMEInfo);
02355   return NS_OK;
02356 }
02357 
02358 NS_IMETHODIMP
02359 nsDownload::GetTargetFile(nsILocalFile** aTargetFile)
02360 {
02361   nsresult rv;
02362 
02363   nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget, &rv);
02364   if (NS_FAILED(rv)) return rv;
02365 
02366   nsCOMPtr<nsIFile> file;
02367   rv = fileURL->GetFile(getter_AddRefs(file));
02368   if (NS_SUCCEEDED(rv))
02369     rv = CallQueryInterface(file, aTargetFile);
02370   return rv;
02371 }
02372 
02373 NS_IMETHODIMP
02374 nsDownload::GetSpeed(double* aSpeed)
02375 {
02376   *aSpeed = mSpeed;
02377   return NS_OK;
02378 }
02379 
02380 void
02381 nsDownload::Pause(PRBool aPaused)
02382 {
02383   if (mPaused != aPaused) {
02384     if (mRequest) {
02385       if (aPaused) {
02386         mRequest->Suspend();
02387         mPaused = PR_TRUE;
02388         mDownloadState = nsIDownloadManager::DOWNLOAD_PAUSED;
02389       }
02390       else {
02391         mRequest->Resume();
02392         mPaused = PR_FALSE;
02393         mDownloadState = nsIDownloadManager::DOWNLOAD_DOWNLOADING;
02394       }
02395     }
02396   }
02397 }
02398 
02399 PRBool
02400 nsDownload::IsPaused()
02401 {
02402   return mPaused;
02403 }
02404