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 /* vim:set ts=2 sw=2 sts=2 et cin: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Blake Ross <blaker@netscape.com> (Original Author)
00025  *   Ben Goodger <ben@netscape.com> (Original Author)
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040  
00041 #include "nsDownloadManager.h"
00042 #include "nsIWebProgress.h"
00043 #include "nsIRDFLiteral.h"
00044 #include "rdf.h"
00045 #include "nsNetUtil.h"
00046 #include "nsIDOMWindow.h"
00047 #include "nsIDOMWindowInternal.h"
00048 #include "nsIDOMEvent.h"
00049 #include "nsIDOMEventTarget.h"
00050 #include "nsRDFCID.h"
00051 #include "nsAppDirectoryServiceDefs.h"
00052 #include "nsIWebBrowserPersist.h"
00053 #include "nsIObserver.h"
00054 #include "nsIProgressDialog.h"
00055 #include "nsIWebBrowserPersist.h"
00056 #include "nsIWindowWatcher.h"
00057 #include "nsIStringBundle.h"
00058 #include "nsCRT.h"
00059 #include "nsIWindowMediator.h"
00060 #include "nsIPromptService.h"
00061 #include "nsIObserverService.h"
00062 #include "nsIProfileChangeStatus.h"
00063 #include "nsIPrefService.h"
00064 #include "nsIFileURL.h"
00065 #ifndef MOZ_THUNDERBIRD
00066 #include "nsIAlertsService.h"
00067 #endif
00068 #include "nsEmbedCID.h"
00069 #include "nsInt64.h"
00070 
00071 /* Outstanding issues/todo:
00072  * 1. Implement pause/resume.
00073  */
00074   
00075 #define DOWNLOAD_MANAGER_FE_URL "chrome://communicator/content/downloadmanager/downloadmanager.xul"
00076 #define DOWNLOAD_MANAGER_BUNDLE "chrome://communicator/locale/downloadmanager/downloadmanager.properties"
00077 
00078 static const nsInt64 gInterval((PRUint32)(400 * PR_USEC_PER_MSEC));
00079 
00080 static nsIRDFResource* gNC_DownloadsRoot = nsnull;
00081 static nsIRDFResource* gNC_File = nsnull;
00082 static nsIRDFResource* gNC_URL = nsnull;
00083 static nsIRDFResource* gNC_Name = nsnull;
00084 static nsIRDFResource* gNC_ProgressMode = nsnull;
00085 static nsIRDFResource* gNC_ProgressPercent = nsnull;
00086 static nsIRDFResource* gNC_Transferred = nsnull;
00087 static nsIRDFResource* gNC_DownloadState = nsnull;
00088 static nsIRDFResource* gNC_StatusText = nsnull;
00089 
00090 static nsIRDFService* gRDFService = nsnull;
00091 
00092 static PRInt32 gRefCnt = 0;
00093 
00097 static nsresult
00098 GetFilePathUTF8(nsIURI *aURI, nsACString &aResult)
00099 {
00100   nsresult rv;
00101 
00102   nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
00103   if (NS_FAILED(rv)) return rv;
00104 
00105   nsCOMPtr<nsIFile> file;
00106   rv = fileURL->GetFile(getter_AddRefs(file));
00107   if (NS_FAILED(rv)) return rv;
00108 
00109   nsAutoString path;
00110   rv = file->GetPath(path);
00111   if (NS_SUCCEEDED(rv))
00112     CopyUTF16toUTF8(path, aResult);
00113   return rv;
00114 }
00115 
00117 // nsDownloadManager
00118 
00119 NS_IMPL_ISUPPORTS3(nsDownloadManager, nsIDownloadManager, nsIDOMEventListener, nsIObserver)
00120 
00121 nsDownloadManager::nsDownloadManager() : mBatches(0)
00122 {
00123 }
00124 
00125 nsDownloadManager::~nsDownloadManager()
00126 {
00127   if (--gRefCnt != 0 || !gRDFService)
00128     // Either somebody tried to use |CreateInstance| instead of
00129     // |GetService| or |Init| failed very early, so there's nothing to
00130     // do here.
00131     return;
00132 
00133   gRDFService->UnregisterDataSource(mDataSource);
00134 
00135   NS_IF_RELEASE(gNC_DownloadsRoot);                                             
00136   NS_IF_RELEASE(gNC_File);                                                      
00137   NS_IF_RELEASE(gNC_URL);                                                       
00138   NS_IF_RELEASE(gNC_Name);                                                      
00139   NS_IF_RELEASE(gNC_ProgressMode);
00140   NS_IF_RELEASE(gNC_ProgressPercent);
00141   NS_IF_RELEASE(gNC_Transferred);
00142   NS_IF_RELEASE(gNC_DownloadState);
00143   NS_IF_RELEASE(gNC_StatusText);
00144 
00145   NS_RELEASE(gRDFService);
00146 }
00147 
00148 nsresult
00149 nsDownloadManager::Init()
00150 {
00151   if (gRefCnt++ != 0) {
00152     NS_NOTREACHED("download manager should be used as a service");
00153     return NS_ERROR_UNEXPECTED; // This will make the |CreateInstance| fail.
00154   }
00155 
00156   if (!mCurrDownloads.Init())
00157     return NS_ERROR_FAILURE;
00158 
00159   nsresult rv;
00160   mRDFContainerUtils = do_GetService("@mozilla.org/rdf/container-utils;1", &rv);
00161   if (NS_FAILED(rv)) return rv;
00162 
00163   nsCOMPtr<nsIObserverService> obsService = do_GetService("@mozilla.org/observer-service;1", &rv);
00164   if (NS_FAILED(rv)) return rv;
00165   
00166 
00167   rv = CallGetService("@mozilla.org/rdf/rdf-service;1", &gRDFService);
00168   if (NS_FAILED(rv)) return rv;                                                 
00169 
00170   gRDFService->GetResource(NS_LITERAL_CSTRING("NC:DownloadsRoot"), &gNC_DownloadsRoot);
00171   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "File"), &gNC_File);
00172   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "URL"), &gNC_URL);
00173   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Name"), &gNC_Name);
00174   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "ProgressMode"), &gNC_ProgressMode);
00175   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "ProgressPercent"), &gNC_ProgressPercent);
00176   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Transferred"), &gNC_Transferred);
00177   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "DownloadState"), &gNC_DownloadState);
00178   gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "StatusText"), &gNC_StatusText);
00179 
00180   nsCAutoString downloadsDB;
00181   rv = GetProfileDownloadsFileURL(downloadsDB);
00182   if (NS_FAILED(rv)) return rv;
00183 
00184   rv = gRDFService->GetDataSourceBlocking(downloadsDB.get(), getter_AddRefs(mDataSource));
00185   if (NS_FAILED(rv)) return rv;
00186 
00187   mListener = do_CreateInstance("@mozilla.org/download-manager/listener;1", &rv);
00188   if (NS_FAILED(rv)) return rv;
00189 
00190   nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
00191   if (NS_FAILED(rv)) return rv;
00192   
00193   rv =  bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, getter_AddRefs(mBundle));
00194   if (NS_FAILED(rv))
00195     return rv;
00196 
00197   // The following two AddObserver calls must be the last lines in this function,
00198   // because otherwise, this function may fail (and thus, this object would be not
00199   // completely initialized), but the observerservice would still keep a reference
00200   // to us and notify us about shutdown, which may cause crashes.
00201   // failure to add an observer is not critical
00202   obsService->AddObserver(this, "profile-before-change", PR_FALSE);
00203   obsService->AddObserver(this, "profile-approve-change", PR_FALSE);
00204 
00205   return NS_OK;
00206 }
00207 
00208 nsresult
00209 nsDownloadManager::DownloadStarted(const nsACString& aTargetPath)
00210 {
00211   if (mCurrDownloads.GetWeak(aTargetPath))
00212     AssertProgressInfoFor(aTargetPath);
00213 
00214   return NS_OK;
00215 }
00216 
00217 nsresult
00218 nsDownloadManager::DownloadEnded(const nsACString& aTargetPath, const PRUnichar* aMessage)
00219 {
00220   nsDownload* dl = mCurrDownloads.GetWeak(aTargetPath);
00221   if (dl) {
00222     AssertProgressInfoFor(aTargetPath);
00223     mCurrDownloads.Remove(aTargetPath);
00224   }
00225 
00226   return NS_OK;
00227 }
00228 
00229 nsresult
00230 nsDownloadManager::GetProfileDownloadsFileURL(nsCString& aDownloadsFileURL)
00231 {
00232   nsCOMPtr<nsIFile> downloadsFile;
00233   nsresult rv = NS_GetSpecialDirectory(NS_APP_DOWNLOADS_50_FILE, getter_AddRefs(downloadsFile));
00234   if (NS_FAILED(rv))
00235     return rv;
00236     
00237   return NS_GetURLSpecFromFile(downloadsFile, aDownloadsFileURL);
00238 }
00239 
00240 nsresult
00241 nsDownloadManager::GetDownloadsContainer(nsIRDFContainer** aResult)
00242 {
00243   if (mDownloadsContainer) {
00244     *aResult = mDownloadsContainer;
00245     NS_ADDREF(*aResult);
00246     return NS_OK;
00247   }
00248 
00249   PRBool isContainer;
00250   nsresult rv = mRDFContainerUtils->IsContainer(mDataSource, gNC_DownloadsRoot, &isContainer);
00251   if (NS_FAILED(rv)) return rv;
00252 
00253   if (!isContainer) {
00254     rv = mRDFContainerUtils->MakeSeq(mDataSource, gNC_DownloadsRoot, getter_AddRefs(mDownloadsContainer));
00255     if (NS_FAILED(rv)) return rv;
00256   }
00257   else {
00258     mDownloadsContainer = do_CreateInstance(NS_RDF_CONTRACTID "/container;1", &rv);
00259     if (NS_FAILED(rv)) return rv;
00260     rv = mDownloadsContainer->Init(mDataSource, gNC_DownloadsRoot);
00261     if (NS_FAILED(rv)) return rv;
00262   }
00263 
00264   *aResult = mDownloadsContainer;
00265   NS_IF_ADDREF(*aResult);
00266 
00267   return rv;
00268 }
00269 
00270 nsresult
00271 nsDownloadManager::GetInternalListener(nsIDownloadProgressListener** aInternalListener)
00272 {
00273   *aInternalListener = mListener;
00274   NS_IF_ADDREF(*aInternalListener);
00275   return NS_OK;
00276 }
00277 
00278 nsresult
00279 nsDownloadManager::GetDataSource(nsIRDFDataSource** aDataSource)
00280 {
00281   *aDataSource = mDataSource;
00282   NS_ADDREF(*aDataSource);
00283   return NS_OK;
00284 }
00285 
00286 nsresult
00287 nsDownloadManager::AssertProgressInfo()
00288 {
00289   nsCOMPtr<nsISupports> supports;
00290   nsCOMPtr<nsIRDFResource> res;
00291   nsCOMPtr<nsIRDFInt> intLiteral;
00292 
00293   gRDFService->GetIntLiteral(DOWNLOADING, getter_AddRefs(intLiteral));
00294   nsCOMPtr<nsISimpleEnumerator> downloads;
00295   nsresult rv = mDataSource->GetSources(gNC_DownloadState, intLiteral, PR_TRUE, getter_AddRefs(downloads));
00296   if (NS_FAILED(rv)) return rv;
00297   
00298   PRBool hasMoreElements;
00299   downloads->HasMoreElements(&hasMoreElements);
00300 
00301   while (hasMoreElements) {
00302     const char* uri;
00303     downloads->GetNext(getter_AddRefs(supports));
00304     res = do_QueryInterface(supports);
00305     res->GetValueConst(&uri);
00306     AssertProgressInfoFor(nsDependentCString(uri));
00307     downloads->HasMoreElements(&hasMoreElements);
00308   }
00309   return rv;
00310 }
00311 
00312 nsresult
00313 nsDownloadManager::AssertProgressInfoFor(const nsACString& aTargetPath)
00314 {
00315   nsDownload* internalDownload = mCurrDownloads.GetWeak(aTargetPath);
00316   if (!internalDownload)
00317     return NS_ERROR_FAILURE;
00318   
00319   nsresult rv;
00320   PRInt32 percentComplete;
00321   nsCOMPtr<nsIRDFNode> oldTarget;
00322   nsCOMPtr<nsIRDFInt> intLiteral;
00323   nsCOMPtr<nsIRDFResource> res;
00324   nsCOMPtr<nsIRDFLiteral> literal;
00325 
00326   gRDFService->GetResource(aTargetPath, getter_AddRefs(res));
00327 
00328   DownloadState state = internalDownload->GetDownloadState();
00329  
00330   // update progress mode
00331   nsAutoString progressMode;
00332   if (state == DOWNLOADING)
00333     progressMode.AssignLiteral("normal");
00334   else
00335     progressMode.AssignLiteral("none");
00336 
00337   gRDFService->GetLiteral(progressMode.get(), getter_AddRefs(literal));
00338 
00339   rv = mDataSource->GetTarget(res, gNC_ProgressMode, PR_TRUE, getter_AddRefs(oldTarget));
00340   
00341   if (oldTarget)
00342     rv = mDataSource->Change(res, gNC_ProgressMode, oldTarget, literal);
00343   else
00344     rv = mDataSource->Assert(res, gNC_ProgressMode, literal, PR_TRUE);
00345   if (NS_FAILED(rv)) return rv;
00346 
00347   // update download state (not started, downloading, queued, finished, etc...)
00348   gRDFService->GetIntLiteral(state, getter_AddRefs(intLiteral));
00349 
00350   mDataSource->GetTarget(res, gNC_DownloadState, PR_TRUE, getter_AddRefs(oldTarget));
00351   
00352   if (oldTarget) {
00353     rv = mDataSource->Change(res, gNC_DownloadState, oldTarget, intLiteral);
00354     if (NS_FAILED(rv)) return rv;
00355   }
00356 
00357   nsAutoString strKey;
00358   if (state == NOTSTARTED)
00359     strKey.AssignLiteral("notStarted");
00360   else if (state == DOWNLOADING)
00361     strKey.AssignLiteral("downloading");
00362   else if (state == FINISHED)
00363     strKey.AssignLiteral("finished");
00364   else if (state == FAILED)
00365     strKey.AssignLiteral("failed");
00366   else if (state == CANCELED)
00367     strKey.AssignLiteral("canceled");
00368 
00369   nsXPIDLString value;
00370   rv = mBundle->GetStringFromName(strKey.get(), getter_Copies(value));    
00371   if (NS_FAILED(rv)) return rv;
00372 
00373   gRDFService->GetLiteral(value, getter_AddRefs(literal));
00374 
00375   rv = mDataSource->GetTarget(res, gNC_StatusText, PR_TRUE, getter_AddRefs(oldTarget));
00376   
00377   if (oldTarget) {
00378     rv = mDataSource->Change(res, gNC_StatusText, oldTarget, literal);
00379     if (NS_FAILED(rv)) return rv;
00380   }
00381   else {
00382     rv = mDataSource->Assert(res, gNC_StatusText, literal, PR_TRUE);
00383     if (NS_FAILED(rv)) return rv;
00384   }
00385   
00386   // update percentage
00387   internalDownload->GetPercentComplete(&percentComplete);
00388 
00389   mDataSource->GetTarget(res, gNC_ProgressPercent, PR_TRUE, getter_AddRefs(oldTarget));
00390   gRDFService->GetIntLiteral(percentComplete, getter_AddRefs(intLiteral));
00391 
00392   if (oldTarget)
00393     rv = mDataSource->Change(res, gNC_ProgressPercent, oldTarget, intLiteral);
00394   else
00395     rv = mDataSource->Assert(res, gNC_ProgressPercent, intLiteral, PR_TRUE);
00396   if (NS_FAILED(rv)) return rv;
00397 
00398   // update transferred
00399   nsDownload::TransferInformation transferInfo =
00400                                  internalDownload->GetTransferInformation();
00401 
00402   // convert from bytes to kbytes for progress display
00403   PRInt64 current = (PRFloat64)transferInfo.mCurrBytes / 1024 + .5;
00404   PRInt64 max = (PRFloat64)transferInfo.mMaxBytes / 1024 + .5;
00405 
00406   nsAutoString currBytes; currBytes.AppendInt(current);
00407   nsAutoString maxBytes; maxBytes.AppendInt(max);
00408   const PRUnichar *strings[] = {
00409     currBytes.get(),
00410     maxBytes.get()
00411   };
00412   
00413   rv = mBundle->FormatStringFromName(NS_LITERAL_STRING("transferred").get(),
00414                                      strings, 2, getter_Copies(value));
00415   if (NS_FAILED(rv)) return rv;
00416 
00417   gRDFService->GetLiteral(value, getter_AddRefs(literal));
00418  
00419   mDataSource->GetTarget(res, gNC_Transferred, PR_TRUE, getter_AddRefs(oldTarget));
00420  
00421   if (oldTarget)
00422     rv = mDataSource->Change(res, gNC_Transferred, oldTarget, literal);
00423   else
00424     rv = mDataSource->Assert(res, gNC_Transferred, literal, PR_TRUE);
00425   if (NS_FAILED(rv)) return rv;
00426 
00427   nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mDataSource);
00428   remote->Flush();
00429 
00430   // XXX should also store and update time elapsed
00431   return rv;
00432 }  
00433 
00435 // nsIDownloadManager
00436 
00437 NS_IMETHODIMP
00438 nsDownloadManager::AddDownload(nsIURI* aSource,
00439                                nsIURI* aTarget,
00440                                const nsAString& aDisplayName,
00441                                nsIMIMEInfo *aMIMEInfo,
00442                                PRTime aStartTime,
00443                                nsILocalFile* aTempFile,
00444                                nsICancelable* aCancelable,
00445                                nsIDownload** aDownload)
00446 {
00447   NS_ENSURE_ARG_POINTER(aSource);
00448   NS_ENSURE_ARG_POINTER(aTarget);
00449   NS_ENSURE_ARG_POINTER(aDownload);
00450 
00451   nsCOMPtr<nsIRDFContainer> downloads;
00452   nsresult rv = GetDownloadsContainer(getter_AddRefs(downloads));
00453   if (NS_FAILED(rv)) return rv;
00454 
00455   // this will create a cycle that will be broken in nsDownload::OnStateChange
00456   nsDownload* internalDownload = new nsDownload(this, aTarget, aSource, aCancelable);
00457   if (!internalDownload)
00458     return NS_ERROR_OUT_OF_MEMORY;
00459 
00460   NS_ADDREF(*aDownload = internalDownload);
00461 
00462   // the path of the target is the unique identifier we use
00463   nsCOMPtr<nsILocalFile> targetFile;
00464   rv = internalDownload->GetTargetFile(getter_AddRefs(targetFile));
00465   if (NS_FAILED(rv)) return rv;
00466 
00467   nsAutoString path;
00468   rv = targetFile->GetPath(path);
00469   if (NS_FAILED(rv)) return rv;
00470 
00471   NS_ConvertUTF16toUTF8 utf8Path(path);
00472 
00473   nsCOMPtr<nsIRDFResource> downloadRes;
00474   gRDFService->GetResource(utf8Path, getter_AddRefs(downloadRes));
00475 
00476   nsCOMPtr<nsIRDFNode> node;
00477 
00478   // Assert source url information
00479   nsCAutoString spec;
00480   aSource->GetSpec(spec);
00481 
00482   nsCOMPtr<nsIRDFResource> urlResource;
00483   gRDFService->GetResource(spec, getter_AddRefs(urlResource));
00484   mDataSource->GetTarget(downloadRes, gNC_URL, PR_TRUE, getter_AddRefs(node));
00485   if (node)
00486     rv = mDataSource->Change(downloadRes, gNC_URL, node, urlResource);
00487   else
00488     rv = mDataSource->Assert(downloadRes, gNC_URL, urlResource, PR_TRUE);
00489   if (NS_FAILED(rv)) return rv;
00490 
00491   // Set and assert the "pretty" (display) name of the download
00492   nsAutoString displayName; displayName.Assign(aDisplayName);
00493   if (displayName.IsEmpty()) {
00494     targetFile->GetLeafName(displayName);
00495   }
00496   internalDownload->SetDisplayName(displayName.get());
00497   internalDownload->SetTempFile(aTempFile);
00498  
00499   nsCOMPtr<nsIRDFLiteral> nameLiteral;
00500   gRDFService->GetLiteral(displayName.get(), getter_AddRefs(nameLiteral));
00501   mDataSource->GetTarget(downloadRes, gNC_Name, PR_TRUE, getter_AddRefs(node));
00502   if (node)
00503     rv = mDataSource->Change(downloadRes, gNC_Name, node, nameLiteral);
00504   else
00505     rv = mDataSource->Assert(downloadRes, gNC_Name, nameLiteral, PR_TRUE);
00506   if (NS_FAILED(rv)) return rv;
00507   
00508   internalDownload->SetMIMEInfo(aMIMEInfo);
00509   internalDownload->SetStartTime(aStartTime);
00510 
00511   // Assert file information
00512   nsCOMPtr<nsIRDFResource> fileResource;
00513   gRDFService->GetResource(utf8Path, getter_AddRefs(fileResource));
00514   rv = mDataSource->Assert(downloadRes, gNC_File, fileResource, PR_TRUE);
00515   if (NS_FAILED(rv)) return rv;
00516   
00517   // Assert download state information (NOTSTARTED, since it's just now being added)
00518   nsCOMPtr<nsIRDFInt> intLiteral;
00519   gRDFService->GetIntLiteral(NOTSTARTED, getter_AddRefs(intLiteral));
00520   mDataSource->GetTarget(downloadRes, gNC_DownloadState, PR_TRUE, getter_AddRefs(node));
00521   if (node)
00522     rv = mDataSource->Change(downloadRes, gNC_DownloadState, node, intLiteral);
00523   else
00524     rv = mDataSource->Assert(downloadRes, gNC_DownloadState, intLiteral, PR_TRUE);
00525   if (NS_FAILED(rv)) return rv;
00526   
00527   PRInt32 itemIndex;
00528   downloads->IndexOf(downloadRes, &itemIndex);
00529   if (itemIndex == -1) {
00530     rv = downloads->AppendElement(downloadRes);
00531     if (NS_FAILED(rv)) return rv;
00532   }
00533 
00534   // Now flush all this to disk
00535   nsCOMPtr<nsIRDFRemoteDataSource> remote(do_QueryInterface(mDataSource));
00536   rv = remote->Flush();
00537   if (NS_FAILED(rv)) return rv;
00538 
00539   mCurrDownloads.Put(utf8Path, internalDownload);
00540 
00541   return rv;
00542 }
00543 
00544 NS_IMETHODIMP
00545 nsDownloadManager::GetDownload(const nsACString & aTargetPath, nsIDownload** aDownloadItem)
00546 {
00547   NS_ENSURE_ARG_POINTER(aDownloadItem);
00548 
00549   // if it's currently downloading we can get it from the table
00550   // XXX otherwise we should look for it in the datasource and
00551   //     create a new nsIDownload with the resource's properties
00552   NS_IF_ADDREF(*aDownloadItem = mCurrDownloads.GetWeak(aTargetPath));
00553   return NS_OK;
00554 }
00555 
00556 NS_IMETHODIMP
00557 nsDownloadManager::CancelDownload(const nsACString & aTargetPath)
00558 {
00559   nsRefPtr<nsDownload> internalDownload = mCurrDownloads.GetWeak(aTargetPath);
00560   if (!internalDownload)
00561     return NS_ERROR_FAILURE;
00562 
00563   return internalDownload->Cancel();
00564 }
00565 
00566 NS_IMETHODIMP
00567 nsDownloadManager::PauseDownload(nsIDownload* aDownload)
00568 {
00569   NS_ENSURE_ARG_POINTER(aDownload);
00570   return NS_STATIC_CAST(nsDownload*, aDownload)->Suspend();
00571 }
00572 
00573 NS_IMETHODIMP
00574 nsDownloadManager::ResumeDownload(const nsACString & aTargetPath)
00575 {
00576   nsDownload* dl = mCurrDownloads.GetWeak(aTargetPath);
00577   if (!dl)
00578     return NS_ERROR_NOT_AVAILABLE;
00579   return dl->Resume();
00580 }
00581 
00582 NS_IMETHODIMP
00583 nsDownloadManager::RemoveDownload(const nsACString & aTargetPath)
00584 {
00585   // RemoveDownload is for downloads not currently in progress. Having it
00586   // cancel in-progress downloads would make things complicated, so just return.
00587   nsDownload* inProgress = mCurrDownloads.GetWeak(aTargetPath);
00588   NS_ASSERTION(!inProgress, "Can't call RemoveDownload on a download in progress!");
00589   if (inProgress)
00590     return NS_ERROR_FAILURE;
00591 
00592   nsCOMPtr<nsIRDFContainer> downloads;
00593     nsresult rv = GetDownloadsContainer(getter_AddRefs(downloads));
00594   if (NS_FAILED(rv)) return rv;
00595   
00596   nsCOMPtr<nsIRDFResource> res;
00597   gRDFService->GetResource(aTargetPath, getter_AddRefs(res));
00598 
00599   // remove all the arcs for this resource, and then remove it from the Seq
00600   nsCOMPtr<nsISimpleEnumerator> arcs;
00601   rv = mDataSource->ArcLabelsOut(res, getter_AddRefs(arcs));
00602   if (NS_FAILED(rv)) return rv;
00603 
00604   PRBool moreArcs;
00605   rv = arcs->HasMoreElements(&moreArcs);
00606   if (NS_FAILED(rv)) return rv;
00607 
00608   while (moreArcs) {
00609     nsCOMPtr<nsISupports> supports;
00610     rv = arcs->GetNext(getter_AddRefs(supports));
00611     if (NS_FAILED(rv)) return rv;
00612 
00613     nsCOMPtr<nsIRDFResource> arc(do_QueryInterface(supports, &rv));
00614     if (NS_FAILED(rv)) return rv;
00615 
00616     nsCOMPtr<nsISimpleEnumerator> targets;
00617     rv = mDataSource->GetTargets(res, arc, PR_TRUE, getter_AddRefs(targets));
00618     if (NS_FAILED(rv)) return rv;
00619 
00620     PRBool moreTargets;
00621     rv = targets->HasMoreElements(&moreTargets);
00622     if (NS_FAILED(rv)) return rv;
00623 
00624     while (moreTargets) {
00625       rv = targets->GetNext(getter_AddRefs(supports));
00626       if (NS_FAILED(rv)) return rv;
00627 
00628       nsCOMPtr<nsIRDFNode> target(do_QueryInterface(supports, &rv));
00629       if (NS_FAILED(rv)) return rv;
00630 
00631       // and now drop this assertion from the graph
00632       rv = mDataSource->Unassert(res, arc, target);
00633       if (NS_FAILED(rv)) return rv;
00634 
00635       rv = targets->HasMoreElements(&moreTargets);
00636       if (NS_FAILED(rv)) return rv;
00637     }
00638     rv = arcs->HasMoreElements(&moreArcs);
00639     if (NS_FAILED(rv)) return rv;
00640   }
00641 
00642   PRInt32 itemIndex;
00643   downloads->IndexOf(res, &itemIndex);
00644   if (itemIndex <= 0)
00645     return NS_ERROR_FAILURE;
00646   
00647   nsCOMPtr<nsIRDFNode> node;
00648   rv = downloads->RemoveElementAt(itemIndex, PR_TRUE, getter_AddRefs(node));
00649   if (NS_FAILED(rv)) return rv;
00650   
00651   // if a mass removal is being done, we don't want to flush every time
00652   if (mBatches) return rv;
00653 
00654   nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mDataSource);
00655   return remote->Flush();
00656 }  
00657 
00658 NS_IMETHODIMP
00659 nsDownloadManager::StartBatchUpdate()
00660 {
00661   ++mBatches;
00662   return NS_OK;
00663 }
00664 
00665 NS_IMETHODIMP
00666 nsDownloadManager::EndBatchUpdate()
00667 {
00668   nsresult rv = NS_OK;
00669   if (--mBatches == 0) {
00670     nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mDataSource);
00671     rv = remote->Flush();
00672   }
00673   return rv;
00674 }
00675 
00676 NS_IMETHODIMP
00677 nsDownloadManager::Open(nsIDOMWindow* aParent, nsIDownload* aDownload)
00678 {
00679 
00680   // first assert new progress info so the ui is correctly updated
00681   // if this fails, it fails -- continue.
00682   AssertProgressInfo();
00683   
00684   // check for an existing manager window and focus it
00685   nsresult rv;
00686   nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
00687   if (NS_FAILED(rv)) return rv;
00688 
00689   nsCOMPtr<nsISupports> dlSupports(do_QueryInterface(aDownload));
00690 
00691   // if the window's already open, do nothing (focusing it would be annoying)
00692   nsCOMPtr<nsIDOMWindowInternal> recentWindow;
00693   wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(), getter_AddRefs(recentWindow));
00694   if (recentWindow) {
00695     nsCOMPtr<nsIObserverService> obsService = do_GetService("@mozilla.org/observer-service;1", &rv);
00696     if (NS_FAILED(rv)) return rv;
00697     return obsService->NotifyObservers(dlSupports, "download-starting", nsnull);
00698   }
00699 
00700   // if we ever have the capability to display the UI of third party dl managers,
00701   // we'll open their UI here instead.
00702   nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
00703   if (NS_FAILED(rv)) return rv;
00704 
00705   // pass the datasource to the window
00706   nsCOMPtr<nsISupportsArray> params(do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID));
00707   nsCOMPtr<nsISupports> dsSupports(do_QueryInterface(mDataSource));
00708   params->AppendElement(dsSupports);
00709   params->AppendElement(dlSupports);
00710 
00711   nsCOMPtr<nsIDOMWindow> newWindow;
00712   rv = ww->OpenWindow(aParent,
00713                       DOWNLOAD_MANAGER_FE_URL,
00714                       "_blank",
00715                       "chrome,all,dialog=no,resizable",
00716                       params,
00717                       getter_AddRefs(newWindow));
00718 
00719   if (NS_FAILED(rv)) return rv;
00720 
00721   nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(newWindow);
00722   if (!target) return NS_ERROR_FAILURE;
00723   
00724   rv = target->AddEventListener(NS_LITERAL_STRING("load"), this, PR_FALSE);
00725   if (NS_FAILED(rv)) return rv;
00726 
00727   return target->AddEventListener(NS_LITERAL_STRING("unload"), this, PR_FALSE);
00728 }
00729 
00730 NS_IMETHODIMP
00731 nsDownloadManager::OpenProgressDialogFor(nsIDownload* aDownload, nsIDOMWindow* aParent, PRBool aCancelDownloadOnClose)
00732 {
00733   NS_ENSURE_ARG_POINTER(aDownload);
00734   nsresult rv;
00735   nsDownload* internalDownload = NS_STATIC_CAST(nsDownload*, aDownload);
00736   nsIProgressDialog* oldDialog = internalDownload->GetDialog();
00737   
00738   if (oldDialog) {
00739     nsCOMPtr<nsIDOMWindow> window;
00740     oldDialog->GetDialog(getter_AddRefs(window));
00741     if (window) {
00742       nsCOMPtr<nsIDOMWindowInternal> internalWin = do_QueryInterface(window);
00743       internalWin->Focus();
00744       return NS_OK;
00745     }
00746   }
00747 
00748   nsCOMPtr<nsIProgressDialog> dialog(do_CreateInstance("@mozilla.org/progressdialog;1", &rv));
00749   if (NS_FAILED(rv)) return rv;
00750   
00751   dialog->SetCancelDownloadOnClose(aCancelDownloadOnClose);
00752   
00753   // now give the dialog the necessary context
00754   
00755   // start time...
00756   PRInt64 startTime = 0;
00757   aDownload->GetStartTime(&startTime);
00758   
00759   // source...
00760   nsCOMPtr<nsIURI> source;
00761   aDownload->GetSource(getter_AddRefs(source));
00762 
00763   // target...
00764   nsCOMPtr<nsIURI> target;
00765   aDownload->GetTarget(getter_AddRefs(target));
00766   
00767   // helper app...
00768   nsCOMPtr<nsIMIMEInfo> mimeInfo;
00769   aDownload->GetMIMEInfo(getter_AddRefs(mimeInfo));
00770 
00771   dialog->Init(source, target, EmptyString(), mimeInfo, startTime, nsnull,
00772                nsnull); 
00773   dialog->SetObserver(internalDownload);
00774 
00775   // now set the listener so we forward notifications to the dialog
00776   nsCOMPtr<nsIWebProgressListener2> listener = do_QueryInterface(dialog);
00777   internalDownload->SetDialogListener(listener);
00778   
00779   internalDownload->SetDialog(dialog);
00780   
00781   return dialog->Open(aParent);
00782 }
00783 
00784 NS_IMETHODIMP
00785 nsDownloadManager::OnClose()
00786 {
00787   mDocument = nsnull;
00788   mListener->SetDocument(nsnull);
00789   return NS_OK;
00790 }
00791 
00793 // nsIDOMEventListener
00794 
00795 NS_IMETHODIMP
00796 nsDownloadManager::HandleEvent(nsIDOMEvent* aEvent)
00797 {
00798   // the event is either load or unload
00799   nsAutoString eventType;
00800   aEvent->GetType(eventType);
00801   if (eventType.EqualsLiteral("unload"))
00802     return OnClose();
00803 
00804   nsCOMPtr<nsIDOMEventTarget> target;
00805   nsresult rv = aEvent->GetTarget(getter_AddRefs(target));
00806   if (NS_FAILED(rv)) return rv;
00807 
00808   mDocument = do_QueryInterface(target);
00809   mListener->SetDocument(mDocument);
00810   return NS_OK;
00811 }
00812 
00814 // nsIObserver
00815 
00816 NS_IMETHODIMP
00817 nsDownloadManager::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
00818 {
00819   nsresult rv;
00820   if (nsCRT::strcmp(aTopic, "profile-approve-change") == 0) {
00821     // Only run this on profile switch
00822     if (!NS_LITERAL_STRING("switch").Equals(aData))
00823       return NS_OK;
00824 
00825     // If count == 0, nothing to do
00826     if (mCurrDownloads.Count() == 0)
00827       return NS_OK;
00828 
00829     nsCOMPtr<nsIProfileChangeStatus> changeStatus(do_QueryInterface(aSubject));
00830     if (!changeStatus)
00831       return NS_ERROR_UNEXPECTED;
00832 
00833     nsXPIDLString title, text, proceed, cancel;
00834     nsresult rv = mBundle->GetStringFromName(NS_LITERAL_STRING("profileSwitchTitle").get(),
00835                                              getter_Copies(title));
00836     NS_ENSURE_SUCCESS(rv, rv);
00837     rv = mBundle->GetStringFromName(NS_LITERAL_STRING("profileSwitchText").get(),
00838                                     getter_Copies(text));
00839     NS_ENSURE_SUCCESS(rv, rv);
00840     rv = mBundle->GetStringFromName(NS_LITERAL_STRING("profileSwitchContinue").get(),
00841                                     getter_Copies(proceed));
00842     NS_ENSURE_SUCCESS(rv, rv);
00843 
00844     nsCOMPtr<nsIPromptService> promptService(do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv));
00845     if (NS_FAILED(rv))
00846       return rv;
00847 
00848     PRInt32 button;
00849     rv = promptService->ConfirmEx(nsnull, title.get(), text.get(),
00850                                   nsIPromptService::BUTTON_TITLE_CANCEL * nsIPromptService::BUTTON_POS_0 |
00851                                   nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_1,
00852                                   nsnull,
00853                                   proceed.get(),
00854                                   nsnull,
00855                                   nsnull,
00856                                   nsnull,
00857                                   &button);
00858     if (NS_FAILED(rv))
00859       return rv;
00860 
00861     if (button == 0)
00862       changeStatus->VetoChange();
00863   }
00864   else if (nsCRT::strcmp(aTopic, "profile-before-change") == 0) {
00865     nsCOMPtr<nsISupports> supports;
00866     nsCOMPtr<nsIRDFResource> res;
00867     nsCOMPtr<nsIRDFInt> intLiteral;
00868 
00869     gRDFService->GetIntLiteral(DOWNLOADING, getter_AddRefs(intLiteral));
00870     nsCOMPtr<nsISimpleEnumerator> downloads;
00871     rv = mDataSource->GetSources(gNC_DownloadState, intLiteral, PR_TRUE, getter_AddRefs(downloads));
00872     if (NS_FAILED(rv)) return rv;
00873     
00874     PRBool hasMoreElements;
00875     downloads->HasMoreElements(&hasMoreElements);
00876 
00877     while (hasMoreElements) {
00878       const char* uri;
00879 
00880       downloads->GetNext(getter_AddRefs(supports));
00881       res = do_QueryInterface(supports);
00882       res->GetValueConst(&uri);
00883       CancelDownload(nsDependentCString(uri));
00884       downloads->HasMoreElements(&hasMoreElements);
00885     }    
00886   }
00887   return NS_OK;
00888 }
00889 
00891 // nsDownload
00892 
00893 NS_IMPL_ISUPPORTS6(nsDownload, nsIDownload, nsIDownload_MOZILLA_1_8_BRANCH,
00894                    nsITransfer, nsIWebProgressListener,
00895                    nsIWebProgressListener2, nsIObserver)
00896 
00897 nsDownload::nsDownload(nsDownloadManager* aManager,
00898                        nsIURI* aTarget,
00899                        nsIURI* aSource,
00900                        nsICancelable* aCancelable) :
00901                          mDownloadManager(aManager),
00902                          mTarget(aTarget),
00903                          mSource(aSource),
00904                          mCancelable(aCancelable),
00905                          mDownloadState(NOTSTARTED),
00906                          mPercentComplete(0),
00907                          mCurrBytes(LL_ZERO),
00908                          mMaxBytes(LL_ZERO),
00909                          mStartTime(LL_ZERO),
00910                          mLastUpdate(PR_Now() - (PRUint32)gInterval),
00911                          mSpeed(0)
00912 {
00913 }
00914 
00915 nsDownload::~nsDownload()
00916 {  
00917   nsCAutoString path;
00918   nsresult rv = GetFilePathUTF8(mTarget, path);
00919   if (NS_FAILED(rv)) return;
00920 
00921   mDownloadManager->AssertProgressInfoFor(path);
00922 }
00923 
00924 nsresult
00925 nsDownload::Suspend()
00926 {
00927   if (!mRequest)
00928     return NS_ERROR_UNEXPECTED;
00929   return mRequest->Suspend();
00930 }
00931 
00932 nsresult
00933 nsDownload::Cancel()
00934 {
00935   // Don't cancel if download is already finished or canceled
00936   if (GetDownloadState() == FINISHED || GetDownloadState() == CANCELED)
00937     return NS_OK;
00938 
00939   nsresult rv = mCancelable->Cancel(NS_BINDING_ABORTED);
00940   if (NS_FAILED(rv))
00941     return rv;
00942   
00943   SetDownloadState(CANCELED);
00944 
00945   nsCAutoString path;
00946   rv = GetFilePathUTF8(mTarget, path);
00947   if (NS_FAILED(rv))
00948     return rv;
00949   mDownloadManager->DownloadEnded(path, nsnull);
00950   
00951   // Dump the temp file.  This should really be done when the transfer
00952   // is cancelled, but there are other cancellation causes that shouldn't
00953   // remove this. We need to improve those bits.
00954   if (mTempFile) {
00955     PRBool exists;
00956     mTempFile->Exists(&exists);
00957     if (exists)
00958       mTempFile->Remove(PR_FALSE);
00959   }
00960 
00961   // if there's a progress dialog open for the item,
00962   // we have to notify it that we're cancelling
00963   nsCOMPtr<nsIObserver> observer = do_QueryInterface(GetDialog());
00964   if (observer) {
00965     rv = observer->Observe(NS_STATIC_CAST(nsIDownload*, this), "oncancel", nsnull);
00966   }
00967   
00968   return rv;
00969 }
00970 
00971 nsresult
00972 nsDownload::SetDisplayName(const PRUnichar* aDisplayName)
00973 {
00974   mDisplayName = aDisplayName;
00975 
00976   nsCOMPtr<nsIRDFDataSource> ds;
00977   mDownloadManager->GetDataSource(getter_AddRefs(ds));
00978 
00979   nsCOMPtr<nsIRDFLiteral> nameLiteral;
00980   nsCOMPtr<nsIRDFResource> res;
00981   nsCAutoString path;
00982   nsresult rv = GetFilePathUTF8(mTarget, path);
00983   if (NS_FAILED(rv)) return rv;
00984 
00985   gRDFService->GetResource(path, getter_AddRefs(res));
00986   
00987   gRDFService->GetLiteral(aDisplayName, getter_AddRefs(nameLiteral));
00988   ds->Assert(res, gNC_Name, nameLiteral, PR_TRUE);
00989 
00990   return NS_OK;
00991 }
00992 
00993 nsresult
00994 nsDownload::Resume()
00995 {
00996   if (!mRequest)
00997     return NS_ERROR_UNEXPECTED;
00998   return mRequest->Resume();
00999 }
01000 
01002 // nsIObserver
01003 NS_IMETHODIMP
01004 nsDownload::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
01005 {
01006   if (strcmp(aTopic, "onpause") == 0) {
01007     return Suspend();
01008   }
01009   if (strcmp(aTopic, "onresume") == 0) {
01010     return Resume();
01011   }
01012   if (strcmp(aTopic, "oncancel") == 0) {
01013     SetDialog(nsnull);
01014 
01015     Cancel();
01016     // Ignoring return value; this function will get called twice,
01017     // and bad things happen if we return a failure code the second time.
01018     return NS_OK;
01019   }
01020 
01021   if (strcmp(aTopic, "alertclickcallback") == 0) {
01022     // show the download manager
01023     mDownloadManager->Open(nsnull, this);
01024     return NS_OK;
01025   }
01026 
01027   return NS_OK;
01028 }
01029 
01031 // nsIWebProgressListener2
01032 
01033 NS_IMETHODIMP
01034 nsDownload::OnProgressChange64(nsIWebProgress *aWebProgress,
01035                                nsIRequest *aRequest,
01036                                PRInt64 aCurSelfProgress,
01037                                PRInt64 aMaxSelfProgress,
01038                                PRInt64 aCurTotalProgress,
01039                                PRInt64 aMaxTotalProgress)
01040 {
01041   if (!mRequest)
01042     mRequest = aRequest; // used for pause/resume
01043 
01044   // Filter notifications since they come in so frequently, but we want to
01045   // process the last notification.
01046   PRTime now = PR_Now();
01047   nsInt64 delta = now - mLastUpdate;
01048   if (delta < gInterval && aCurTotalProgress != aMaxTotalProgress)
01049     return NS_OK;
01050 
01051   mLastUpdate = now;
01052 
01053   if (mDownloadState == NOTSTARTED) {
01054     nsCAutoString path;
01055     nsresult rv = GetFilePathUTF8(mTarget, path);
01056     if (NS_FAILED(rv)) return rv;
01057 
01058     mDownloadState = DOWNLOADING;
01059     mDownloadManager->DownloadStarted(path);
01060   }
01061 
01062   // Calculate the speed using the elapsed delta time and bytes downloaded
01063   // during that time for more accuracy.
01064   double elapsedSecs = double(delta) / PR_USEC_PER_SEC;
01065   if (elapsedSecs > 0) {
01066     nsUint64 curTotalProgress = (PRUint64)aCurTotalProgress;
01067     nsUint64 diffBytes = curTotalProgress - nsUint64(mCurrBytes);
01068     double speed = double(diffBytes) / elapsedSecs;
01069     if (LL_IS_ZERO(mCurrBytes))
01070       mSpeed = speed;
01071     else {
01072       // Calculate 'smoothed average' of 10 readings.
01073       mSpeed = mSpeed * 0.9 + speed * 0.1;
01074     }
01075   }
01076 
01077   if (aMaxTotalProgress > 0)
01078     mPercentComplete = aCurTotalProgress * 100 / aMaxTotalProgress;
01079   else
01080     mPercentComplete = -1;
01081 
01082   mCurrBytes = aCurTotalProgress;
01083   mMaxBytes = aMaxTotalProgress;
01084 
01085   if (mDownloadManager->MustUpdateUI()) {
01086     nsCOMPtr<nsIDownloadProgressListener> internalListener;
01087     mDownloadManager->GetInternalListener(getter_AddRefs(internalListener));
01088     if (internalListener) {
01089       internalListener->OnProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress,
01090                                          aCurTotalProgress, aMaxTotalProgress, this);
01091     }
01092   }
01093 
01094   if (mDialogListener) {
01095     mDialogListener->OnProgressChange64(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress,
01096                                         aCurTotalProgress, aMaxTotalProgress);
01097   }
01098 
01099   return NS_OK;
01100 }
01101 
01103 // nsIWebProgressListener
01104 
01105 NS_IMETHODIMP
01106 nsDownload::OnProgressChange(nsIWebProgress *aWebProgress,
01107                              nsIRequest *aRequest,
01108                              PRInt32 aCurSelfProgress,
01109                              PRInt32 aMaxSelfProgress,
01110                              PRInt32 aCurTotalProgress,
01111                              PRInt32 aMaxTotalProgress)
01112 {
01113   return OnProgressChange64(aWebProgress, aRequest,
01114                             aCurSelfProgress, aMaxSelfProgress,
01115                             aCurTotalProgress, aMaxTotalProgress);
01116 }
01117 
01118 
01119 NS_IMETHODIMP
01120 nsDownload::OnLocationChange(nsIWebProgress *aWebProgress,
01121                              nsIRequest *aRequest, nsIURI *aLocation)
01122 {
01123   if (mDownloadManager->MustUpdateUI()) {
01124     nsCOMPtr<nsIDownloadProgressListener> internalListener;
01125     mDownloadManager->GetInternalListener(getter_AddRefs(internalListener));
01126     if (internalListener)
01127       internalListener->OnLocationChange(aWebProgress, aRequest, aLocation, this);
01128   }
01129 
01130   if (mDialogListener)
01131     mDialogListener->OnLocationChange(aWebProgress, aRequest, aLocation);
01132 
01133   return NS_OK;
01134 }
01135 
01136 NS_IMETHODIMP
01137 nsDownload::OnStatusChange(nsIWebProgress *aWebProgress,
01138                            nsIRequest *aRequest, nsresult aStatus,
01139                            const PRUnichar *aMessage)
01140 {   
01141   if (NS_FAILED(aStatus)) {
01142     mDownloadState = FAILED;
01143     nsCAutoString path;
01144     nsresult rv = GetFilePathUTF8(mTarget, path);
01145     if (NS_SUCCEEDED(rv))
01146       mDownloadManager->DownloadEnded(path, aMessage);
01147   }
01148 
01149   if (mDownloadManager->MustUpdateUI()) {
01150     nsCOMPtr<nsIDownloadProgressListener> internalListener;
01151     mDownloadManager->GetInternalListener(getter_AddRefs(internalListener));
01152     if (internalListener)
01153       internalListener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage, this);
01154   }
01155 
01156   if (mDialogListener)
01157     mDialogListener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
01158   else {
01159     // Need to display error alert ourselves, if an error occurred.
01160     if (NS_FAILED(aStatus)) {
01161       // Get title for alert.
01162       nsXPIDLString title;
01163       nsresult rv;
01164       nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
01165       nsCOMPtr<nsIStringBundle> bundle;
01166       if (bundleService)
01167         rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, getter_AddRefs(bundle));
01168       if (bundle)
01169         bundle->GetStringFromName(NS_LITERAL_STRING("alertTitle").get(), getter_Copies(title));    
01170 
01171       // Get Download Manager window, to be parent of alert.
01172       nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
01173       nsCOMPtr<nsIDOMWindowInternal> dmWindow;
01174       if (wm)
01175         wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(), getter_AddRefs(dmWindow));
01176 
01177       // Show alert.
01178       nsCOMPtr<nsIPromptService> prompter(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
01179       if (prompter)
01180         prompter->Alert(dmWindow, title, aMessage);
01181     }
01182   }
01183 
01184   return NS_OK;
01185 }
01186 
01187 void nsDownload::DisplayDownloadFinishedAlert()
01188 {
01189   nsresult rv;
01190 #ifndef MOZ_THUNDERBIRD
01191   nsCOMPtr<nsIAlertsService> alertsService(do_GetService(NS_ALERTSERVICE_CONTRACTID, &rv));
01192   if (NS_FAILED(rv))
01193     return;
01194 
01195   nsCOMPtr<nsIStringBundle> bundle;
01196   nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
01197   if (NS_FAILED(rv))
01198     return;
01199 
01200   rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, getter_AddRefs(bundle));
01201   if (NS_FAILED(rv))
01202     return;
01203 
01204   nsXPIDLString finishedTitle, finishedText;
01205   rv = bundle->GetStringFromName(NS_LITERAL_STRING("finishedTitle").get(),
01206                                  getter_Copies(finishedTitle));
01207   if (NS_FAILED(rv))
01208     return;
01209 
01210   const PRUnichar *strings[] = { mDisplayName.get() };
01211   rv = bundle->FormatStringFromName(NS_LITERAL_STRING("finishedText").get(),
01212                                     strings, 1, getter_Copies(finishedText));
01213   if (NS_FAILED(rv))
01214     return;
01215   
01216   nsCAutoString url;
01217   mTarget->GetSpec(url);
01218   alertsService->ShowAlertNotification(NS_LITERAL_STRING("moz-icon://") + NS_ConvertUTF8toUTF16(url),
01219                                        finishedTitle, finishedText, PR_TRUE,
01220                                        NS_LITERAL_STRING("download"), this);
01221 #endif
01222 }
01223 
01224 NS_IMETHODIMP
01225 nsDownload::OnStateChange(nsIWebProgress* aWebProgress,
01226                           nsIRequest* aRequest, PRUint32 aStateFlags,
01227                           nsresult aStatus)
01228 {
01229   // Record the start time only if it hasn't been set.
01230   if (LL_IS_ZERO(mStartTime) && (aStateFlags & STATE_START))
01231     SetStartTime(PR_Now());
01232 
01233   // When we break the ref cycle with mPersist, we don't want to lose
01234   // access to out member vars!
01235   nsRefPtr<nsDownload> kungFuDeathGrip(this);
01236   
01237   // We need to update mDownloadState before updating the dialog, because
01238   // that will close and call CancelDownload if it was the last open window.
01239   nsresult rv = NS_OK;
01240   if (aStateFlags & STATE_STOP) {
01241     if (mDownloadState == DOWNLOADING || mDownloadState == NOTSTARTED) {
01242       mDownloadState = FINISHED;
01243 
01244       // Set file size at the end of a transfer (for unknown transfer amounts)
01245       if (mMaxBytes == -1)
01246         mMaxBytes = mCurrBytes;
01247 
01248       // Files less than 1Kb shouldn't show up as 0Kb.
01249       if (mMaxBytes < 1024) {
01250         mCurrBytes = 1024;
01251         mMaxBytes  = 1024;
01252       }
01253 
01254       mPercentComplete = 100;
01255 
01256       // Play a sound or show an alert when the download finishes
01257       PRBool playSound = PR_FALSE;
01258       PRBool showAlert = PR_FALSE;
01259       nsXPIDLCString soundStr;
01260 
01261       nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1");
01262 
01263       if (prefs) {
01264         nsCOMPtr<nsIPrefBranch> prefBranch;
01265         prefs->GetBranch(nsnull, getter_AddRefs(prefBranch));
01266         if (prefBranch) {
01267           rv = prefBranch->GetBoolPref("browser.download.finished_download_sound", &playSound);
01268           if (NS_SUCCEEDED(rv) && playSound)
01269             prefBranch->GetCharPref("browser.download.finished_sound_url", getter_Copies(soundStr));
01270           rv = prefBranch->GetBoolPref("browser.download.finished_download_alert", &showAlert);
01271           if (NS_FAILED(rv))
01272             showAlert = PR_FALSE;
01273         }
01274       }
01275 
01276       if (!soundStr.IsEmpty()) {
01277         if (!mDownloadManager->mSoundInterface) {
01278           mDownloadManager->mSoundInterface = do_CreateInstance("@mozilla.org/sound;1");
01279         }
01280         if (mDownloadManager->mSoundInterface) {
01281           nsCOMPtr<nsIURI> soundURI;
01282           
01283           NS_NewURI(getter_AddRefs(soundURI), soundStr);
01284           nsCOMPtr<nsIURL> soundURL(do_QueryInterface(soundURI));
01285           if (soundURL)
01286             mDownloadManager->mSoundInterface->Play(soundURL);
01287           else
01288             mDownloadManager->mSoundInterface->Beep();
01289         }
01290       }
01291       if (showAlert)
01292         DisplayDownloadFinishedAlert();
01293       
01294       nsCAutoString path;
01295       rv = GetFilePathUTF8(mTarget, path);
01296       // can't do an early return; have to break reference cycle below
01297       if (NS_SUCCEEDED(rv)) {
01298         mDownloadManager->DownloadEnded(path, nsnull);
01299       }
01300     }
01301 
01302     // break the cycle we created in AddDownload
01303     mCancelable = nsnull;
01304     // and the one with the progress dialog
01305     if (mDialog) {
01306       mDialog->SetObserver(nsnull);
01307       mDialog = nsnull;
01308     }
01309   }
01310 
01311   if (mDownloadManager->MustUpdateUI()) {
01312     nsCOMPtr<nsIDownloadProgressListener> internalListener;
01313     mDownloadManager->GetInternalListener(getter_AddRefs(internalListener));
01314     if (internalListener)
01315       internalListener->OnStateChange(aWebProgress, aRequest, aStateFlags, aStatus, this);
01316   }
01317 
01318   if (mDialogListener) {
01319     mDialogListener->OnStateChange(aWebProgress, aRequest, aStateFlags, aStatus);
01320     if (aStateFlags & STATE_STOP) {
01321       // Break this cycle, too
01322       mDialogListener = nsnull;
01323     }
01324   }
01325 
01326   return rv;
01327 }
01328 
01329 NS_IMETHODIMP
01330 nsDownload::OnSecurityChange(nsIWebProgress *aWebProgress,
01331                              nsIRequest *aRequest, PRUint32 aState)
01332 {
01333   if (mDownloadManager->MustUpdateUI()) {
01334     nsCOMPtr<nsIDownloadProgressListener> internalListener;
01335     mDownloadManager->GetInternalListener(getter_AddRefs(internalListener));
01336     if (internalListener)
01337       internalListener->OnSecurityChange(aWebProgress, aRequest, aState, this);
01338   }
01339 
01340   if (mDialogListener)
01341     mDialogListener->OnSecurityChange(aWebProgress, aRequest, aState);
01342 
01343   return NS_OK;
01344 }
01345 
01347 // nsIDownload
01348 
01349 NS_IMETHODIMP
01350 nsDownload::Init(nsIURI* aSource,
01351                  nsIURI* aTarget,
01352                  const nsAString& aDisplayName,
01353                  nsIMIMEInfo *aMIMEInfo,
01354                  PRTime aStartTime,
01355                  nsILocalFile* aTempFile,
01356                  nsICancelable* aCancelable)
01357 {
01358   NS_NOTREACHED("Huh...how did we get here?!");
01359   return NS_OK;
01360 }
01361 
01362 NS_IMETHODIMP
01363 nsDownload::GetDisplayName(PRUnichar** aDisplayName)
01364 {
01365   *aDisplayName = ToNewUnicode(mDisplayName);
01366   return NS_OK;
01367 }
01368 
01369 NS_IMETHODIMP
01370 nsDownload::GetCancelable(nsICancelable** aCancelable)
01371 {
01372   *aCancelable = mCancelable;
01373   NS_IF_ADDREF(*aCancelable);
01374   return NS_OK;
01375 }
01376 
01377 NS_IMETHODIMP
01378 nsDownload::GetTarget(nsIURI** aTarget)
01379 {
01380   *aTarget = mTarget;
01381   NS_IF_ADDREF(*aTarget);
01382   return NS_OK;
01383 }
01384 
01385 NS_IMETHODIMP
01386 nsDownload::GetSource(nsIURI** aSource)
01387 {
01388   *aSource = mSource;
01389   NS_IF_ADDREF(*aSource);
01390   return NS_OK;
01391 }
01392 
01393 NS_IMETHODIMP
01394 nsDownload::GetStartTime(PRInt64* aStartTime)
01395 {
01396   *aStartTime = mStartTime;
01397   return NS_OK;
01398 }
01399 
01400 NS_IMETHODIMP
01401 nsDownload::GetPercentComplete(PRInt32* aPercentComplete)
01402 {
01403   *aPercentComplete = mPercentComplete;
01404   return NS_OK;
01405 }
01406 
01407 NS_IMETHODIMP
01408 nsDownload::GetAmountTransferred(PRUint64* aAmountTransferred)
01409 {
01410   *aAmountTransferred = ((PRFloat64)mCurrBytes / 1024.0 + .5);
01411   return NS_OK;
01412 }
01413 
01414 NS_IMETHODIMP
01415 nsDownload::GetSize(PRUint64* aSize)
01416 {
01417   *aSize = ((PRFloat64)mMaxBytes / 1024 + .5);
01418   return NS_OK;
01419 }
01420 
01421 NS_IMETHODIMP
01422 nsDownload::GetMIMEInfo(nsIMIMEInfo** aMIMEInfo)
01423 {
01424   *aMIMEInfo = mMIMEInfo;
01425   NS_IF_ADDREF(*aMIMEInfo);
01426   return NS_OK;
01427 }
01428 
01429 NS_IMETHODIMP
01430 nsDownload::GetTargetFile(nsILocalFile** aTargetFile)
01431 {
01432   nsresult rv;
01433 
01434   nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget, &rv);
01435   if (NS_FAILED(rv)) return rv;
01436 
01437   nsCOMPtr<nsIFile> file;
01438   rv = fileURL->GetFile(getter_AddRefs(file));
01439   if (NS_SUCCEEDED(rv))
01440     rv = CallQueryInterface(file, aTargetFile);
01441   return rv;
01442 }
01443 
01444 NS_IMETHODIMP
01445 nsDownload::GetSpeed(double* aSpeed)
01446 {
01447   *aSpeed = mSpeed;
01448   return NS_OK;
01449 }
01450 
01451 nsresult
01452 nsDownload::SetTempFile(nsILocalFile* aTempFile)
01453 {
01454   mTempFile = aTempFile;
01455   return NS_OK;
01456 }