Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / xpfe / components / download-manager / src / nsDownloadManager.cpp
blobf7d91bca0401e5eee34d466234f2207ef8297f4b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cin: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Blake Ross <blaker@netscape.com> (Original Author)
25 * Ben Goodger <ben@netscape.com> (Original Author)
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nsDownloadManager.h"
42 #include "nsIWebProgress.h"
43 #include "nsIRDFLiteral.h"
44 #include "rdf.h"
45 #include "nsNetUtil.h"
46 #include "nsIDOMWindow.h"
47 #include "nsIDOMWindowInternal.h"
48 #include "nsIDOMEvent.h"
49 #include "nsIDOMEventTarget.h"
50 #include "nsRDFCID.h"
51 #include "nsAppDirectoryServiceDefs.h"
52 #include "nsIObserver.h"
53 #include "nsIProgressDialog.h"
54 #include "nsIWebBrowserPersist.h"
55 #include "nsIWindowWatcher.h"
56 #include "nsIStringBundle.h"
57 #include "nsCRT.h"
58 #include "nsIWindowMediator.h"
59 #include "nsIPromptService.h"
60 #include "nsIObserverService.h"
61 #include "nsIProfileChangeStatus.h"
62 #include "nsIPrefService.h"
63 #include "nsIFileURL.h"
64 #include "nsIAlertsService.h"
65 #include "nsEmbedCID.h"
66 #include "nsInt64.h"
67 #include "nsToolkitCompsCID.h"
69 /* Outstanding issues/todo:
70 * 1. Implement pause/resume.
73 #define DOWNLOAD_MANAGER_FE_URL "chrome://communicator/content/downloadmanager/downloadmanager.xul"
74 #define DOWNLOAD_MANAGER_BUNDLE "chrome://communicator/locale/downloadmanager/downloadmanager.properties"
76 static const nsInt64 gInterval((PRUint32)(400 * PR_USEC_PER_MSEC));
78 static nsIRDFResource* gNC_DownloadsRoot = nsnull;
79 static nsIRDFResource* gNC_File = nsnull;
80 static nsIRDFResource* gNC_URL = nsnull;
81 static nsIRDFResource* gNC_Name = nsnull;
82 static nsIRDFResource* gNC_ProgressMode = nsnull;
83 static nsIRDFResource* gNC_ProgressPercent = nsnull;
84 static nsIRDFResource* gNC_Transferred = nsnull;
85 static nsIRDFResource* gNC_DownloadState = nsnull;
86 static nsIRDFResource* gNC_StatusText = nsnull;
88 static nsIRDFService* gRDFService = nsnull;
90 static PRInt32 gRefCnt = 0;
92 /**
93 * This function extracts the local file path corresponding to the given URI.
95 static nsresult
96 GetFilePathUTF8(nsIURI *aURI, nsACString &aResult)
98 nsresult rv;
100 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
101 if (NS_FAILED(rv)) return rv;
103 nsCOMPtr<nsIFile> file;
104 rv = fileURL->GetFile(getter_AddRefs(file));
105 if (NS_FAILED(rv)) return rv;
107 nsAutoString path;
108 rv = file->GetPath(path);
109 if (NS_SUCCEEDED(rv))
110 CopyUTF16toUTF8(path, aResult);
111 return rv;
114 ///////////////////////////////////////////////////////////////////////////////
115 // nsDownloadManager
117 NS_IMPL_ISUPPORTS3(nsDownloadManager, nsIDownloadManager, nsIDOMEventListener, nsIObserver)
119 nsDownloadManager::nsDownloadManager() : mBatches(0)
123 nsDownloadManager::~nsDownloadManager()
125 if (--gRefCnt != 0 || !gRDFService)
126 // Either somebody tried to use |CreateInstance| instead of
127 // |GetService| or |Init| failed very early, so there's nothing to
128 // do here.
129 return;
131 gRDFService->UnregisterDataSource(mDataSource);
133 NS_IF_RELEASE(gNC_DownloadsRoot);
134 NS_IF_RELEASE(gNC_File);
135 NS_IF_RELEASE(gNC_URL);
136 NS_IF_RELEASE(gNC_Name);
137 NS_IF_RELEASE(gNC_ProgressMode);
138 NS_IF_RELEASE(gNC_ProgressPercent);
139 NS_IF_RELEASE(gNC_Transferred);
140 NS_IF_RELEASE(gNC_DownloadState);
141 NS_IF_RELEASE(gNC_StatusText);
143 NS_RELEASE(gRDFService);
146 nsresult
147 nsDownloadManager::Init()
149 if (gRefCnt++ != 0) {
150 NS_NOTREACHED("download manager should be used as a service");
151 return NS_ERROR_UNEXPECTED; // This will make the |CreateInstance| fail.
154 if (!mCurrDownloads.Init())
155 return NS_ERROR_FAILURE;
157 nsresult rv;
158 mRDFContainerUtils = do_GetService("@mozilla.org/rdf/container-utils;1", &rv);
159 if (NS_FAILED(rv)) return rv;
161 nsCOMPtr<nsIObserverService> obsService = do_GetService("@mozilla.org/observer-service;1", &rv);
162 if (NS_FAILED(rv)) return rv;
165 rv = CallGetService("@mozilla.org/rdf/rdf-service;1", &gRDFService);
166 if (NS_FAILED(rv)) return rv;
168 gRDFService->GetResource(NS_LITERAL_CSTRING("NC:DownloadsRoot"), &gNC_DownloadsRoot);
169 gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "File"), &gNC_File);
170 gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "URL"), &gNC_URL);
171 gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Name"), &gNC_Name);
172 gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "ProgressMode"), &gNC_ProgressMode);
173 gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "ProgressPercent"), &gNC_ProgressPercent);
174 gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Transferred"), &gNC_Transferred);
175 gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "DownloadState"), &gNC_DownloadState);
176 gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "StatusText"), &gNC_StatusText);
178 nsCAutoString downloadsDB;
179 rv = GetProfileDownloadsFileURL(downloadsDB);
180 if (NS_FAILED(rv)) return rv;
182 rv = gRDFService->GetDataSourceBlocking(downloadsDB.get(), getter_AddRefs(mDataSource));
183 if (NS_FAILED(rv)) return rv;
185 mListener = do_CreateInstance("@mozilla.org/download-manager/listener;1", &rv);
186 if (NS_FAILED(rv)) return rv;
188 nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
189 if (NS_FAILED(rv)) return rv;
191 rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, getter_AddRefs(mBundle));
192 if (NS_FAILED(rv))
193 return rv;
195 // The following two AddObserver calls must be the last lines in this function,
196 // because otherwise, this function may fail (and thus, this object would be not
197 // completely initialized), but the observerservice would still keep a reference
198 // to us and notify us about shutdown, which may cause crashes.
199 // failure to add an observer is not critical
200 obsService->AddObserver(this, "profile-before-change", PR_FALSE);
201 obsService->AddObserver(this, "profile-approve-change", PR_FALSE);
203 return NS_OK;
206 nsresult
207 nsDownloadManager::DownloadStarted(const nsACString& aTargetPath)
209 if (mCurrDownloads.GetWeak(aTargetPath))
210 AssertProgressInfoFor(aTargetPath);
212 return NS_OK;
215 nsresult
216 nsDownloadManager::DownloadEnded(const nsACString& aTargetPath, const PRUnichar* aMessage)
218 nsDownload* dl = mCurrDownloads.GetWeak(aTargetPath);
219 if (dl) {
220 AssertProgressInfoFor(aTargetPath);
221 mCurrDownloads.Remove(aTargetPath);
224 return NS_OK;
227 nsresult
228 nsDownloadManager::GetProfileDownloadsFileURL(nsCString& aDownloadsFileURL)
230 nsCOMPtr<nsIFile> downloadsFile;
231 nsresult rv = NS_GetSpecialDirectory(NS_APP_DOWNLOADS_50_FILE, getter_AddRefs(downloadsFile));
232 if (NS_FAILED(rv))
233 return rv;
235 return NS_GetURLSpecFromFile(downloadsFile, aDownloadsFileURL);
238 nsresult
239 nsDownloadManager::GetDownloadsContainer(nsIRDFContainer** aResult)
241 if (mDownloadsContainer) {
242 *aResult = mDownloadsContainer;
243 NS_ADDREF(*aResult);
244 return NS_OK;
247 PRBool isContainer;
248 nsresult rv = mRDFContainerUtils->IsContainer(mDataSource, gNC_DownloadsRoot, &isContainer);
249 if (NS_FAILED(rv)) return rv;
251 if (!isContainer) {
252 rv = mRDFContainerUtils->MakeSeq(mDataSource, gNC_DownloadsRoot, getter_AddRefs(mDownloadsContainer));
253 if (NS_FAILED(rv)) return rv;
255 else {
256 mDownloadsContainer = do_CreateInstance(NS_RDF_CONTRACTID "/container;1", &rv);
257 if (NS_FAILED(rv)) return rv;
258 rv = mDownloadsContainer->Init(mDataSource, gNC_DownloadsRoot);
259 if (NS_FAILED(rv)) return rv;
262 *aResult = mDownloadsContainer;
263 NS_IF_ADDREF(*aResult);
265 return rv;
268 nsresult
269 nsDownloadManager::GetInternalListener(nsIDownloadProgressListener** aInternalListener)
271 *aInternalListener = mListener;
272 NS_IF_ADDREF(*aInternalListener);
273 return NS_OK;
276 nsresult
277 nsDownloadManager::GetDataSource(nsIRDFDataSource** aDataSource)
279 *aDataSource = mDataSource;
280 NS_ADDREF(*aDataSource);
281 return NS_OK;
284 nsresult
285 nsDownloadManager::AssertProgressInfo()
287 nsCOMPtr<nsISupports> supports;
288 nsCOMPtr<nsIRDFResource> res;
289 nsCOMPtr<nsIRDFInt> intLiteral;
291 gRDFService->GetIntLiteral(DOWNLOADING, getter_AddRefs(intLiteral));
292 nsCOMPtr<nsISimpleEnumerator> downloads;
293 nsresult rv = mDataSource->GetSources(gNC_DownloadState, intLiteral, PR_TRUE, getter_AddRefs(downloads));
294 if (NS_FAILED(rv)) return rv;
296 PRBool hasMoreElements;
297 downloads->HasMoreElements(&hasMoreElements);
299 while (hasMoreElements) {
300 const char* uri;
301 downloads->GetNext(getter_AddRefs(supports));
302 res = do_QueryInterface(supports);
303 res->GetValueConst(&uri);
304 AssertProgressInfoFor(nsDependentCString(uri));
305 downloads->HasMoreElements(&hasMoreElements);
307 return rv;
310 nsresult
311 nsDownloadManager::AssertProgressInfoFor(const nsACString& aTargetPath)
313 nsDownload* internalDownload = mCurrDownloads.GetWeak(aTargetPath);
314 if (!internalDownload)
315 return NS_ERROR_FAILURE;
317 nsresult rv;
318 PRInt32 percentComplete;
319 nsCOMPtr<nsIRDFNode> oldTarget;
320 nsCOMPtr<nsIRDFInt> intLiteral;
321 nsCOMPtr<nsIRDFResource> res;
322 nsCOMPtr<nsIRDFLiteral> literal;
324 gRDFService->GetResource(aTargetPath, getter_AddRefs(res));
326 DownloadState state = internalDownload->GetDownloadState();
328 // update progress mode
329 nsAutoString progressMode;
330 if (state == DOWNLOADING)
331 progressMode.AssignLiteral("normal");
332 else
333 progressMode.AssignLiteral("none");
335 gRDFService->GetLiteral(progressMode.get(), getter_AddRefs(literal));
337 rv = mDataSource->GetTarget(res, gNC_ProgressMode, PR_TRUE, getter_AddRefs(oldTarget));
339 if (oldTarget)
340 rv = mDataSource->Change(res, gNC_ProgressMode, oldTarget, literal);
341 else
342 rv = mDataSource->Assert(res, gNC_ProgressMode, literal, PR_TRUE);
343 if (NS_FAILED(rv)) return rv;
345 // update download state (not started, downloading, queued, finished, etc...)
346 gRDFService->GetIntLiteral(state, getter_AddRefs(intLiteral));
348 mDataSource->GetTarget(res, gNC_DownloadState, PR_TRUE, getter_AddRefs(oldTarget));
350 if (oldTarget) {
351 rv = mDataSource->Change(res, gNC_DownloadState, oldTarget, intLiteral);
352 if (NS_FAILED(rv)) return rv;
355 nsAutoString strKey;
356 if (state == NOTSTARTED)
357 strKey.AssignLiteral("notStarted");
358 else if (state == DOWNLOADING)
359 strKey.AssignLiteral("downloading");
360 else if (state == FINISHED)
361 strKey.AssignLiteral("finished");
362 else if (state == FAILED)
363 strKey.AssignLiteral("failed");
364 else if (state == CANCELED)
365 strKey.AssignLiteral("canceled");
367 nsXPIDLString value;
368 rv = mBundle->GetStringFromName(strKey.get(), getter_Copies(value));
369 if (NS_FAILED(rv)) return rv;
371 gRDFService->GetLiteral(value, getter_AddRefs(literal));
373 rv = mDataSource->GetTarget(res, gNC_StatusText, PR_TRUE, getter_AddRefs(oldTarget));
375 if (oldTarget) {
376 rv = mDataSource->Change(res, gNC_StatusText, oldTarget, literal);
377 if (NS_FAILED(rv)) return rv;
379 else {
380 rv = mDataSource->Assert(res, gNC_StatusText, literal, PR_TRUE);
381 if (NS_FAILED(rv)) return rv;
384 // update percentage
385 internalDownload->GetPercentComplete(&percentComplete);
387 mDataSource->GetTarget(res, gNC_ProgressPercent, PR_TRUE, getter_AddRefs(oldTarget));
388 gRDFService->GetIntLiteral(percentComplete, getter_AddRefs(intLiteral));
390 if (oldTarget)
391 rv = mDataSource->Change(res, gNC_ProgressPercent, oldTarget, intLiteral);
392 else
393 rv = mDataSource->Assert(res, gNC_ProgressPercent, intLiteral, PR_TRUE);
394 if (NS_FAILED(rv)) return rv;
396 // update transferred
397 nsDownload::TransferInformation transferInfo =
398 internalDownload->GetTransferInformation();
400 // convert from bytes to kbytes for progress display
401 PRInt64 current = (transferInfo.mCurrBytes + 512) / 1024;
402 PRInt64 max = (transferInfo.mMaxBytes + 512) / 1024;
404 nsAutoString currBytes; currBytes.AppendInt(current);
405 nsAutoString maxBytes; maxBytes.AppendInt(max);
406 const PRUnichar *strings[] = {
407 currBytes.get(),
408 maxBytes.get()
411 rv = mBundle->FormatStringFromName(NS_LITERAL_STRING("transferred").get(),
412 strings, 2, getter_Copies(value));
413 if (NS_FAILED(rv)) return rv;
415 gRDFService->GetLiteral(value, getter_AddRefs(literal));
417 mDataSource->GetTarget(res, gNC_Transferred, PR_TRUE, getter_AddRefs(oldTarget));
419 if (oldTarget)
420 rv = mDataSource->Change(res, gNC_Transferred, oldTarget, literal);
421 else
422 rv = mDataSource->Assert(res, gNC_Transferred, literal, PR_TRUE);
423 if (NS_FAILED(rv)) return rv;
425 nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mDataSource);
426 remote->Flush();
428 // XXX should also store and update time elapsed
429 return rv;
432 ///////////////////////////////////////////////////////////////////////////////
433 // nsIDownloadManager
435 NS_IMETHODIMP
436 nsDownloadManager::AddDownload(nsIURI* aSource,
437 nsIURI* aTarget,
438 const nsAString& aDisplayName,
439 nsIMIMEInfo *aMIMEInfo,
440 PRTime aStartTime,
441 nsILocalFile* aTempFile,
442 nsICancelable* aCancelable,
443 nsIDownload** aDownload)
445 NS_ENSURE_ARG_POINTER(aSource);
446 NS_ENSURE_ARG_POINTER(aTarget);
447 NS_ENSURE_ARG_POINTER(aDownload);
449 nsCOMPtr<nsIRDFContainer> downloads;
450 nsresult rv = GetDownloadsContainer(getter_AddRefs(downloads));
451 if (NS_FAILED(rv)) return rv;
453 // this will create a cycle that will be broken in nsDownload::OnStateChange
454 nsDownload* internalDownload = new nsDownload(this, aTarget, aSource, aCancelable);
455 if (!internalDownload)
456 return NS_ERROR_OUT_OF_MEMORY;
458 NS_ADDREF(*aDownload = internalDownload);
460 // the path of the target is the unique identifier we use
461 nsCOMPtr<nsILocalFile> targetFile;
462 rv = internalDownload->GetTargetFile(getter_AddRefs(targetFile));
463 if (NS_FAILED(rv)) return rv;
465 nsAutoString path;
466 rv = targetFile->GetPath(path);
467 if (NS_FAILED(rv)) return rv;
469 NS_ConvertUTF16toUTF8 utf8Path(path);
471 nsCOMPtr<nsIRDFResource> downloadRes;
472 gRDFService->GetResource(utf8Path, getter_AddRefs(downloadRes));
474 nsCOMPtr<nsIRDFNode> node;
476 // Assert source url information
477 nsCAutoString spec;
478 aSource->GetSpec(spec);
480 nsCOMPtr<nsIRDFResource> urlResource;
481 gRDFService->GetResource(spec, getter_AddRefs(urlResource));
482 mDataSource->GetTarget(downloadRes, gNC_URL, PR_TRUE, getter_AddRefs(node));
483 if (node)
484 rv = mDataSource->Change(downloadRes, gNC_URL, node, urlResource);
485 else
486 rv = mDataSource->Assert(downloadRes, gNC_URL, urlResource, PR_TRUE);
487 if (NS_FAILED(rv)) return rv;
489 // Set and assert the "pretty" (display) name of the download
490 nsAutoString displayName; displayName.Assign(aDisplayName);
491 if (displayName.IsEmpty()) {
492 targetFile->GetLeafName(displayName);
494 internalDownload->SetDisplayName(displayName.get());
495 internalDownload->SetTempFile(aTempFile);
497 nsCOMPtr<nsIRDFLiteral> nameLiteral;
498 gRDFService->GetLiteral(displayName.get(), getter_AddRefs(nameLiteral));
499 mDataSource->GetTarget(downloadRes, gNC_Name, PR_TRUE, getter_AddRefs(node));
500 if (node)
501 rv = mDataSource->Change(downloadRes, gNC_Name, node, nameLiteral);
502 else
503 rv = mDataSource->Assert(downloadRes, gNC_Name, nameLiteral, PR_TRUE);
504 if (NS_FAILED(rv)) return rv;
506 internalDownload->SetMIMEInfo(aMIMEInfo);
507 internalDownload->SetStartTime(aStartTime);
509 // Assert file information
510 nsCOMPtr<nsIRDFResource> fileResource;
511 gRDFService->GetResource(utf8Path, getter_AddRefs(fileResource));
512 rv = mDataSource->Assert(downloadRes, gNC_File, fileResource, PR_TRUE);
513 if (NS_FAILED(rv)) return rv;
515 // Assert download state information (NOTSTARTED, since it's just now being added)
516 nsCOMPtr<nsIRDFInt> intLiteral;
517 gRDFService->GetIntLiteral(NOTSTARTED, getter_AddRefs(intLiteral));
518 mDataSource->GetTarget(downloadRes, gNC_DownloadState, PR_TRUE, getter_AddRefs(node));
519 if (node)
520 rv = mDataSource->Change(downloadRes, gNC_DownloadState, node, intLiteral);
521 else
522 rv = mDataSource->Assert(downloadRes, gNC_DownloadState, intLiteral, PR_TRUE);
523 if (NS_FAILED(rv)) return rv;
525 PRInt32 itemIndex;
526 downloads->IndexOf(downloadRes, &itemIndex);
527 if (itemIndex == -1) {
528 rv = downloads->AppendElement(downloadRes);
529 if (NS_FAILED(rv)) return rv;
532 // Now flush all this to disk
533 nsCOMPtr<nsIRDFRemoteDataSource> remote(do_QueryInterface(mDataSource));
534 rv = remote->Flush();
535 if (NS_FAILED(rv)) return rv;
537 mCurrDownloads.Put(utf8Path, internalDownload);
539 return rv;
542 NS_IMETHODIMP
543 nsDownloadManager::GetDownload(const nsACString & aTargetPath, nsIDownload** aDownloadItem)
545 NS_ENSURE_ARG_POINTER(aDownloadItem);
547 // if it's currently downloading we can get it from the table
548 // XXX otherwise we should look for it in the datasource and
549 // create a new nsIDownload with the resource's properties
550 NS_IF_ADDREF(*aDownloadItem = mCurrDownloads.GetWeak(aTargetPath));
551 return NS_OK;
554 NS_IMETHODIMP
555 nsDownloadManager::CancelDownload(const nsACString & aTargetPath)
557 nsRefPtr<nsDownload> internalDownload = mCurrDownloads.GetWeak(aTargetPath);
558 if (!internalDownload)
559 return NS_ERROR_FAILURE;
561 return internalDownload->Cancel();
564 NS_IMETHODIMP
565 nsDownloadManager::PauseDownload(nsIDownload* aDownload)
567 NS_ENSURE_ARG_POINTER(aDownload);
568 return static_cast<nsDownload*>(aDownload)->Suspend();
571 NS_IMETHODIMP
572 nsDownloadManager::ResumeDownload(const nsACString & aTargetPath)
574 nsDownload* dl = mCurrDownloads.GetWeak(aTargetPath);
575 if (!dl)
576 return NS_ERROR_NOT_AVAILABLE;
577 return dl->Resume();
580 NS_IMETHODIMP
581 nsDownloadManager::RemoveDownload(const nsACString & aTargetPath)
583 // RemoveDownload is for downloads not currently in progress. Having it
584 // cancel in-progress downloads would make things complicated, so just return.
585 nsDownload* inProgress = mCurrDownloads.GetWeak(aTargetPath);
586 NS_ASSERTION(!inProgress, "Can't call RemoveDownload on a download in progress!");
587 if (inProgress)
588 return NS_ERROR_FAILURE;
590 nsCOMPtr<nsIRDFContainer> downloads;
591 nsresult rv = GetDownloadsContainer(getter_AddRefs(downloads));
592 if (NS_FAILED(rv)) return rv;
594 nsCOMPtr<nsIRDFResource> res;
595 gRDFService->GetResource(aTargetPath, getter_AddRefs(res));
597 // remove all the arcs for this resource, and then remove it from the Seq
598 nsCOMPtr<nsISimpleEnumerator> arcs;
599 rv = mDataSource->ArcLabelsOut(res, getter_AddRefs(arcs));
600 if (NS_FAILED(rv)) return rv;
602 PRBool moreArcs;
603 rv = arcs->HasMoreElements(&moreArcs);
604 if (NS_FAILED(rv)) return rv;
606 while (moreArcs) {
607 nsCOMPtr<nsISupports> supports;
608 rv = arcs->GetNext(getter_AddRefs(supports));
609 if (NS_FAILED(rv)) return rv;
611 nsCOMPtr<nsIRDFResource> arc(do_QueryInterface(supports, &rv));
612 if (NS_FAILED(rv)) return rv;
614 nsCOMPtr<nsISimpleEnumerator> targets;
615 rv = mDataSource->GetTargets(res, arc, PR_TRUE, getter_AddRefs(targets));
616 if (NS_FAILED(rv)) return rv;
618 PRBool moreTargets;
619 rv = targets->HasMoreElements(&moreTargets);
620 if (NS_FAILED(rv)) return rv;
622 while (moreTargets) {
623 rv = targets->GetNext(getter_AddRefs(supports));
624 if (NS_FAILED(rv)) return rv;
626 nsCOMPtr<nsIRDFNode> target(do_QueryInterface(supports, &rv));
627 if (NS_FAILED(rv)) return rv;
629 // and now drop this assertion from the graph
630 rv = mDataSource->Unassert(res, arc, target);
631 if (NS_FAILED(rv)) return rv;
633 rv = targets->HasMoreElements(&moreTargets);
634 if (NS_FAILED(rv)) return rv;
636 rv = arcs->HasMoreElements(&moreArcs);
637 if (NS_FAILED(rv)) return rv;
640 PRInt32 itemIndex;
641 downloads->IndexOf(res, &itemIndex);
642 if (itemIndex <= 0)
643 return NS_ERROR_FAILURE;
645 nsCOMPtr<nsIRDFNode> node;
646 rv = downloads->RemoveElementAt(itemIndex, PR_TRUE, getter_AddRefs(node));
647 if (NS_FAILED(rv)) return rv;
649 // if a mass removal is being done, we don't want to flush every time
650 if (mBatches) return rv;
652 nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mDataSource);
653 return remote->Flush();
656 NS_IMETHODIMP
657 nsDownloadManager::StartBatchUpdate()
659 ++mBatches;
660 return NS_OK;
663 NS_IMETHODIMP
664 nsDownloadManager::EndBatchUpdate()
666 nsresult rv = NS_OK;
667 if (--mBatches == 0) {
668 nsCOMPtr<nsIRDFRemoteDataSource> remote = do_QueryInterface(mDataSource);
669 rv = remote->Flush();
671 return rv;
674 NS_IMETHODIMP
675 nsDownloadManager::Open(nsIDOMWindow* aParent, nsIDownload* aDownload)
678 // first assert new progress info so the ui is correctly updated
679 // if this fails, it fails -- continue.
680 AssertProgressInfo();
682 // check for an existing manager window and focus it
683 nsresult rv;
684 nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
685 if (NS_FAILED(rv)) return rv;
687 nsCOMPtr<nsISupports> dlSupports(do_QueryInterface(aDownload));
689 // if the window's already open, do nothing (focusing it would be annoying)
690 nsCOMPtr<nsIDOMWindowInternal> recentWindow;
691 wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(), getter_AddRefs(recentWindow));
692 if (recentWindow) {
693 nsCOMPtr<nsIObserverService> obsService = do_GetService("@mozilla.org/observer-service;1", &rv);
694 if (NS_FAILED(rv)) return rv;
695 return obsService->NotifyObservers(dlSupports, "download-starting", nsnull);
698 // if we ever have the capability to display the UI of third party dl managers,
699 // we'll open their UI here instead.
700 nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
701 if (NS_FAILED(rv)) return rv;
703 // pass the datasource to the window
704 nsCOMPtr<nsISupportsArray> params(do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID));
705 nsCOMPtr<nsISupports> dsSupports(do_QueryInterface(mDataSource));
706 params->AppendElement(dsSupports);
707 params->AppendElement(dlSupports);
709 nsCOMPtr<nsIDOMWindow> newWindow;
710 rv = ww->OpenWindow(aParent,
711 DOWNLOAD_MANAGER_FE_URL,
712 "_blank",
713 "chrome,all,dialog=no,resizable",
714 params,
715 getter_AddRefs(newWindow));
717 if (NS_FAILED(rv)) return rv;
719 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(newWindow);
720 if (!target) return NS_ERROR_FAILURE;
722 rv = target->AddEventListener(NS_LITERAL_STRING("load"), this, PR_FALSE);
723 if (NS_FAILED(rv)) return rv;
725 return target->AddEventListener(NS_LITERAL_STRING("unload"), this, PR_FALSE);
728 NS_IMETHODIMP
729 nsDownloadManager::OpenProgressDialogFor(nsIDownload* aDownload, nsIDOMWindow* aParent, PRBool aCancelDownloadOnClose)
731 NS_ENSURE_ARG_POINTER(aDownload);
732 nsresult rv;
733 nsDownload* internalDownload = static_cast<nsDownload*>(aDownload);
734 nsIProgressDialog* oldDialog = internalDownload->GetDialog();
736 if (oldDialog) {
737 nsCOMPtr<nsIDOMWindow> window;
738 oldDialog->GetDialog(getter_AddRefs(window));
739 if (window) {
740 nsCOMPtr<nsIDOMWindowInternal> internalWin = do_QueryInterface(window);
741 internalWin->Focus();
742 return NS_OK;
746 nsCOMPtr<nsIProgressDialog> dialog(do_CreateInstance("@mozilla.org/progressdialog;1", &rv));
747 if (NS_FAILED(rv)) return rv;
749 dialog->SetCancelDownloadOnClose(aCancelDownloadOnClose);
751 // now give the dialog the necessary context
753 // start time...
754 PRInt64 startTime = 0;
755 aDownload->GetStartTime(&startTime);
757 // source...
758 nsCOMPtr<nsIURI> source;
759 aDownload->GetSource(getter_AddRefs(source));
761 // target...
762 nsCOMPtr<nsIURI> target;
763 aDownload->GetTarget(getter_AddRefs(target));
765 // helper app...
766 nsCOMPtr<nsIMIMEInfo> mimeInfo;
767 aDownload->GetMIMEInfo(getter_AddRefs(mimeInfo));
769 dialog->Init(source, target, EmptyString(), mimeInfo, startTime, nsnull,
770 nsnull);
771 dialog->SetObserver(internalDownload);
773 // now set the listener so we forward notifications to the dialog
774 nsCOMPtr<nsIDownloadProgressListener> listener = do_QueryInterface(dialog);
775 internalDownload->SetDialogListener(listener);
777 internalDownload->SetDialog(dialog);
779 return dialog->Open(aParent);
782 NS_IMETHODIMP
783 nsDownloadManager::OnClose()
785 mDocument = nsnull;
786 mListener->SetDocument(nsnull);
787 return NS_OK;
790 ///////////////////////////////////////////////////////////////////////////////
791 // nsIDOMEventListener
793 NS_IMETHODIMP
794 nsDownloadManager::HandleEvent(nsIDOMEvent* aEvent)
796 // the event is either load or unload
797 nsAutoString eventType;
798 aEvent->GetType(eventType);
799 if (eventType.EqualsLiteral("unload"))
800 return OnClose();
802 nsCOMPtr<nsIDOMEventTarget> target;
803 nsresult rv = aEvent->GetTarget(getter_AddRefs(target));
804 if (NS_FAILED(rv)) return rv;
806 mDocument = do_QueryInterface(target);
807 mListener->SetDocument(mDocument);
808 return NS_OK;
811 ///////////////////////////////////////////////////////////////////////////////
812 // nsIObserver
814 NS_IMETHODIMP
815 nsDownloadManager::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
817 nsresult rv;
818 if (nsCRT::strcmp(aTopic, "profile-approve-change") == 0) {
819 // Only run this on profile switch
820 if (!NS_LITERAL_STRING("switch").Equals(aData))
821 return NS_OK;
823 // If count == 0, nothing to do
824 if (mCurrDownloads.Count() == 0)
825 return NS_OK;
827 nsCOMPtr<nsIProfileChangeStatus> changeStatus(do_QueryInterface(aSubject));
828 if (!changeStatus)
829 return NS_ERROR_UNEXPECTED;
831 nsXPIDLString title, text, proceed, cancel;
832 nsresult rv = mBundle->GetStringFromName(NS_LITERAL_STRING("profileSwitchTitle").get(),
833 getter_Copies(title));
834 NS_ENSURE_SUCCESS(rv, rv);
835 rv = mBundle->GetStringFromName(NS_LITERAL_STRING("profileSwitchText").get(),
836 getter_Copies(text));
837 NS_ENSURE_SUCCESS(rv, rv);
838 rv = mBundle->GetStringFromName(NS_LITERAL_STRING("profileSwitchContinue").get(),
839 getter_Copies(proceed));
840 NS_ENSURE_SUCCESS(rv, rv);
842 nsCOMPtr<nsIPromptService> promptService(do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv));
843 if (NS_FAILED(rv))
844 return rv;
846 PRInt32 button;
847 rv = promptService->ConfirmEx(nsnull, title.get(), text.get(),
848 nsIPromptService::BUTTON_TITLE_CANCEL * nsIPromptService::BUTTON_POS_0 |
849 nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_1,
850 nsnull,
851 proceed.get(),
852 nsnull,
853 nsnull,
854 nsnull,
855 &button);
856 if (NS_FAILED(rv))
857 return rv;
859 if (button == 0)
860 changeStatus->VetoChange();
862 else if (nsCRT::strcmp(aTopic, "profile-before-change") == 0) {
863 nsCOMPtr<nsISupports> supports;
864 nsCOMPtr<nsIRDFResource> res;
865 nsCOMPtr<nsIRDFInt> intLiteral;
867 gRDFService->GetIntLiteral(DOWNLOADING, getter_AddRefs(intLiteral));
868 nsCOMPtr<nsISimpleEnumerator> downloads;
869 rv = mDataSource->GetSources(gNC_DownloadState, intLiteral, PR_TRUE, getter_AddRefs(downloads));
870 if (NS_FAILED(rv)) return rv;
872 PRBool hasMoreElements;
873 downloads->HasMoreElements(&hasMoreElements);
875 while (hasMoreElements) {
876 const char* uri;
878 downloads->GetNext(getter_AddRefs(supports));
879 res = do_QueryInterface(supports);
880 res->GetValueConst(&uri);
881 CancelDownload(nsDependentCString(uri));
882 downloads->HasMoreElements(&hasMoreElements);
885 return NS_OK;
888 ///////////////////////////////////////////////////////////////////////////////
889 // nsDownload
891 NS_IMPL_ISUPPORTS5(nsDownload, nsIDownload, nsITransfer, nsIWebProgressListener,
892 nsIWebProgressListener2, nsIObserver)
894 nsDownload::nsDownload(nsDownloadManager* aManager,
895 nsIURI* aTarget,
896 nsIURI* aSource,
897 nsICancelable* aCancelable) :
898 mDownloadManager(aManager),
899 mTarget(aTarget),
900 mSource(aSource),
901 mCancelable(aCancelable),
902 mDownloadState(NOTSTARTED),
903 mPercentComplete(0),
904 mCurrBytes(LL_ZERO),
905 mMaxBytes(LL_ZERO),
906 mStartTime(LL_ZERO),
907 mLastUpdate(PR_Now() - (PRUint32)gInterval),
908 mSpeed(0)
912 nsDownload::~nsDownload()
914 nsCAutoString path;
915 nsresult rv = GetFilePathUTF8(mTarget, path);
916 if (NS_FAILED(rv)) return;
918 mDownloadManager->AssertProgressInfoFor(path);
921 nsresult
922 nsDownload::Suspend()
924 if (!mRequest)
925 return NS_ERROR_UNEXPECTED;
926 return mRequest->Suspend();
929 nsresult
930 nsDownload::Cancel()
932 // Don't cancel if download is already finished or canceled
933 if (GetDownloadState() == FINISHED || GetDownloadState() == CANCELED)
934 return NS_OK;
936 nsresult rv = mCancelable->Cancel(NS_BINDING_ABORTED);
937 if (NS_FAILED(rv))
938 return rv;
940 SetDownloadState(CANCELED);
942 nsCAutoString path;
943 rv = GetFilePathUTF8(mTarget, path);
944 if (NS_FAILED(rv))
945 return rv;
946 mDownloadManager->DownloadEnded(path, nsnull);
948 // Dump the temp file. This should really be done when the transfer
949 // is cancelled, but there are other cancellation causes that shouldn't
950 // remove this. We need to improve those bits.
951 if (mTempFile) {
952 PRBool exists;
953 mTempFile->Exists(&exists);
954 if (exists)
955 mTempFile->Remove(PR_FALSE);
958 // if there's a progress dialog open for the item,
959 // we have to notify it that we're cancelling
960 nsCOMPtr<nsIObserver> observer = do_QueryInterface(GetDialog());
961 if (observer) {
962 rv = observer->Observe(static_cast<nsIDownload*>(this), "oncancel", nsnull);
965 return rv;
968 nsresult
969 nsDownload::SetDisplayName(const PRUnichar* aDisplayName)
971 mDisplayName = aDisplayName;
973 nsCOMPtr<nsIRDFDataSource> ds;
974 mDownloadManager->GetDataSource(getter_AddRefs(ds));
976 nsCOMPtr<nsIRDFLiteral> nameLiteral;
977 nsCOMPtr<nsIRDFResource> res;
978 nsCAutoString path;
979 nsresult rv = GetFilePathUTF8(mTarget, path);
980 if (NS_FAILED(rv)) return rv;
982 gRDFService->GetResource(path, getter_AddRefs(res));
984 gRDFService->GetLiteral(aDisplayName, getter_AddRefs(nameLiteral));
985 ds->Assert(res, gNC_Name, nameLiteral, PR_TRUE);
987 return NS_OK;
990 nsresult
991 nsDownload::Resume()
993 if (!mRequest)
994 return NS_ERROR_UNEXPECTED;
995 return mRequest->Resume();
998 ///////////////////////////////////////////////////////////////////////////////
999 // nsIObserver
1000 NS_IMETHODIMP
1001 nsDownload::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
1003 if (strcmp(aTopic, "onpause") == 0) {
1004 return Suspend();
1006 if (strcmp(aTopic, "onresume") == 0) {
1007 return Resume();
1009 if (strcmp(aTopic, "oncancel") == 0) {
1010 SetDialog(nsnull);
1012 Cancel();
1013 // Ignoring return value; this function will get called twice,
1014 // and bad things happen if we return a failure code the second time.
1015 return NS_OK;
1018 if (strcmp(aTopic, "alertclickcallback") == 0) {
1019 // show the download manager
1020 mDownloadManager->Open(nsnull, this);
1021 return NS_OK;
1024 return NS_OK;
1027 ///////////////////////////////////////////////////////////////////////////////
1028 // nsIWebProgressListener2
1030 NS_IMETHODIMP
1031 nsDownload::OnProgressChange64(nsIWebProgress *aWebProgress,
1032 nsIRequest *aRequest,
1033 PRInt64 aCurSelfProgress,
1034 PRInt64 aMaxSelfProgress,
1035 PRInt64 aCurTotalProgress,
1036 PRInt64 aMaxTotalProgress)
1038 if (!mRequest)
1039 mRequest = aRequest; // used for pause/resume
1041 // Filter notifications since they come in so frequently, but we want to
1042 // process the last notification.
1043 PRTime now = PR_Now();
1044 nsInt64 delta = now - mLastUpdate;
1045 if (delta < gInterval && aCurTotalProgress != aMaxTotalProgress)
1046 return NS_OK;
1048 mLastUpdate = now;
1050 if (mDownloadState == NOTSTARTED) {
1051 nsCAutoString path;
1052 nsresult rv = GetFilePathUTF8(mTarget, path);
1053 if (NS_FAILED(rv)) return rv;
1055 mDownloadState = DOWNLOADING;
1056 mDownloadManager->DownloadStarted(path);
1059 // Calculate the speed using the elapsed delta time and bytes downloaded
1060 // during that time for more accuracy.
1061 double elapsedSecs = double(delta) / PR_USEC_PER_SEC;
1062 if (elapsedSecs > 0) {
1063 nsUint64 curTotalProgress = (PRUint64)aCurTotalProgress;
1064 nsUint64 diffBytes = curTotalProgress - nsUint64(mCurrBytes);
1065 double speed = double(diffBytes) / elapsedSecs;
1066 if (LL_IS_ZERO(mCurrBytes))
1067 mSpeed = speed;
1068 else {
1069 // Calculate 'smoothed average' of 10 readings.
1070 mSpeed = mSpeed * 0.9 + speed * 0.1;
1074 if (aMaxTotalProgress > 0)
1075 mPercentComplete = aCurTotalProgress * 100 / aMaxTotalProgress;
1076 else
1077 mPercentComplete = -1;
1079 mCurrBytes = aCurTotalProgress;
1080 mMaxBytes = aMaxTotalProgress;
1082 if (mDownloadManager->MustUpdateUI()) {
1083 nsCOMPtr<nsIDownloadProgressListener> internalListener;
1084 mDownloadManager->GetInternalListener(getter_AddRefs(internalListener));
1085 if (internalListener) {
1086 internalListener->OnProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress,
1087 aCurTotalProgress, aMaxTotalProgress, this);
1091 if (mDialogListener) {
1092 mDialogListener->OnProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress,
1093 aCurTotalProgress, aMaxTotalProgress, this);
1096 return NS_OK;
1099 ///////////////////////////////////////////////////////////////////////////////
1100 // nsIWebProgressListener
1102 NS_IMETHODIMP
1103 nsDownload::OnProgressChange(nsIWebProgress *aWebProgress,
1104 nsIRequest *aRequest,
1105 PRInt32 aCurSelfProgress,
1106 PRInt32 aMaxSelfProgress,
1107 PRInt32 aCurTotalProgress,
1108 PRInt32 aMaxTotalProgress)
1110 return OnProgressChange64(aWebProgress, aRequest,
1111 aCurSelfProgress, aMaxSelfProgress,
1112 aCurTotalProgress, aMaxTotalProgress);
1115 NS_IMETHODIMP
1116 nsDownload::OnRefreshAttempted(nsIWebProgress *aWebProgress,
1117 nsIURI *aUri,
1118 PRInt32 aDelay,
1119 PRBool aSameUri,
1120 PRBool *allowRefresh)
1122 *allowRefresh = PR_TRUE;
1123 return NS_OK;
1126 NS_IMETHODIMP
1127 nsDownload::OnLocationChange(nsIWebProgress *aWebProgress,
1128 nsIRequest *aRequest, nsIURI *aLocation)
1130 if (mDownloadManager->MustUpdateUI()) {
1131 nsCOMPtr<nsIDownloadProgressListener> internalListener;
1132 mDownloadManager->GetInternalListener(getter_AddRefs(internalListener));
1133 if (internalListener)
1134 internalListener->OnLocationChange(aWebProgress, aRequest, aLocation, this);
1137 if (mDialogListener)
1138 mDialogListener->OnLocationChange(aWebProgress, aRequest, aLocation, this);
1140 return NS_OK;
1143 NS_IMETHODIMP
1144 nsDownload::OnStatusChange(nsIWebProgress *aWebProgress,
1145 nsIRequest *aRequest, nsresult aStatus,
1146 const PRUnichar *aMessage)
1148 if (NS_FAILED(aStatus)) {
1149 mDownloadState = FAILED;
1150 nsCAutoString path;
1151 nsresult rv = GetFilePathUTF8(mTarget, path);
1152 if (NS_SUCCEEDED(rv))
1153 mDownloadManager->DownloadEnded(path, aMessage);
1156 if (mDownloadManager->MustUpdateUI()) {
1157 nsCOMPtr<nsIDownloadProgressListener> internalListener;
1158 mDownloadManager->GetInternalListener(getter_AddRefs(internalListener));
1159 if (internalListener)
1160 internalListener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage, this);
1163 if (mDialogListener)
1164 mDialogListener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage, this);
1165 else {
1166 // Need to display error alert ourselves, if an error occurred.
1167 if (NS_FAILED(aStatus)) {
1168 // Get title for alert.
1169 nsXPIDLString title;
1170 nsresult rv;
1171 nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
1172 nsCOMPtr<nsIStringBundle> bundle;
1173 if (bundleService)
1174 rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, getter_AddRefs(bundle));
1175 if (bundle)
1176 bundle->GetStringFromName(NS_LITERAL_STRING("alertTitle").get(), getter_Copies(title));
1178 // Get Download Manager window, to be parent of alert.
1179 nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
1180 nsCOMPtr<nsIDOMWindowInternal> dmWindow;
1181 if (wm)
1182 wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(), getter_AddRefs(dmWindow));
1184 // Show alert.
1185 nsCOMPtr<nsIPromptService> prompter(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
1186 if (prompter)
1187 prompter->Alert(dmWindow, title, aMessage);
1191 return NS_OK;
1194 void nsDownload::DisplayDownloadFinishedAlert()
1196 nsresult rv;
1197 nsCOMPtr<nsIAlertsService> alertsService(do_GetService(NS_ALERTSERVICE_CONTRACTID, &rv));
1198 if (NS_FAILED(rv))
1199 return;
1201 nsCOMPtr<nsIStringBundle> bundle;
1202 nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
1203 if (NS_FAILED(rv))
1204 return;
1206 rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE, getter_AddRefs(bundle));
1207 if (NS_FAILED(rv))
1208 return;
1210 nsXPIDLString finishedTitle, finishedText;
1211 rv = bundle->GetStringFromName(NS_LITERAL_STRING("finishedTitle").get(),
1212 getter_Copies(finishedTitle));
1213 if (NS_FAILED(rv))
1214 return;
1216 const PRUnichar *strings[] = { mDisplayName.get() };
1217 rv = bundle->FormatStringFromName(NS_LITERAL_STRING("finishedText").get(),
1218 strings, 1, getter_Copies(finishedText));
1219 if (NS_FAILED(rv))
1220 return;
1222 nsCAutoString url;
1223 mTarget->GetSpec(url);
1224 alertsService->ShowAlertNotification(NS_LITERAL_STRING("moz-icon://") + NS_ConvertUTF8toUTF16(url),
1225 finishedTitle, finishedText, PR_TRUE,
1226 NS_LITERAL_STRING("download"), this,
1227 EmptyString());
1230 NS_IMETHODIMP
1231 nsDownload::OnStateChange(nsIWebProgress* aWebProgress,
1232 nsIRequest* aRequest, PRUint32 aStateFlags,
1233 nsresult aStatus)
1235 // Record the start time only if it hasn't been set.
1236 if (LL_IS_ZERO(mStartTime) && (aStateFlags & STATE_START))
1237 SetStartTime(PR_Now());
1239 // When we break the ref cycle with mPersist, we don't want to lose
1240 // access to out member vars!
1241 nsRefPtr<nsDownload> kungFuDeathGrip(this);
1243 // We need to update mDownloadState before updating the dialog, because
1244 // that will close and call CancelDownload if it was the last open window.
1245 nsresult rv = NS_OK;
1246 if (aStateFlags & STATE_STOP) {
1247 if (mDownloadState == DOWNLOADING || mDownloadState == NOTSTARTED) {
1248 mDownloadState = FINISHED;
1250 // Set file size at the end of a transfer (for unknown transfer amounts)
1251 if (mMaxBytes == -1)
1252 mMaxBytes = mCurrBytes;
1254 // Files less than 1Kb shouldn't show up as 0Kb.
1255 if (mMaxBytes < 1024) {
1256 mCurrBytes = 1024;
1257 mMaxBytes = 1024;
1260 mPercentComplete = 100;
1262 // Play a sound or show an alert when the download finishes
1263 PRBool playSound = PR_FALSE;
1264 PRBool showAlert = PR_FALSE;
1265 nsXPIDLCString soundStr;
1267 nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1");
1269 if (prefs) {
1270 nsCOMPtr<nsIPrefBranch> prefBranch;
1271 prefs->GetBranch(nsnull, getter_AddRefs(prefBranch));
1272 if (prefBranch) {
1273 rv = prefBranch->GetBoolPref("browser.download.finished_download_sound", &playSound);
1274 if (NS_SUCCEEDED(rv) && playSound)
1275 prefBranch->GetCharPref("browser.download.finished_sound_url", getter_Copies(soundStr));
1276 rv = prefBranch->GetBoolPref("browser.download.finished_download_alert", &showAlert);
1277 if (NS_FAILED(rv))
1278 showAlert = PR_FALSE;
1282 if (!soundStr.IsEmpty()) {
1283 if (!mDownloadManager->mSoundInterface) {
1284 mDownloadManager->mSoundInterface = do_CreateInstance("@mozilla.org/sound;1");
1286 if (mDownloadManager->mSoundInterface) {
1287 nsCOMPtr<nsIURI> soundURI;
1289 NS_NewURI(getter_AddRefs(soundURI), soundStr);
1290 nsCOMPtr<nsIURL> soundURL(do_QueryInterface(soundURI));
1291 if (soundURL)
1292 mDownloadManager->mSoundInterface->Play(soundURL);
1293 else
1294 mDownloadManager->mSoundInterface->Beep();
1297 if (showAlert)
1298 DisplayDownloadFinishedAlert();
1300 nsCAutoString path;
1301 rv = GetFilePathUTF8(mTarget, path);
1302 // can't do an early return; have to break reference cycle below
1303 if (NS_SUCCEEDED(rv)) {
1304 mDownloadManager->DownloadEnded(path, nsnull);
1308 // break the cycle we created in AddDownload
1309 mCancelable = nsnull;
1310 // and the one with the progress dialog
1311 if (mDialog) {
1312 mDialog->SetObserver(nsnull);
1313 mDialog = nsnull;
1317 if (mDownloadManager->MustUpdateUI()) {
1318 nsCOMPtr<nsIDownloadProgressListener> internalListener;
1319 mDownloadManager->GetInternalListener(getter_AddRefs(internalListener));
1320 if (internalListener)
1321 internalListener->OnStateChange(aWebProgress, aRequest, aStateFlags, aStatus, this);
1324 if (mDialogListener) {
1325 mDialogListener->OnStateChange(aWebProgress, aRequest, aStateFlags, aStatus, this);
1326 if (aStateFlags & STATE_STOP) {
1327 // Break this cycle, too
1328 mDialogListener = nsnull;
1332 return rv;
1335 NS_IMETHODIMP
1336 nsDownload::OnSecurityChange(nsIWebProgress *aWebProgress,
1337 nsIRequest *aRequest, PRUint32 aState)
1339 if (mDownloadManager->MustUpdateUI()) {
1340 nsCOMPtr<nsIDownloadProgressListener> internalListener;
1341 mDownloadManager->GetInternalListener(getter_AddRefs(internalListener));
1342 if (internalListener)
1343 internalListener->OnSecurityChange(aWebProgress, aRequest, aState, this);
1346 if (mDialogListener)
1347 mDialogListener->OnSecurityChange(aWebProgress, aRequest, aState, this);
1349 return NS_OK;
1352 ///////////////////////////////////////////////////////////////////////////////
1353 // nsIDownload
1355 NS_IMETHODIMP
1356 nsDownload::Init(nsIURI* aSource,
1357 nsIURI* aTarget,
1358 const nsAString& aDisplayName,
1359 nsIMIMEInfo *aMIMEInfo,
1360 PRTime aStartTime,
1361 nsILocalFile* aTempFile,
1362 nsICancelable* aCancelable)
1364 NS_NOTREACHED("Huh...how did we get here?!");
1365 return NS_OK;
1368 NS_IMETHODIMP
1369 nsDownload::GetDisplayName(nsAString &aDisplayName)
1371 aDisplayName = mDisplayName;
1372 return NS_OK;
1375 NS_IMETHODIMP
1376 nsDownload::GetCancelable(nsICancelable** aCancelable)
1378 *aCancelable = mCancelable;
1379 NS_IF_ADDREF(*aCancelable);
1380 return NS_OK;
1383 NS_IMETHODIMP
1384 nsDownload::GetTarget(nsIURI** aTarget)
1386 *aTarget = mTarget;
1387 NS_IF_ADDREF(*aTarget);
1388 return NS_OK;
1391 NS_IMETHODIMP
1392 nsDownload::GetSource(nsIURI** aSource)
1394 *aSource = mSource;
1395 NS_IF_ADDREF(*aSource);
1396 return NS_OK;
1399 NS_IMETHODIMP
1400 nsDownload::GetStartTime(PRInt64* aStartTime)
1402 *aStartTime = mStartTime;
1403 return NS_OK;
1406 NS_IMETHODIMP
1407 nsDownload::GetPercentComplete(PRInt32* aPercentComplete)
1409 *aPercentComplete = mPercentComplete;
1410 return NS_OK;
1413 NS_IMETHODIMP
1414 nsDownload::GetAmountTransferred(PRUint64* aAmountTransferred)
1416 *aAmountTransferred = mCurrBytes;
1417 return NS_OK;
1420 NS_IMETHODIMP
1421 nsDownload::GetSize(PRUint64* aSize)
1423 *aSize = mMaxBytes;
1424 return NS_OK;
1427 NS_IMETHODIMP
1428 nsDownload::GetMIMEInfo(nsIMIMEInfo** aMIMEInfo)
1430 *aMIMEInfo = mMIMEInfo;
1431 NS_IF_ADDREF(*aMIMEInfo);
1432 return NS_OK;
1435 NS_IMETHODIMP
1436 nsDownload::GetTargetFile(nsILocalFile** aTargetFile)
1438 nsresult rv;
1440 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget, &rv);
1441 if (NS_FAILED(rv)) return rv;
1443 nsCOMPtr<nsIFile> file;
1444 rv = fileURL->GetFile(getter_AddRefs(file));
1445 if (NS_SUCCEEDED(rv))
1446 rv = CallQueryInterface(file, aTargetFile);
1447 return rv;
1450 NS_IMETHODIMP
1451 nsDownload::GetSpeed(double* aSpeed)
1453 *aSpeed = mSpeed;
1454 return NS_OK;
1457 NS_IMETHODIMP
1458 nsDownload::GetId(PRUint32 *aId)
1460 return NS_ERROR_NOT_IMPLEMENTED;
1463 NS_IMETHODIMP
1464 nsDownload::GetState(PRInt16 *aState)
1466 *aState = mDownloadState;
1467 return NS_OK;
1470 nsresult
1471 nsDownload::SetTempFile(nsILocalFile* aTempFile)
1473 mTempFile = aTempFile;
1474 return NS_OK;