1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Blake Ross <blaker@netscape.com> (Original Author)
24 : * Ben Goodger <ben@netscape.com> (Original Author)
25 : * Shawn Wilsher <me@shawnwilsher.com>
26 : * Srirang G Doddihal <brahmana@doddihal.com>
27 : * Edward Lee <edward.lee@engineering.uiuc.edu>
28 : * Graeme McCutcheon <graememcc_firefox@graeme-online.co.uk>
29 : * Ehsan Akhgari <ehsan.akhgari@gmail.com>
30 : *
31 : * Alternatively, the contents of this file may be used under the terms of
32 : * either the GNU General Public License Version 2 or later (the "GPL"), or
33 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
34 : * in which case the provisions of the GPL or the LGPL are applicable instead
35 : * of those above. If you wish to allow use of your version of this file only
36 : * under the terms of either the GPL or the LGPL, and not to allow others to
37 : * use your version of this file under the terms of the MPL, indicate your
38 : * decision by deleting the provisions above and replace them with the notice
39 : * and other provisions required by the GPL or the LGPL. If you do not delete
40 : * the provisions above, a recipient may use your version of this file under
41 : * the terms of any one of the MPL, the GPL or the LGPL.
42 : *
43 : * ***** END LICENSE BLOCK ***** */
44 :
45 : #include "mozilla/Util.h"
46 :
47 : #include "mozIStorageService.h"
48 : #include "nsIAlertsService.h"
49 : #include "nsIClassInfoImpl.h"
50 : #include "nsIDOMWindow.h"
51 : #include "nsIDownloadHistory.h"
52 : #include "nsIDownloadManagerUI.h"
53 : #include "nsIMIMEService.h"
54 : #include "nsIParentalControlsService.h"
55 : #include "nsIPrefService.h"
56 : #include "nsIPrivateBrowsingService.h"
57 : #include "nsIPromptService.h"
58 : #include "nsIResumableChannel.h"
59 : #include "nsIWebBrowserPersist.h"
60 : #include "nsIWindowMediator.h"
61 : #include "nsILocalFileWin.h"
62 :
63 : #include "nsAppDirectoryServiceDefs.h"
64 : #include "nsArrayEnumerator.h"
65 : #include "nsCExternalHandlerService.h"
66 : #include "nsDirectoryServiceDefs.h"
67 : #include "nsDownloadManager.h"
68 : #include "nsNetUtil.h"
69 :
70 : #include "mozStorageCID.h"
71 : #include "nsDocShellCID.h"
72 : #include "nsEmbedCID.h"
73 : #include "nsToolkitCompsCID.h"
74 :
75 : #ifdef XP_WIN
76 : #include <shlobj.h>
77 : #ifdef DOWNLOAD_SCANNER
78 : #include "nsDownloadScanner.h"
79 : #endif
80 : #endif
81 :
82 : #ifdef XP_MACOSX
83 : #include <CoreFoundation/CoreFoundation.h>
84 : #endif
85 :
86 : #ifdef MOZ_WIDGET_ANDROID
87 : #include "AndroidBridge.h"
88 : #endif
89 :
90 : using namespace mozilla;
91 :
92 : #define DOWNLOAD_MANAGER_BUNDLE "chrome://mozapps/locale/downloads/downloads.properties"
93 : #define DOWNLOAD_MANAGER_ALERT_ICON "chrome://mozapps/skin/downloads/downloadIcon.png"
94 : #define PREF_BDM_SHOWALERTONCOMPLETE "browser.download.manager.showAlertOnComplete"
95 : #define PREF_BDM_SHOWALERTINTERVAL "browser.download.manager.showAlertInterval"
96 : #define PREF_BDM_RETENTION "browser.download.manager.retention"
97 : #define PREF_BDM_QUITBEHAVIOR "browser.download.manager.quitBehavior"
98 : #define PREF_BDM_ADDTORECENTDOCS "browser.download.manager.addToRecentDocs"
99 : #define PREF_BDM_SCANWHENDONE "browser.download.manager.scanWhenDone"
100 : #define PREF_BDM_RESUMEONWAKEDELAY "browser.download.manager.resumeOnWakeDelay"
101 : #define PREF_BH_DELETETEMPFILEONEXIT "browser.helperApps.deleteTempFileOnExit"
102 :
103 : static const PRInt64 gUpdateInterval = 400 * PR_USEC_PER_MSEC;
104 :
105 : #define DM_SCHEMA_VERSION 8
106 : #define DM_DB_NAME NS_LITERAL_STRING("downloads.sqlite")
107 : #define DM_DB_CORRUPT_FILENAME NS_LITERAL_STRING("downloads.sqlite.corrupt")
108 :
109 : #define NS_SYSTEMINFO_CONTRACTID "@mozilla.org/system-info;1"
110 :
111 : ////////////////////////////////////////////////////////////////////////////////
112 : //// nsDownloadManager
113 :
114 4896 : NS_IMPL_ISUPPORTS4(
115 : nsDownloadManager
116 : , nsIDownloadManager
117 : , nsINavHistoryObserver
118 : , nsIObserver
119 : , nsISupportsWeakReference
120 : )
121 :
122 : nsDownloadManager *nsDownloadManager::gDownloadManagerService = nsnull;
123 :
124 : nsDownloadManager *
125 35 : nsDownloadManager::GetSingleton()
126 : {
127 35 : if (gDownloadManagerService) {
128 1 : NS_ADDREF(gDownloadManagerService);
129 1 : return gDownloadManagerService;
130 : }
131 :
132 34 : gDownloadManagerService = new nsDownloadManager();
133 34 : if (gDownloadManagerService) {
134 34 : NS_ADDREF(gDownloadManagerService);
135 34 : if (NS_FAILED(gDownloadManagerService->Init()))
136 0 : NS_RELEASE(gDownloadManagerService);
137 : }
138 :
139 34 : return gDownloadManagerService;
140 : }
141 :
142 81 : nsDownloadManager::~nsDownloadManager()
143 : {
144 : #ifdef DOWNLOAD_SCANNER
145 : if (mScanner) {
146 : delete mScanner;
147 : mScanner = nsnull;
148 : }
149 : #endif
150 27 : gDownloadManagerService = nsnull;
151 108 : }
152 :
153 : nsresult
154 3 : nsDownloadManager::ResumeRetry(nsDownload *aDl)
155 : {
156 : // Keep a reference in case we need to cancel the download
157 6 : nsRefPtr<nsDownload> dl = aDl;
158 :
159 : // Try to resume the active download
160 3 : nsresult rv = dl->Resume();
161 :
162 : // If not, try to retry the download
163 3 : if (NS_FAILED(rv)) {
164 : // First cancel the download so it's no longer active
165 0 : rv = CancelDownload(dl->mID);
166 :
167 : // Then retry it
168 0 : if (NS_SUCCEEDED(rv))
169 0 : rv = RetryDownload(dl->mID);
170 : }
171 :
172 3 : return rv;
173 : }
174 :
175 : nsresult
176 87 : nsDownloadManager::PauseAllDownloads(bool aSetResume)
177 : {
178 87 : nsresult retVal = NS_OK;
179 96 : for (PRInt32 i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
180 18 : nsRefPtr<nsDownload> dl = mCurrentDownloads[i];
181 :
182 : // Only pause things that need to be paused
183 9 : if (!dl->IsPaused()) {
184 : // Set auto-resume before pausing so that it gets into the DB
185 6 : dl->mAutoResume = aSetResume ? nsDownload::AUTO_RESUME :
186 6 : nsDownload::DONT_RESUME;
187 :
188 : // Try to pause the download but don't bail now if we fail
189 6 : nsresult rv = dl->Pause();
190 6 : if (NS_FAILED(rv))
191 2 : retVal = rv;
192 : }
193 : }
194 :
195 87 : return retVal;
196 : }
197 :
198 : nsresult
199 53 : nsDownloadManager::ResumeAllDownloads(bool aResumeAll)
200 : {
201 53 : nsresult retVal = NS_OK;
202 63 : for (PRInt32 i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
203 20 : nsRefPtr<nsDownload> dl = mCurrentDownloads[i];
204 :
205 : // If aResumeAll is true, then resume everything; otherwise, check if the
206 : // download should auto-resume
207 10 : if (aResumeAll || dl->ShouldAutoResume()) {
208 : // Reset auto-resume before retrying so that it gets into the DB through
209 : // ResumeRetry's eventual call to SetState. We clear the value now so we
210 : // don't accidentally query completed downloads that were previously
211 : // auto-resumed (and try to resume them).
212 3 : dl->mAutoResume = nsDownload::DONT_RESUME;
213 :
214 : // Try to resume/retry the download but don't bail now if we fail
215 3 : nsresult rv = ResumeRetry(dl);
216 3 : if (NS_FAILED(rv))
217 0 : retVal = rv;
218 : }
219 : }
220 :
221 53 : return retVal;
222 : }
223 :
224 : nsresult
225 46 : nsDownloadManager::RemoveAllDownloads()
226 : {
227 46 : nsresult rv = NS_OK;
228 51 : for (PRInt32 i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
229 10 : nsRefPtr<nsDownload> dl = mCurrentDownloads[0];
230 :
231 : nsresult result;
232 5 : if (dl->IsPaused() && GetQuitBehavior() != QUIT_AND_CANCEL)
233 4 : result = mCurrentDownloads.RemoveObject(dl);
234 : else
235 1 : result = CancelDownload(dl->mID);
236 :
237 : // Track the failure, but don't miss out on other downloads
238 5 : if (NS_FAILED(result))
239 0 : rv = result;
240 : }
241 :
242 46 : return rv;
243 : }
244 :
245 : nsresult
246 3 : nsDownloadManager::RemoveDownloadsForURI(nsIURI *aURI)
247 : {
248 6 : mozStorageStatementScoper scope(mGetIdsForURIStatement);
249 :
250 6 : nsCAutoString source;
251 3 : nsresult rv = aURI->GetSpec(source);
252 3 : NS_ENSURE_SUCCESS(rv, rv);
253 :
254 3 : rv = mGetIdsForURIStatement->BindUTF8StringByName(
255 3 : NS_LITERAL_CSTRING("source"), source);
256 3 : NS_ENSURE_SUCCESS(rv, rv);
257 :
258 3 : bool hasMore = false;
259 6 : nsAutoTArray<PRInt64, 4> downloads;
260 : // Get all the downloads that match the provided URI
261 7 : while (NS_SUCCEEDED(mGetIdsForURIStatement->ExecuteStep(&hasMore)) &&
262 : hasMore) {
263 : PRInt64 downloadId;
264 1 : rv = mGetIdsForURIStatement->GetInt64(0, &downloadId);
265 1 : NS_ENSURE_SUCCESS(rv, rv);
266 :
267 1 : downloads.AppendElement(downloadId);
268 : }
269 :
270 : // Remove each download ignoring any failure so we reach other downloads
271 7 : for (PRInt32 i = downloads.Length(); --i >= 0; )
272 1 : (void)RemoveDownload(downloads[i]);
273 :
274 3 : return NS_OK;
275 : }
276 :
277 : void // static
278 1 : nsDownloadManager::ResumeOnWakeCallback(nsITimer *aTimer, void *aClosure)
279 : {
280 : // Resume the downloads that were set to autoResume
281 1 : nsDownloadManager *dlMgr = static_cast<nsDownloadManager *>(aClosure);
282 1 : (void)dlMgr->ResumeAllDownloads(false);
283 1 : }
284 :
285 : already_AddRefed<mozIStorageConnection>
286 40 : nsDownloadManager::GetFileDBConnection(nsIFile *dbFile) const
287 : {
288 40 : NS_ASSERTION(dbFile, "GetFileDBConnection called with an invalid nsIFile");
289 :
290 : nsCOMPtr<mozIStorageService> storage =
291 80 : do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
292 40 : NS_ENSURE_TRUE(storage, nsnull);
293 :
294 80 : nsCOMPtr<mozIStorageConnection> conn;
295 40 : nsresult rv = storage->OpenDatabase(dbFile, getter_AddRefs(conn));
296 40 : if (rv == NS_ERROR_FILE_CORRUPTED) {
297 : // delete and try again, since we don't care so much about losing a user's
298 : // download history
299 0 : rv = dbFile->Remove(false);
300 0 : NS_ENSURE_SUCCESS(rv, nsnull);
301 0 : rv = storage->OpenDatabase(dbFile, getter_AddRefs(conn));
302 : }
303 40 : NS_ENSURE_SUCCESS(rv, nsnull);
304 :
305 40 : return conn.forget();
306 : }
307 :
308 : already_AddRefed<mozIStorageConnection>
309 6 : nsDownloadManager::GetMemoryDBConnection() const
310 : {
311 : nsCOMPtr<mozIStorageService> storage =
312 12 : do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
313 6 : NS_ENSURE_TRUE(storage, nsnull);
314 :
315 12 : nsCOMPtr<mozIStorageConnection> conn;
316 6 : nsresult rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(conn));
317 6 : NS_ENSURE_SUCCESS(rv, nsnull);
318 :
319 6 : return conn.forget();
320 : }
321 :
322 : nsresult
323 6 : nsDownloadManager::InitMemoryDB()
324 : {
325 6 : mDBConn = GetMemoryDBConnection();
326 6 : if (!mDBConn)
327 0 : return NS_ERROR_NOT_AVAILABLE;
328 :
329 6 : nsresult rv = CreateTable();
330 6 : NS_ENSURE_SUCCESS(rv, rv);
331 :
332 6 : mDBType = DATABASE_MEMORY;
333 6 : return NS_OK;
334 : }
335 :
336 : nsresult
337 40 : nsDownloadManager::InitFileDB()
338 : {
339 : nsresult rv;
340 :
341 80 : nsCOMPtr<nsIFile> dbFile;
342 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
343 40 : getter_AddRefs(dbFile));
344 40 : NS_ENSURE_SUCCESS(rv, rv);
345 40 : rv = dbFile->Append(DM_DB_NAME);
346 40 : NS_ENSURE_SUCCESS(rv, rv);
347 :
348 40 : mDBConn = GetFileDBConnection(dbFile);
349 40 : NS_ENSURE_TRUE(mDBConn, NS_ERROR_NOT_AVAILABLE);
350 :
351 : bool tableExists;
352 40 : rv = mDBConn->TableExists(NS_LITERAL_CSTRING("moz_downloads"), &tableExists);
353 40 : NS_ENSURE_SUCCESS(rv, rv);
354 40 : if (!tableExists) {
355 23 : rv = CreateTable();
356 23 : NS_ENSURE_SUCCESS(rv, rv);
357 23 : mDBType = DATABASE_DISK;
358 23 : return NS_OK;
359 : }
360 :
361 17 : mDBType = DATABASE_DISK;
362 :
363 : // Checking the database schema now
364 : PRInt32 schemaVersion;
365 17 : rv = mDBConn->GetSchemaVersion(&schemaVersion);
366 17 : NS_ENSURE_SUCCESS(rv, rv);
367 :
368 : // Changing the database? Be sure to do these two things!
369 : // 1) Increment DM_SCHEMA_VERSION
370 : // 2) Implement the proper downgrade/upgrade code for the current version
371 :
372 17 : switch (schemaVersion) {
373 : // Upgrading
374 : // Every time you increment the database schema, you need to implement
375 : // the upgrading code from the previous version to the new one.
376 : // Also, don't forget to make a unit test to test your upgrading code!
377 : case 1: // Drop a column (iconURL) from the database (bug 385875)
378 : {
379 : // Safely wrap this in a transaction so we don't hose the whole DB
380 2 : mozStorageTransaction safeTransaction(mDBConn, true);
381 :
382 : // Create a temporary table that will store the existing records
383 2 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
384 : "CREATE TEMPORARY TABLE moz_downloads_backup ("
385 : "id INTEGER PRIMARY KEY, "
386 : "name TEXT, "
387 : "source TEXT, "
388 : "target TEXT, "
389 : "startTime INTEGER, "
390 : "endTime INTEGER, "
391 : "state INTEGER"
392 1 : ")"));
393 1 : NS_ENSURE_SUCCESS(rv, rv);
394 :
395 : // Insert into a temporary table
396 2 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
397 : "INSERT INTO moz_downloads_backup "
398 : "SELECT id, name, source, target, startTime, endTime, state "
399 1 : "FROM moz_downloads"));
400 1 : NS_ENSURE_SUCCESS(rv, rv);
401 :
402 : // Drop the old table
403 2 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
404 1 : "DROP TABLE moz_downloads"));
405 1 : NS_ENSURE_SUCCESS(rv, rv);
406 :
407 : // Now recreate it with this schema version
408 2 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
409 : "CREATE TABLE moz_downloads ("
410 : "id INTEGER PRIMARY KEY, "
411 : "name TEXT, "
412 : "source TEXT, "
413 : "target TEXT, "
414 : "startTime INTEGER, "
415 : "endTime INTEGER, "
416 : "state INTEGER"
417 1 : ")"));
418 1 : NS_ENSURE_SUCCESS(rv, rv);
419 :
420 : // Insert the data back into it
421 2 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
422 : "INSERT INTO moz_downloads "
423 : "SELECT id, name, source, target, startTime, endTime, state "
424 1 : "FROM moz_downloads_backup"));
425 1 : NS_ENSURE_SUCCESS(rv, rv);
426 :
427 : // And drop our temporary table
428 2 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
429 1 : "DROP TABLE moz_downloads_backup"));
430 1 : NS_ENSURE_SUCCESS(rv, rv);
431 :
432 : // Finally, update the schemaVersion variable and the database schema
433 1 : schemaVersion = 2;
434 1 : rv = mDBConn->SetSchemaVersion(schemaVersion);
435 1 : NS_ENSURE_SUCCESS(rv, rv);
436 : }
437 : // Fallthrough to the next upgrade
438 :
439 : case 2: // Add referrer column to the database
440 : {
441 4 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
442 : "ALTER TABLE moz_downloads "
443 2 : "ADD COLUMN referrer TEXT"));
444 2 : NS_ENSURE_SUCCESS(rv, rv);
445 :
446 : // Finally, update the schemaVersion variable and the database schema
447 2 : schemaVersion = 3;
448 2 : rv = mDBConn->SetSchemaVersion(schemaVersion);
449 2 : NS_ENSURE_SUCCESS(rv, rv);
450 : }
451 : // Fallthrough to the next upgrade
452 :
453 : case 3: // This version adds a column to the database (entityID)
454 : {
455 6 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
456 : "ALTER TABLE moz_downloads "
457 3 : "ADD COLUMN entityID TEXT"));
458 3 : NS_ENSURE_SUCCESS(rv, rv);
459 :
460 : // Finally, update the schemaVersion variable and the database schema
461 3 : schemaVersion = 4;
462 3 : rv = mDBConn->SetSchemaVersion(schemaVersion);
463 3 : NS_ENSURE_SUCCESS(rv, rv);
464 : }
465 : // Fallthrough to the next upgrade
466 :
467 : case 4: // This version adds a column to the database (tempPath)
468 : {
469 6 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
470 : "ALTER TABLE moz_downloads "
471 3 : "ADD COLUMN tempPath TEXT"));
472 3 : NS_ENSURE_SUCCESS(rv, rv);
473 :
474 : // Finally, update the schemaVersion variable and the database schema
475 3 : schemaVersion = 5;
476 3 : rv = mDBConn->SetSchemaVersion(schemaVersion);
477 3 : NS_ENSURE_SUCCESS(rv, rv);
478 : }
479 : // Fallthrough to the next upgrade
480 :
481 : case 5: // This version adds two columns for tracking transfer progress
482 : {
483 8 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
484 : "ALTER TABLE moz_downloads "
485 4 : "ADD COLUMN currBytes INTEGER NOT NULL DEFAULT 0"));
486 4 : NS_ENSURE_SUCCESS(rv, rv);
487 :
488 8 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
489 : "ALTER TABLE moz_downloads "
490 4 : "ADD COLUMN maxBytes INTEGER NOT NULL DEFAULT -1"));
491 4 : NS_ENSURE_SUCCESS(rv, rv);
492 :
493 : // Finally, update the schemaVersion variable and the database schema
494 4 : schemaVersion = 6;
495 4 : rv = mDBConn->SetSchemaVersion(schemaVersion);
496 4 : NS_ENSURE_SUCCESS(rv, rv);
497 : }
498 : // Fallthrough to the next upgrade
499 :
500 : case 6: // This version adds three columns to DB (MIME type related info)
501 : {
502 12 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
503 : "ALTER TABLE moz_downloads "
504 6 : "ADD COLUMN mimeType TEXT"));
505 6 : NS_ENSURE_SUCCESS(rv, rv);
506 :
507 12 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
508 : "ALTER TABLE moz_downloads "
509 6 : "ADD COLUMN preferredApplication TEXT"));
510 6 : NS_ENSURE_SUCCESS(rv, rv);
511 :
512 12 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
513 : "ALTER TABLE moz_downloads "
514 6 : "ADD COLUMN preferredAction INTEGER NOT NULL DEFAULT 0"));
515 6 : NS_ENSURE_SUCCESS(rv, rv);
516 :
517 : // Finally, update the schemaVersion variable and the database schema
518 6 : schemaVersion = 7;
519 6 : rv = mDBConn->SetSchemaVersion(schemaVersion);
520 6 : NS_ENSURE_SUCCESS(rv, rv);
521 : }
522 : // Fallthrough to next upgrade
523 :
524 : case 7: // This version adds a column to remember to auto-resume downloads
525 : {
526 14 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
527 : "ALTER TABLE moz_downloads "
528 7 : "ADD COLUMN autoResume INTEGER NOT NULL DEFAULT 0"));
529 7 : NS_ENSURE_SUCCESS(rv, rv);
530 :
531 : // Finally, update the schemaVersion variable and the database schema
532 7 : schemaVersion = 8;
533 7 : rv = mDBConn->SetSchemaVersion(schemaVersion);
534 7 : NS_ENSURE_SUCCESS(rv, rv);
535 : }
536 : // Fallthrough to the next upgrade
537 :
538 : // Extra sanity checking for developers
539 : #ifndef DEBUG
540 : case DM_SCHEMA_VERSION:
541 : #endif
542 7 : break;
543 :
544 : case 0:
545 : {
546 0 : NS_WARNING("Could not get download database's schema version!");
547 :
548 : // The table may still be usable - someone may have just messed with the
549 : // schema version, so let's just treat this like a downgrade and verify
550 : // that the needed columns are there. If they aren't there, we'll drop
551 : // the table anyway.
552 0 : rv = mDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
553 0 : NS_ENSURE_SUCCESS(rv, rv);
554 : }
555 : // Fallthrough to downgrade check
556 :
557 : // Downgrading
558 : // If columns have been added to the table, we can still use the ones we
559 : // understand safely. If columns have been deleted or alterd, we just
560 : // drop the table and start from scratch. If you change how a column
561 : // should be interpreted, make sure you also change its name so this
562 : // check will catch it.
563 : default:
564 : {
565 20 : nsCOMPtr<mozIStorageStatement> stmt;
566 20 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
567 : "SELECT id, name, source, target, tempPath, startTime, endTime, state, "
568 : "referrer, entityID, currBytes, maxBytes, mimeType, "
569 : "preferredApplication, preferredAction, autoResume "
570 20 : "FROM moz_downloads"), getter_AddRefs(stmt));
571 10 : if (NS_SUCCEEDED(rv))
572 : break;
573 :
574 : // if the statement fails, that means all the columns were not there.
575 : // First we backup the database
576 : nsCOMPtr<mozIStorageService> storage =
577 0 : do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
578 0 : NS_ENSURE_TRUE(storage, NS_ERROR_NOT_AVAILABLE);
579 0 : nsCOMPtr<nsIFile> backup;
580 0 : rv = storage->BackupDatabaseFile(dbFile, DM_DB_CORRUPT_FILENAME, nsnull,
581 0 : getter_AddRefs(backup));
582 0 : NS_ENSURE_SUCCESS(rv, rv);
583 :
584 : // Then we dump it
585 0 : rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
586 0 : "DROP TABLE moz_downloads"));
587 0 : NS_ENSURE_SUCCESS(rv, rv);
588 :
589 0 : rv = CreateTable();
590 0 : NS_ENSURE_SUCCESS(rv, rv);
591 : }
592 0 : break;
593 : }
594 :
595 17 : return NS_OK;
596 : }
597 :
598 : nsresult
599 29 : nsDownloadManager::CreateTable()
600 : {
601 29 : nsresult rv = mDBConn->SetSchemaVersion(DM_SCHEMA_VERSION);
602 29 : if (NS_FAILED(rv)) return rv;
603 :
604 58 : return mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
605 : "CREATE TABLE moz_downloads ("
606 : "id INTEGER PRIMARY KEY, "
607 : "name TEXT, "
608 : "source TEXT, "
609 : "target TEXT, "
610 : "tempPath TEXT, "
611 : "startTime INTEGER, "
612 : "endTime INTEGER, "
613 : "state INTEGER, "
614 : "referrer TEXT, "
615 : "entityID TEXT, "
616 : "currBytes INTEGER NOT NULL DEFAULT 0, "
617 : "maxBytes INTEGER NOT NULL DEFAULT -1, "
618 : "mimeType TEXT, "
619 : "preferredApplication TEXT, "
620 : "preferredAction INTEGER NOT NULL DEFAULT 0, "
621 : "autoResume INTEGER NOT NULL DEFAULT 0"
622 29 : ")"));
623 : }
624 :
625 : nsresult
626 46 : nsDownloadManager::RestoreDatabaseState()
627 : {
628 : // Restore downloads that were in a scanning state. We can assume that they
629 : // have been dealt with by the virus scanner
630 92 : nsCOMPtr<mozIStorageStatement> stmt;
631 92 : nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
632 : "UPDATE moz_downloads "
633 : "SET state = :state "
634 92 : "WHERE state = :state_cond"), getter_AddRefs(stmt));
635 46 : NS_ENSURE_SUCCESS(rv, rv);
636 :
637 46 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED);
638 46 : NS_ENSURE_SUCCESS(rv, rv);
639 46 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state_cond"), nsIDownloadManager::DOWNLOAD_SCANNING);
640 46 : NS_ENSURE_SUCCESS(rv, rv);
641 :
642 46 : rv = stmt->Execute();
643 46 : NS_ENSURE_SUCCESS(rv, rv);
644 :
645 : // Convert supposedly-active downloads into downloads that should auto-resume
646 92 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
647 : "UPDATE moz_downloads "
648 : "SET autoResume = :autoResume "
649 : "WHERE state = :notStarted "
650 : "OR state = :queued "
651 92 : "OR state = :downloading"), getter_AddRefs(stmt));
652 46 : NS_ENSURE_SUCCESS(rv, rv);
653 :
654 46 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::AUTO_RESUME);
655 46 : NS_ENSURE_SUCCESS(rv, rv);
656 46 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("notStarted"), nsIDownloadManager::DOWNLOAD_NOTSTARTED);
657 46 : NS_ENSURE_SUCCESS(rv, rv);
658 46 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("queued"), nsIDownloadManager::DOWNLOAD_QUEUED);
659 46 : NS_ENSURE_SUCCESS(rv, rv);
660 46 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("downloading"), nsIDownloadManager::DOWNLOAD_DOWNLOADING);
661 46 : NS_ENSURE_SUCCESS(rv, rv);
662 :
663 46 : rv = stmt->Execute();
664 46 : NS_ENSURE_SUCCESS(rv, rv);
665 :
666 : // Switch any download that is supposed to automatically resume and is in a
667 : // finished state to *not* automatically resume. See Bug 409179 for details.
668 92 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
669 : "UPDATE moz_downloads "
670 : "SET autoResume = :autoResume "
671 : "WHERE state = :state "
672 : "AND autoResume = :autoResume_cond"),
673 92 : getter_AddRefs(stmt));
674 46 : NS_ENSURE_SUCCESS(rv, rv);
675 :
676 46 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::DONT_RESUME);
677 46 : NS_ENSURE_SUCCESS(rv, rv);
678 46 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_FINISHED);
679 46 : NS_ENSURE_SUCCESS(rv, rv);
680 46 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume_cond"), nsDownload::AUTO_RESUME);
681 46 : NS_ENSURE_SUCCESS(rv, rv);
682 :
683 46 : rv = stmt->Execute();
684 46 : NS_ENSURE_SUCCESS(rv, rv);
685 :
686 46 : return NS_OK;
687 : }
688 :
689 : nsresult
690 46 : nsDownloadManager::RestoreActiveDownloads()
691 : {
692 92 : nsCOMPtr<mozIStorageStatement> stmt;
693 92 : nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
694 : "SELECT id "
695 : "FROM moz_downloads "
696 : "WHERE (state = :state AND LENGTH(entityID) > 0) "
697 92 : "OR autoResume != :autoResume"), getter_AddRefs(stmt));
698 46 : NS_ENSURE_SUCCESS(rv, rv);
699 :
700 46 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_PAUSED);
701 46 : NS_ENSURE_SUCCESS(rv, rv);
702 46 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), nsDownload::DONT_RESUME);
703 46 : NS_ENSURE_SUCCESS(rv, rv);
704 :
705 46 : nsresult retVal = NS_OK;
706 : bool hasResults;
707 99 : while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResults)) && hasResults) {
708 14 : nsRefPtr<nsDownload> dl;
709 : // Keep trying to add even if we fail one, but make sure to return failure.
710 : // Additionally, be careful to not call anything that tries to change the
711 : // database because we're iterating over a live statement.
712 14 : if (NS_FAILED(GetDownloadFromDB(stmt->AsInt32(0), getter_AddRefs(dl))) ||
713 7 : NS_FAILED(AddToCurrentDownloads(dl)))
714 0 : retVal = NS_ERROR_FAILURE;
715 : }
716 :
717 : // Try to resume only the downloads that should auto-resume
718 46 : rv = ResumeAllDownloads(false);
719 46 : NS_ENSURE_SUCCESS(rv, rv);
720 :
721 46 : return retVal;
722 : }
723 :
724 : PRInt64
725 20 : nsDownloadManager::AddDownloadToDB(const nsAString &aName,
726 : const nsACString &aSource,
727 : const nsACString &aTarget,
728 : const nsAString &aTempPath,
729 : PRInt64 aStartTime,
730 : PRInt64 aEndTime,
731 : const nsACString &aMimeType,
732 : const nsACString &aPreferredApp,
733 : nsHandlerInfoAction aPreferredAction)
734 : {
735 40 : nsCOMPtr<mozIStorageStatement> stmt;
736 40 : nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
737 : "INSERT INTO moz_downloads "
738 : "(name, source, target, tempPath, startTime, endTime, state, "
739 : "mimeType, preferredApplication, preferredAction) VALUES "
740 : "(:name, :source, :target, :tempPath, :startTime, :endTime, :state, "
741 : ":mimeType, :preferredApplication, :preferredAction)"),
742 40 : getter_AddRefs(stmt));
743 20 : NS_ENSURE_SUCCESS(rv, 0);
744 :
745 20 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
746 20 : NS_ENSURE_SUCCESS(rv, 0);
747 :
748 20 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("source"), aSource);
749 20 : NS_ENSURE_SUCCESS(rv, 0);
750 :
751 20 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("target"), aTarget);
752 20 : NS_ENSURE_SUCCESS(rv, 0);
753 :
754 20 : rv = stmt->BindStringByName(NS_LITERAL_CSTRING("tempPath"), aTempPath);
755 20 : NS_ENSURE_SUCCESS(rv, 0);
756 :
757 20 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), aStartTime);
758 20 : NS_ENSURE_SUCCESS(rv, 0);
759 :
760 20 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), aEndTime);
761 20 : NS_ENSURE_SUCCESS(rv, 0);
762 :
763 20 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), nsIDownloadManager::DOWNLOAD_NOTSTARTED);
764 20 : NS_ENSURE_SUCCESS(rv, 0);
765 :
766 20 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mimeType"), aMimeType);
767 20 : NS_ENSURE_SUCCESS(rv, 0);
768 :
769 20 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("preferredApplication"), aPreferredApp);
770 20 : NS_ENSURE_SUCCESS(rv, 0);
771 :
772 20 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("preferredAction"), aPreferredAction);
773 20 : NS_ENSURE_SUCCESS(rv, 0);
774 :
775 : bool hasMore;
776 20 : rv = stmt->ExecuteStep(&hasMore); // we want to keep our lock
777 20 : NS_ENSURE_SUCCESS(rv, 0);
778 :
779 20 : PRInt64 id = 0;
780 20 : rv = mDBConn->GetLastInsertRowID(&id);
781 20 : NS_ENSURE_SUCCESS(rv, 0);
782 :
783 : // lock on DB from statement will be released once we return
784 20 : return id;
785 : }
786 :
787 : nsresult
788 46 : nsDownloadManager::InitDB()
789 : {
790 46 : nsresult rv = NS_OK;
791 :
792 46 : switch (mDBType) {
793 : case DATABASE_MEMORY:
794 6 : rv = InitMemoryDB();
795 6 : break;
796 :
797 : case DATABASE_DISK:
798 40 : rv = InitFileDB();
799 40 : break;
800 :
801 : default:
802 0 : NS_ERROR("Unexpected value encountered for nsDownloadManager::mDBType");
803 0 : break;
804 : }
805 46 : NS_ENSURE_SUCCESS(rv, rv);
806 :
807 92 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
808 : "UPDATE moz_downloads "
809 : "SET tempPath = :tempPath, startTime = :startTime, endTime = :endTime, "
810 : "state = :state, referrer = :referrer, entityID = :entityID, "
811 : "currBytes = :currBytes, maxBytes = :maxBytes, autoResume = :autoResume "
812 92 : "WHERE id = :id"), getter_AddRefs(mUpdateDownloadStatement));
813 46 : NS_ENSURE_SUCCESS(rv, rv);
814 :
815 92 : rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
816 : "SELECT id "
817 : "FROM moz_downloads "
818 92 : "WHERE source = :source"), getter_AddRefs(mGetIdsForURIStatement));
819 46 : NS_ENSURE_SUCCESS(rv, rv);
820 :
821 46 : return rv;
822 : }
823 :
824 : nsresult
825 34 : nsDownloadManager::Init()
826 : {
827 : // Clean up any old downloads.rdf files from before Firefox 3
828 : {
829 68 : nsCOMPtr<nsIFile> oldDownloadsFile;
830 : bool fileExists;
831 54 : if (NS_SUCCEEDED(NS_GetSpecialDirectory(NS_APP_DOWNLOADS_50_FILE,
832 : getter_AddRefs(oldDownloadsFile))) &&
833 20 : NS_SUCCEEDED(oldDownloadsFile->Exists(&fileExists)) &&
834 : fileExists) {
835 1 : (void)oldDownloadsFile->Remove(false);
836 : }
837 : }
838 :
839 34 : mObserverService = mozilla::services::GetObserverService();
840 34 : if (!mObserverService)
841 0 : return NS_ERROR_FAILURE;
842 :
843 : nsCOMPtr<nsIStringBundleService> bundleService =
844 68 : mozilla::services::GetStringBundleService();
845 34 : if (!bundleService)
846 0 : return NS_ERROR_FAILURE;
847 :
848 34 : nsresult rv = InitDB();
849 34 : NS_ENSURE_SUCCESS(rv, rv);
850 :
851 34 : rv = bundleService->CreateBundle(DOWNLOAD_MANAGER_BUNDLE,
852 34 : getter_AddRefs(mBundle));
853 34 : NS_ENSURE_SUCCESS(rv, rv);
854 :
855 : #ifdef DOWNLOAD_SCANNER
856 : mScanner = new nsDownloadScanner();
857 : if (!mScanner)
858 : return NS_ERROR_OUT_OF_MEMORY;
859 : rv = mScanner->Init();
860 : if (NS_FAILED(rv)) {
861 : delete mScanner;
862 : mScanner = nsnull;
863 : }
864 : #endif
865 :
866 : // Do things *after* initializing various download manager properties such as
867 : // restoring downloads to a consistent state
868 34 : rv = RestoreDatabaseState();
869 34 : NS_ENSURE_SUCCESS(rv, rv);
870 :
871 34 : rv = RestoreActiveDownloads();
872 34 : NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to restore all active downloads");
873 :
874 : nsCOMPtr<nsIPrivateBrowsingService> pbs =
875 68 : do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
876 34 : if (pbs) {
877 34 : (void)pbs->GetPrivateBrowsingEnabled(&mInPrivateBrowsing);
878 34 : if (mInPrivateBrowsing)
879 0 : OnEnterPrivateBrowsingMode();
880 : }
881 :
882 : nsCOMPtr<nsINavHistoryService> history =
883 68 : do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
884 :
885 : // The following AddObserver calls must be the last lines in this function,
886 : // because otherwise, this function may fail (and thus, this object would be not
887 : // completely initialized), but the observerservice would still keep a reference
888 : // to us and notify us about shutdown, which may cause crashes.
889 : // failure to add an observer is not critical
890 34 : (void)mObserverService->AddObserver(this, "quit-application", true);
891 34 : (void)mObserverService->AddObserver(this, "quit-application-requested", true);
892 34 : (void)mObserverService->AddObserver(this, "offline-requested", true);
893 34 : (void)mObserverService->AddObserver(this, "sleep_notification", true);
894 34 : (void)mObserverService->AddObserver(this, "wake_notification", true);
895 34 : (void)mObserverService->AddObserver(this, "profile-before-change", true);
896 34 : (void)mObserverService->AddObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC, true);
897 34 : (void)mObserverService->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, true);
898 34 : (void)mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_REQUEST_TOPIC, true);
899 34 : (void)mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true);
900 :
901 34 : if (history)
902 34 : (void)history->AddObserver(this, true);
903 :
904 34 : return NS_OK;
905 : }
906 :
907 : PRInt32
908 48 : nsDownloadManager::GetRetentionBehavior()
909 : {
910 : // We use 0 as the default, which is "remove when done"
911 : nsresult rv;
912 96 : nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
913 48 : NS_ENSURE_SUCCESS(rv, 0);
914 :
915 : PRInt32 val;
916 48 : rv = pref->GetIntPref(PREF_BDM_RETENTION, &val);
917 48 : NS_ENSURE_SUCCESS(rv, 0);
918 :
919 48 : return val;
920 : }
921 :
922 : enum nsDownloadManager::QuitBehavior
923 204 : nsDownloadManager::GetQuitBehavior()
924 : {
925 : // We use 0 as the default, which is "remember and resume the download"
926 : nsresult rv;
927 408 : nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
928 204 : NS_ENSURE_SUCCESS(rv, QUIT_AND_RESUME);
929 :
930 : PRInt32 val;
931 204 : rv = pref->GetIntPref(PREF_BDM_QUITBEHAVIOR, &val);
932 204 : NS_ENSURE_SUCCESS(rv, QUIT_AND_RESUME);
933 :
934 204 : switch (val) {
935 : case 1:
936 0 : return QUIT_AND_PAUSE;
937 : case 2:
938 0 : return QUIT_AND_CANCEL;
939 : default:
940 204 : return QUIT_AND_RESUME;
941 : }
942 : }
943 :
944 : nsresult
945 18 : nsDownloadManager::GetDownloadFromDB(PRUint32 aID, nsDownload **retVal)
946 : {
947 18 : NS_ASSERTION(!FindDownload(aID),
948 : "If it is a current download, you should not call this method!");
949 :
950 : // First, let's query the database and see if it even exists
951 36 : nsCOMPtr<mozIStorageStatement> stmt;
952 36 : nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
953 : "SELECT id, state, startTime, source, target, tempPath, name, referrer, "
954 : "entityID, currBytes, maxBytes, mimeType, preferredAction, "
955 : "preferredApplication, autoResume "
956 : "FROM moz_downloads "
957 36 : "WHERE id = :id"), getter_AddRefs(stmt));
958 18 : NS_ENSURE_SUCCESS(rv, rv);
959 :
960 18 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID);
961 18 : NS_ENSURE_SUCCESS(rv, rv);
962 :
963 18 : bool hasResults = false;
964 18 : rv = stmt->ExecuteStep(&hasResults);
965 18 : if (NS_FAILED(rv) || !hasResults)
966 3 : return NS_ERROR_NOT_AVAILABLE;
967 :
968 : // We have a download, so lets create it
969 30 : nsRefPtr<nsDownload> dl = new nsDownload();
970 15 : if (!dl)
971 0 : return NS_ERROR_OUT_OF_MEMORY;
972 :
973 15 : PRInt32 i = 0;
974 : // Setting all properties of the download now
975 15 : dl->mCancelable = nsnull;
976 15 : dl->mID = stmt->AsInt64(i++);
977 15 : dl->mDownloadState = stmt->AsInt32(i++);
978 15 : dl->mStartTime = stmt->AsInt64(i++);
979 :
980 30 : nsCString source;
981 15 : stmt->GetUTF8String(i++, source);
982 15 : rv = NS_NewURI(getter_AddRefs(dl->mSource), source);
983 15 : NS_ENSURE_SUCCESS(rv, rv);
984 :
985 30 : nsCString target;
986 15 : stmt->GetUTF8String(i++, target);
987 15 : rv = NS_NewURI(getter_AddRefs(dl->mTarget), target);
988 15 : NS_ENSURE_SUCCESS(rv, rv);
989 :
990 30 : nsString tempPath;
991 15 : stmt->GetString(i++, tempPath);
992 15 : if (!tempPath.IsEmpty()) {
993 0 : rv = NS_NewLocalFile(tempPath, true, getter_AddRefs(dl->mTempFile));
994 0 : NS_ENSURE_SUCCESS(rv, rv);
995 : }
996 :
997 15 : stmt->GetString(i++, dl->mDisplayName);
998 :
999 30 : nsCString referrer;
1000 15 : rv = stmt->GetUTF8String(i++, referrer);
1001 15 : if (NS_SUCCEEDED(rv) && !referrer.IsEmpty()) {
1002 1 : rv = NS_NewURI(getter_AddRefs(dl->mReferrer), referrer);
1003 1 : NS_ENSURE_SUCCESS(rv, rv);
1004 : }
1005 :
1006 15 : rv = stmt->GetUTF8String(i++, dl->mEntityID);
1007 15 : NS_ENSURE_SUCCESS(rv, rv);
1008 :
1009 15 : PRInt64 currBytes = stmt->AsInt64(i++);
1010 15 : PRInt64 maxBytes = stmt->AsInt64(i++);
1011 15 : dl->SetProgressBytes(currBytes, maxBytes);
1012 :
1013 : // Build mMIMEInfo only if the mimeType in DB is not empty
1014 30 : nsCAutoString mimeType;
1015 15 : rv = stmt->GetUTF8String(i++, mimeType);
1016 15 : NS_ENSURE_SUCCESS(rv, rv);
1017 :
1018 15 : if (!mimeType.IsEmpty()) {
1019 : nsCOMPtr<nsIMIMEService> mimeService =
1020 0 : do_GetService(NS_MIMESERVICE_CONTRACTID, &rv);
1021 0 : NS_ENSURE_SUCCESS(rv, rv);
1022 :
1023 0 : rv = mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(),
1024 0 : getter_AddRefs(dl->mMIMEInfo));
1025 0 : NS_ENSURE_SUCCESS(rv, rv);
1026 :
1027 0 : nsHandlerInfoAction action = stmt->AsInt32(i++);
1028 0 : rv = dl->mMIMEInfo->SetPreferredAction(action);
1029 0 : NS_ENSURE_SUCCESS(rv, rv);
1030 :
1031 0 : nsCAutoString persistentDescriptor;
1032 0 : rv = stmt->GetUTF8String(i++, persistentDescriptor);
1033 0 : NS_ENSURE_SUCCESS(rv, rv);
1034 :
1035 0 : if (!persistentDescriptor.IsEmpty()) {
1036 : nsCOMPtr<nsILocalHandlerApp> handler =
1037 0 : do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID, &rv);
1038 0 : NS_ENSURE_SUCCESS(rv, rv);
1039 :
1040 0 : nsCOMPtr<nsILocalFile> localExecutable;
1041 0 : rv = NS_NewNativeLocalFile(EmptyCString(), false,
1042 0 : getter_AddRefs(localExecutable));
1043 0 : NS_ENSURE_SUCCESS(rv, rv);
1044 :
1045 0 : rv = localExecutable->SetPersistentDescriptor(persistentDescriptor);
1046 0 : NS_ENSURE_SUCCESS(rv, rv);
1047 :
1048 0 : rv = handler->SetExecutable(localExecutable);
1049 0 : NS_ENSURE_SUCCESS(rv, rv);
1050 :
1051 0 : rv = dl->mMIMEInfo->SetPreferredApplicationHandler(handler);
1052 0 : NS_ENSURE_SUCCESS(rv, rv);
1053 : }
1054 : } else {
1055 : // Compensate for the i++s skipped in the true block
1056 15 : i += 2;
1057 : }
1058 :
1059 15 : dl->mAutoResume =
1060 15 : static_cast<enum nsDownload::AutoResume>(stmt->AsInt32(i++));
1061 :
1062 : // Addrefing and returning
1063 15 : NS_ADDREF(*retVal = dl);
1064 15 : return NS_OK;
1065 : }
1066 :
1067 : nsresult
1068 29 : nsDownloadManager::AddToCurrentDownloads(nsDownload *aDl)
1069 : {
1070 29 : if (!mCurrentDownloads.AppendObject(aDl))
1071 0 : return NS_ERROR_OUT_OF_MEMORY;
1072 :
1073 29 : aDl->mDownloadManager = this;
1074 29 : return NS_OK;
1075 : }
1076 :
1077 : void
1078 37 : nsDownloadManager::SendEvent(nsDownload *aDownload, const char *aTopic)
1079 : {
1080 37 : (void)mObserverService->NotifyObservers(aDownload, aTopic, nsnull);
1081 37 : }
1082 :
1083 : ////////////////////////////////////////////////////////////////////////////////
1084 : //// nsIDownloadManager
1085 :
1086 : NS_IMETHODIMP
1087 15 : nsDownloadManager::GetActiveDownloadCount(PRInt32 *aResult)
1088 : {
1089 15 : *aResult = mCurrentDownloads.Count();
1090 :
1091 15 : return NS_OK;
1092 : }
1093 :
1094 : NS_IMETHODIMP
1095 64 : nsDownloadManager::GetActiveDownloads(nsISimpleEnumerator **aResult)
1096 : {
1097 64 : return NS_NewArrayEnumerator(aResult, mCurrentDownloads);
1098 : }
1099 :
1100 : /**
1101 : * For platforms where helper apps use the downloads directory (i.e. mobile),
1102 : * this should be kept in sync with nsExternalHelperAppService.cpp
1103 : */
1104 : NS_IMETHODIMP
1105 9 : nsDownloadManager::GetDefaultDownloadsDirectory(nsILocalFile **aResult)
1106 : {
1107 18 : nsCOMPtr<nsILocalFile> downloadDir;
1108 :
1109 : nsresult rv;
1110 : nsCOMPtr<nsIProperties> dirService =
1111 18 : do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
1112 9 : NS_ENSURE_SUCCESS(rv, rv);
1113 :
1114 : // OSX 10.4:
1115 : // Desktop
1116 : // OSX 10.5:
1117 : // User download directory
1118 : // Vista:
1119 : // Downloads
1120 : // XP/2K:
1121 : // My Documents/Downloads
1122 : // Linux:
1123 : // XDG user dir spec, with a fallback to Home/Downloads
1124 :
1125 18 : nsXPIDLString folderName;
1126 18 : mBundle->GetStringFromName(NS_LITERAL_STRING("downloadsFolder").get(),
1127 18 : getter_Copies(folderName));
1128 :
1129 : #if defined (XP_MACOSX)
1130 : rv = dirService->Get(NS_OSX_DEFAULT_DOWNLOAD_DIR,
1131 : NS_GET_IID(nsILocalFile),
1132 : getter_AddRefs(downloadDir));
1133 : NS_ENSURE_SUCCESS(rv, rv);
1134 : #elif defined(XP_WIN)
1135 : rv = dirService->Get(NS_WIN_DEFAULT_DOWNLOAD_DIR,
1136 : NS_GET_IID(nsILocalFile),
1137 : getter_AddRefs(downloadDir));
1138 : NS_ENSURE_SUCCESS(rv, rv);
1139 :
1140 : // Check the os version
1141 : nsCOMPtr<nsIPropertyBag2> infoService =
1142 : do_GetService(NS_SYSTEMINFO_CONTRACTID, &rv);
1143 : NS_ENSURE_SUCCESS(rv, rv);
1144 :
1145 : PRInt32 version;
1146 : NS_NAMED_LITERAL_STRING(osVersion, "version");
1147 : rv = infoService->GetPropertyAsInt32(osVersion, &version);
1148 : NS_ENSURE_SUCCESS(rv, rv);
1149 : if (version < 6) { // XP/2K
1150 : // First get "My Documents"
1151 : rv = dirService->Get(NS_WIN_PERSONAL_DIR,
1152 : NS_GET_IID(nsILocalFile),
1153 : getter_AddRefs(downloadDir));
1154 : NS_ENSURE_SUCCESS(rv, rv);
1155 :
1156 : rv = downloadDir->Append(folderName);
1157 : NS_ENSURE_SUCCESS(rv, rv);
1158 :
1159 : // This could be the first time we are creating the downloads folder in My
1160 : // Documents, so make sure it exists.
1161 : bool exists;
1162 : rv = downloadDir->Exists(&exists);
1163 : NS_ENSURE_SUCCESS(rv, rv);
1164 : if (!exists) {
1165 : rv = downloadDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
1166 : NS_ENSURE_SUCCESS(rv, rv);
1167 : }
1168 : }
1169 : #elif defined(XP_UNIX)
1170 : #if defined(MOZ_PLATFORM_MAEMO)
1171 : // As maemo does not follow the XDG "standard" (as usually desktop
1172 : // Linux distros do) neither has a working $HOME/Desktop folder
1173 : // for us to fallback into, "$HOME/MyDocs/.documents/" is the folder
1174 : // we found most apropriate to be the default target folder for downloads
1175 : // on the platform.
1176 : rv = dirService->Get(NS_UNIX_XDG_DOCUMENTS_DIR,
1177 : NS_GET_IID(nsILocalFile),
1178 : getter_AddRefs(downloadDir));
1179 : #elif defined(MOZ_WIDGET_ANDROID)
1180 : // Android doesn't have a $HOME directory, and by default we only have
1181 : // write access to /data/data/org.mozilla.{$APP} and /sdcard
1182 : char* downloadDirPath = getenv("DOWNLOADS_DIRECTORY");
1183 : if (downloadDirPath) {
1184 : rv = NS_NewNativeLocalFile(nsDependentCString(downloadDirPath),
1185 : true, getter_AddRefs(downloadDir));
1186 : NS_ENSURE_SUCCESS(rv, rv);
1187 : }
1188 : else {
1189 : rv = NS_ERROR_FAILURE;
1190 : }
1191 : #else
1192 9 : rv = dirService->Get(NS_UNIX_DEFAULT_DOWNLOAD_DIR,
1193 : NS_GET_IID(nsILocalFile),
1194 9 : getter_AddRefs(downloadDir));
1195 : // fallback to Home/Downloads
1196 9 : if (NS_FAILED(rv)) {
1197 9 : rv = dirService->Get(NS_UNIX_HOME_DIR,
1198 : NS_GET_IID(nsILocalFile),
1199 9 : getter_AddRefs(downloadDir));
1200 9 : NS_ENSURE_SUCCESS(rv, rv);
1201 9 : rv = downloadDir->Append(folderName);
1202 9 : NS_ENSURE_SUCCESS(rv, rv);
1203 : }
1204 : #endif
1205 : #else
1206 : rv = dirService->Get(NS_OS_HOME_DIR,
1207 : NS_GET_IID(nsILocalFile),
1208 : getter_AddRefs(downloadDir));
1209 : NS_ENSURE_SUCCESS(rv, rv);
1210 : rv = downloadDir->Append(folderName);
1211 : NS_ENSURE_SUCCESS(rv, rv);
1212 : #endif
1213 :
1214 9 : downloadDir.forget(aResult);
1215 :
1216 9 : return NS_OK;
1217 : }
1218 :
1219 : #define NS_BRANCH_DOWNLOAD "browser.download."
1220 : #define NS_PREF_FOLDERLIST "folderList"
1221 : #define NS_PREF_DIR "dir"
1222 :
1223 : NS_IMETHODIMP
1224 9 : nsDownloadManager::GetUserDownloadsDirectory(nsILocalFile **aResult)
1225 : {
1226 : nsresult rv;
1227 : nsCOMPtr<nsIProperties> dirService =
1228 18 : do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
1229 9 : NS_ENSURE_SUCCESS(rv, rv);
1230 :
1231 : nsCOMPtr<nsIPrefService> prefService =
1232 18 : do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
1233 9 : NS_ENSURE_SUCCESS(rv, rv);
1234 :
1235 18 : nsCOMPtr<nsIPrefBranch> prefBranch;
1236 9 : rv = prefService->GetBranch(NS_BRANCH_DOWNLOAD,
1237 9 : getter_AddRefs(prefBranch));
1238 9 : NS_ENSURE_SUCCESS(rv, rv);
1239 :
1240 : PRInt32 val;
1241 9 : rv = prefBranch->GetIntPref(NS_PREF_FOLDERLIST,
1242 9 : &val);
1243 9 : NS_ENSURE_SUCCESS(rv, rv);
1244 :
1245 9 : switch(val) {
1246 : case 0: // Desktop
1247 : {
1248 0 : nsCOMPtr<nsILocalFile> downloadDir;
1249 : nsCOMPtr<nsIProperties> dirService =
1250 0 : do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
1251 0 : NS_ENSURE_SUCCESS(rv, rv);
1252 0 : rv = dirService->Get(NS_OS_DESKTOP_DIR,
1253 : NS_GET_IID(nsILocalFile),
1254 0 : getter_AddRefs(downloadDir));
1255 0 : NS_ENSURE_SUCCESS(rv, rv);
1256 0 : downloadDir.forget(aResult);
1257 0 : return NS_OK;
1258 : }
1259 : break;
1260 : case 1: // Downloads
1261 9 : return GetDefaultDownloadsDirectory(aResult);
1262 : case 2: // Custom
1263 : {
1264 0 : nsCOMPtr<nsILocalFile> customDirectory;
1265 0 : prefBranch->GetComplexValue(NS_PREF_DIR,
1266 : NS_GET_IID(nsILocalFile),
1267 0 : getter_AddRefs(customDirectory));
1268 0 : if (customDirectory) {
1269 0 : bool exists = false;
1270 0 : (void)customDirectory->Exists(&exists);
1271 :
1272 0 : if (!exists) {
1273 0 : rv = customDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
1274 0 : if (NS_SUCCEEDED(rv)) {
1275 0 : customDirectory.forget(aResult);
1276 0 : return NS_OK;
1277 : }
1278 :
1279 : // Create failed, so it still doesn't exist. Fall out and get the
1280 : // default downloads directory.
1281 : }
1282 :
1283 0 : bool writable = false;
1284 0 : bool directory = false;
1285 0 : (void)customDirectory->IsWritable(&writable);
1286 0 : (void)customDirectory->IsDirectory(&directory);
1287 :
1288 0 : if (exists && writable && directory) {
1289 0 : customDirectory.forget(aResult);
1290 0 : return NS_OK;
1291 : }
1292 : }
1293 0 : rv = GetDefaultDownloadsDirectory(aResult);
1294 0 : if (NS_SUCCEEDED(rv)) {
1295 0 : (void)prefBranch->SetComplexValue(NS_PREF_DIR,
1296 : NS_GET_IID(nsILocalFile),
1297 0 : *aResult);
1298 : }
1299 0 : return rv;
1300 : }
1301 : break;
1302 : }
1303 0 : return NS_ERROR_INVALID_ARG;
1304 : }
1305 :
1306 : NS_IMETHODIMP
1307 20 : nsDownloadManager::AddDownload(DownloadType aDownloadType,
1308 : nsIURI *aSource,
1309 : nsIURI *aTarget,
1310 : const nsAString& aDisplayName,
1311 : nsIMIMEInfo *aMIMEInfo,
1312 : PRTime aStartTime,
1313 : nsILocalFile *aTempFile,
1314 : nsICancelable *aCancelable,
1315 : nsIDownload **aDownload)
1316 : {
1317 20 : NS_ENSURE_ARG_POINTER(aSource);
1318 20 : NS_ENSURE_ARG_POINTER(aTarget);
1319 20 : NS_ENSURE_ARG_POINTER(aDownload);
1320 :
1321 : nsresult rv;
1322 :
1323 : // target must be on the local filesystem
1324 40 : nsCOMPtr<nsIFileURL> targetFileURL = do_QueryInterface(aTarget, &rv);
1325 20 : NS_ENSURE_SUCCESS(rv, rv);
1326 :
1327 40 : nsCOMPtr<nsIFile> targetFile;
1328 20 : rv = targetFileURL->GetFile(getter_AddRefs(targetFile));
1329 20 : NS_ENSURE_SUCCESS(rv, rv);
1330 :
1331 40 : nsRefPtr<nsDownload> dl = new nsDownload();
1332 20 : if (!dl)
1333 0 : return NS_ERROR_OUT_OF_MEMORY;
1334 :
1335 : // give our new nsIDownload some info so it's ready to go off into the world
1336 20 : dl->mTarget = aTarget;
1337 20 : dl->mSource = aSource;
1338 20 : dl->mTempFile = aTempFile;
1339 :
1340 20 : dl->mDisplayName = aDisplayName;
1341 20 : if (dl->mDisplayName.IsEmpty())
1342 14 : targetFile->GetLeafName(dl->mDisplayName);
1343 :
1344 20 : dl->mMIMEInfo = aMIMEInfo;
1345 20 : dl->SetStartTime(aStartTime == 0 ? PR_Now() : aStartTime);
1346 :
1347 : // Creates a cycle that will be broken when the download finishes
1348 20 : dl->mCancelable = aCancelable;
1349 :
1350 : // Adding to the DB
1351 40 : nsCAutoString source, target;
1352 20 : aSource->GetSpec(source);
1353 20 : aTarget->GetSpec(target);
1354 :
1355 : // Track the temp file for exthandler downloads
1356 40 : nsAutoString tempPath;
1357 20 : if (aTempFile)
1358 6 : aTempFile->GetPath(tempPath);
1359 :
1360 : // Break down MIMEInfo but don't panic if we can't get all the pieces - we
1361 : // can still download the file
1362 40 : nsCAutoString persistentDescriptor, mimeType;
1363 20 : nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
1364 20 : if (aMIMEInfo) {
1365 6 : (void)aMIMEInfo->GetType(mimeType);
1366 :
1367 12 : nsCOMPtr<nsIHandlerApp> handlerApp;
1368 6 : (void)aMIMEInfo->GetPreferredApplicationHandler(getter_AddRefs(handlerApp));
1369 12 : nsCOMPtr<nsILocalHandlerApp> locHandlerApp = do_QueryInterface(handlerApp);
1370 :
1371 6 : if (locHandlerApp) {
1372 0 : nsCOMPtr<nsIFile> executable;
1373 0 : (void)locHandlerApp->GetExecutable(getter_AddRefs(executable));
1374 0 : nsCOMPtr<nsILocalFile> locExecutable = do_QueryInterface(executable);
1375 :
1376 0 : if (locExecutable)
1377 0 : (void)locExecutable->GetPersistentDescriptor(persistentDescriptor);
1378 : }
1379 :
1380 6 : (void)aMIMEInfo->GetPreferredAction(&action);
1381 : }
1382 :
1383 20 : DownloadState startState = nsIDownloadManager::DOWNLOAD_QUEUED;
1384 :
1385 20 : PRInt64 id = AddDownloadToDB(dl->mDisplayName, source, target, tempPath,
1386 40 : dl->mStartTime, dl->mLastUpdate,
1387 80 : mimeType, persistentDescriptor, action);
1388 20 : NS_ENSURE_TRUE(id, NS_ERROR_FAILURE);
1389 20 : dl->mID = id;
1390 :
1391 20 : rv = AddToCurrentDownloads(dl);
1392 20 : (void)dl->SetState(startState);
1393 20 : NS_ENSURE_SUCCESS(rv, rv);
1394 :
1395 : #ifdef DOWNLOAD_SCANNER
1396 : if (mScanner) {
1397 : bool scan = true;
1398 : nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
1399 : if (prefs) {
1400 : (void)prefs->GetBoolPref(PREF_BDM_SCANWHENDONE, &scan);
1401 : }
1402 : // We currently apply local security policy to downloads when we scan
1403 : // via windows all-in-one download security api. The CheckPolicy call
1404 : // below is a pre-emptive part of that process. So tie applying security
1405 : // zone policy settings when downloads are intiated to the same pref
1406 : // that triggers applying security zone policy settings after a download
1407 : // completes. (bug 504804)
1408 : if (scan) {
1409 : AVCheckPolicyState res = mScanner->CheckPolicy(aSource, aTarget);
1410 : if (res == AVPOLICY_BLOCKED) {
1411 : // This download will get deleted during a call to IAE's Save,
1412 : // so go ahead and mark it as blocked and avoid the download.
1413 : (void)CancelDownload(id);
1414 : startState = nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY;
1415 : }
1416 : }
1417 : }
1418 : #endif
1419 :
1420 : // Check with parental controls to see if file downloads
1421 : // are allowed for this user. If not allowed, cancel the
1422 : // download and mark its state as being blocked.
1423 : nsCOMPtr<nsIParentalControlsService> pc =
1424 40 : do_CreateInstance(NS_PARENTALCONTROLSSERVICE_CONTRACTID);
1425 20 : if (pc) {
1426 0 : bool enabled = false;
1427 0 : (void)pc->GetBlockFileDownloadsEnabled(&enabled);
1428 0 : if (enabled) {
1429 0 : (void)CancelDownload(id);
1430 0 : (void)dl->SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL);
1431 : }
1432 :
1433 : // Log the event if required by pc settings.
1434 0 : bool logEnabled = false;
1435 0 : (void)pc->GetLoggingEnabled(&logEnabled);
1436 0 : if (logEnabled) {
1437 0 : (void)pc->Log(nsIParentalControlsService::ePCLog_FileDownload,
1438 : enabled,
1439 : aSource,
1440 0 : nsnull);
1441 : }
1442 : }
1443 :
1444 20 : NS_ADDREF(*aDownload = dl);
1445 :
1446 20 : return NS_OK;
1447 : }
1448 :
1449 : NS_IMETHODIMP
1450 21 : nsDownloadManager::GetDownload(PRUint32 aID, nsIDownload **aDownloadItem)
1451 : {
1452 21 : nsDownload *itm = FindDownload(aID);
1453 :
1454 42 : nsRefPtr<nsDownload> dl;
1455 21 : if (!itm) {
1456 8 : nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
1457 8 : NS_ENSURE_SUCCESS(rv, rv);
1458 :
1459 6 : itm = dl.get();
1460 : }
1461 :
1462 19 : NS_ADDREF(*aDownloadItem = itm);
1463 :
1464 19 : return NS_OK;
1465 : }
1466 :
1467 : nsDownload *
1468 67 : nsDownloadManager::FindDownload(PRUint32 aID)
1469 : {
1470 : // we shouldn't ever have many downloads, so we can loop over them
1471 88 : for (PRInt32 i = mCurrentDownloads.Count() - 1; i >= 0; --i) {
1472 54 : nsDownload *dl = mCurrentDownloads[i];
1473 :
1474 54 : if (dl->mID == aID)
1475 33 : return dl;
1476 : }
1477 :
1478 34 : return nsnull;
1479 : }
1480 :
1481 : NS_IMETHODIMP
1482 11 : nsDownloadManager::CancelDownload(PRUint32 aID)
1483 : {
1484 : // We AddRef here so we don't lose access to member variables when we remove
1485 22 : nsRefPtr<nsDownload> dl = FindDownload(aID);
1486 :
1487 : // if it's null, someone passed us a bad id.
1488 11 : if (!dl)
1489 0 : return NS_ERROR_FAILURE;
1490 :
1491 : // Don't cancel if download is already finished
1492 11 : if (dl->IsFinished())
1493 0 : return NS_OK;
1494 :
1495 : // if the download is fake-paused, we have to resume it so we can cancel it
1496 11 : if (dl->IsPaused() && !dl->IsResumable())
1497 0 : (void)dl->Resume();
1498 :
1499 : // Have the download cancel its connection
1500 11 : (void)dl->Cancel();
1501 :
1502 : // Dump the temp file because we know we don't need the file anymore. The
1503 : // underlying transfer creating the file doesn't delete the file because it
1504 : // can't distinguish between a pause that cancels the transfer or a real
1505 : // cancel.
1506 11 : if (dl->mTempFile) {
1507 : bool exists;
1508 3 : dl->mTempFile->Exists(&exists);
1509 3 : if (exists)
1510 3 : dl->mTempFile->Remove(false);
1511 : }
1512 :
1513 22 : nsCOMPtr<nsILocalFile> file;
1514 11 : if (NS_SUCCEEDED(dl->GetTargetFile(getter_AddRefs(file))))
1515 : {
1516 : bool exists;
1517 11 : file->Exists(&exists);
1518 11 : if (exists)
1519 2 : file->Remove(false);
1520 : }
1521 :
1522 11 : nsresult rv = dl->SetState(nsIDownloadManager::DOWNLOAD_CANCELED);
1523 11 : NS_ENSURE_SUCCESS(rv, rv);
1524 :
1525 11 : return NS_OK;
1526 : }
1527 :
1528 : NS_IMETHODIMP
1529 3 : nsDownloadManager::RetryDownload(PRUint32 aID)
1530 : {
1531 6 : nsRefPtr<nsDownload> dl;
1532 3 : nsresult rv = GetDownloadFromDB(aID, getter_AddRefs(dl));
1533 3 : NS_ENSURE_SUCCESS(rv, rv);
1534 :
1535 : // if our download is not canceled or failed, we should fail
1536 10 : if (dl->mDownloadState != nsIDownloadManager::DOWNLOAD_FAILED &&
1537 2 : dl->mDownloadState != nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL &&
1538 2 : dl->mDownloadState != nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY &&
1539 2 : dl->mDownloadState != nsIDownloadManager::DOWNLOAD_DIRTY &&
1540 2 : dl->mDownloadState != nsIDownloadManager::DOWNLOAD_CANCELED)
1541 0 : return NS_ERROR_FAILURE;
1542 :
1543 : // If the download has failed and is resumable then we first try resuming it
1544 2 : if (dl->mDownloadState == nsIDownloadManager::DOWNLOAD_FAILED && dl->IsResumable()) {
1545 0 : rv = dl->Resume();
1546 0 : if (NS_SUCCEEDED(rv))
1547 0 : return rv;
1548 : }
1549 :
1550 : // reset time and download progress
1551 2 : dl->SetStartTime(PR_Now());
1552 2 : dl->SetProgressBytes(0, -1);
1553 :
1554 : nsCOMPtr<nsIWebBrowserPersist> wbp =
1555 4 : do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
1556 2 : NS_ENSURE_SUCCESS(rv, rv);
1557 :
1558 2 : rv = wbp->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES |
1559 2 : nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
1560 2 : NS_ENSURE_SUCCESS(rv, rv);
1561 :
1562 2 : rv = AddToCurrentDownloads(dl);
1563 2 : NS_ENSURE_SUCCESS(rv, rv);
1564 :
1565 2 : rv = dl->SetState(nsIDownloadManager::DOWNLOAD_QUEUED);
1566 2 : NS_ENSURE_SUCCESS(rv, rv);
1567 :
1568 : // Creates a cycle that will be broken when the download finishes
1569 2 : dl->mCancelable = wbp;
1570 2 : (void)wbp->SetProgressListener(dl);
1571 :
1572 2 : rv = wbp->SaveURI(dl->mSource, nsnull, nsnull, nsnull, nsnull, dl->mTarget);
1573 2 : if (NS_FAILED(rv)) {
1574 0 : dl->mCancelable = nsnull;
1575 0 : (void)wbp->SetProgressListener(nsnull);
1576 0 : return rv;
1577 : }
1578 :
1579 2 : return NS_OK;
1580 : }
1581 :
1582 : NS_IMETHODIMP
1583 6 : nsDownloadManager::RemoveDownload(PRUint32 aID)
1584 : {
1585 6 : nsDownload *dl = FindDownload(aID);
1586 6 : NS_ASSERTION(!dl, "Can't call RemoveDownload on a download in progress!");
1587 6 : if (dl)
1588 0 : return NS_ERROR_FAILURE;
1589 :
1590 12 : nsCOMPtr<mozIStorageStatement> stmt;
1591 12 : nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1592 : "DELETE FROM moz_downloads "
1593 12 : "WHERE id = :id"), getter_AddRefs(stmt));
1594 6 : NS_ENSURE_SUCCESS(rv, rv);
1595 :
1596 6 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aID); // unsigned; 64-bit to prevent overflow
1597 6 : NS_ENSURE_SUCCESS(rv, rv);
1598 :
1599 6 : rv = stmt->Execute();
1600 6 : NS_ENSURE_SUCCESS(rv, rv);
1601 :
1602 : nsCOMPtr<nsISupportsPRUint32> id =
1603 12 : do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
1604 6 : NS_ENSURE_SUCCESS(rv, rv);
1605 6 : rv = id->SetData(aID);
1606 6 : NS_ENSURE_SUCCESS(rv, rv);
1607 :
1608 : // Notify the UI with the topic and download id
1609 6 : return mObserverService->NotifyObservers(id,
1610 : "download-manager-remove-download",
1611 6 : nsnull);
1612 : }
1613 :
1614 : NS_IMETHODIMP
1615 6 : nsDownloadManager::RemoveDownloadsByTimeframe(PRInt64 aStartTime,
1616 : PRInt64 aEndTime)
1617 : {
1618 12 : nsCOMPtr<mozIStorageStatement> stmt;
1619 12 : nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1620 : "DELETE FROM moz_downloads "
1621 : "WHERE startTime >= :startTime "
1622 : "AND startTime <= :endTime "
1623 12 : "AND state NOT IN (:downloading, :paused, :queued)"), getter_AddRefs(stmt));
1624 6 : NS_ENSURE_SUCCESS(rv, rv);
1625 :
1626 : // Bind the times
1627 6 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), aStartTime);
1628 6 : NS_ENSURE_SUCCESS(rv, rv);
1629 6 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), aEndTime);
1630 6 : NS_ENSURE_SUCCESS(rv, rv);
1631 :
1632 : // Bind the active states
1633 6 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("downloading"), nsIDownloadManager::DOWNLOAD_DOWNLOADING);
1634 6 : NS_ENSURE_SUCCESS(rv, rv);
1635 6 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("paused"), nsIDownloadManager::DOWNLOAD_PAUSED);
1636 6 : NS_ENSURE_SUCCESS(rv, rv);
1637 6 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("queued"), nsIDownloadManager::DOWNLOAD_QUEUED);
1638 6 : NS_ENSURE_SUCCESS(rv, rv);
1639 :
1640 : // Execute
1641 6 : rv = stmt->Execute();
1642 6 : NS_ENSURE_SUCCESS(rv, rv);
1643 :
1644 : // Notify the UI with the topic and null subject to indicate "remove multiple"
1645 6 : return mObserverService->NotifyObservers(nsnull,
1646 : "download-manager-remove-download",
1647 6 : nsnull);
1648 : }
1649 :
1650 : NS_IMETHODIMP
1651 11 : nsDownloadManager::CleanUp()
1652 : {
1653 : DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
1654 : nsIDownloadManager::DOWNLOAD_FAILED,
1655 : nsIDownloadManager::DOWNLOAD_CANCELED,
1656 : nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL,
1657 : nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY,
1658 11 : nsIDownloadManager::DOWNLOAD_DIRTY };
1659 :
1660 22 : nsCOMPtr<mozIStorageStatement> stmt;
1661 22 : nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1662 : "DELETE FROM moz_downloads "
1663 : "WHERE state = ? "
1664 : "OR state = ? "
1665 : "OR state = ? "
1666 : "OR state = ? "
1667 : "OR state = ? "
1668 22 : "OR state = ?"), getter_AddRefs(stmt));
1669 11 : NS_ENSURE_SUCCESS(rv, rv);
1670 77 : for (PRUint32 i = 0; i < ArrayLength(states); ++i) {
1671 66 : rv = stmt->BindInt32ByIndex(i, states[i]);
1672 66 : NS_ENSURE_SUCCESS(rv, rv);
1673 : }
1674 :
1675 11 : rv = stmt->Execute();
1676 11 : NS_ENSURE_SUCCESS(rv, rv);
1677 :
1678 : // Notify the UI with the topic and null subject to indicate "remove multiple"
1679 11 : return mObserverService->NotifyObservers(nsnull,
1680 : "download-manager-remove-download",
1681 11 : nsnull);
1682 : }
1683 :
1684 : NS_IMETHODIMP
1685 3 : nsDownloadManager::GetCanCleanUp(bool *aResult)
1686 : {
1687 : // This method should never return anything but NS_OK for the benefit of
1688 : // unwitting consumers.
1689 :
1690 3 : *aResult = false;
1691 :
1692 : DownloadState states[] = { nsIDownloadManager::DOWNLOAD_FINISHED,
1693 : nsIDownloadManager::DOWNLOAD_FAILED,
1694 : nsIDownloadManager::DOWNLOAD_CANCELED,
1695 : nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL,
1696 : nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY,
1697 3 : nsIDownloadManager::DOWNLOAD_DIRTY };
1698 :
1699 6 : nsCOMPtr<mozIStorageStatement> stmt;
1700 6 : nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1701 : "SELECT COUNT(*) "
1702 : "FROM moz_downloads "
1703 : "WHERE state = ? "
1704 : "OR state = ? "
1705 : "OR state = ? "
1706 : "OR state = ? "
1707 : "OR state = ? "
1708 6 : "OR state = ?"), getter_AddRefs(stmt));
1709 3 : NS_ENSURE_SUCCESS(rv, NS_OK);
1710 21 : for (PRUint32 i = 0; i < ArrayLength(states); ++i) {
1711 18 : rv = stmt->BindInt32ByIndex(i, states[i]);
1712 18 : NS_ENSURE_SUCCESS(rv, NS_OK);
1713 : }
1714 :
1715 : bool moreResults; // We don't really care...
1716 3 : rv = stmt->ExecuteStep(&moreResults);
1717 3 : NS_ENSURE_SUCCESS(rv, NS_OK);
1718 :
1719 : PRInt32 count;
1720 3 : rv = stmt->GetInt32(0, &count);
1721 3 : NS_ENSURE_SUCCESS(rv, NS_OK);
1722 :
1723 3 : if (count > 0)
1724 1 : *aResult = true;
1725 :
1726 3 : return NS_OK;
1727 : }
1728 :
1729 : NS_IMETHODIMP
1730 6 : nsDownloadManager::PauseDownload(PRUint32 aID)
1731 : {
1732 6 : nsDownload *dl = FindDownload(aID);
1733 6 : if (!dl)
1734 1 : return NS_ERROR_FAILURE;
1735 :
1736 5 : return dl->Pause();
1737 : }
1738 :
1739 : NS_IMETHODIMP
1740 5 : nsDownloadManager::ResumeDownload(PRUint32 aID)
1741 : {
1742 5 : nsDownload *dl = FindDownload(aID);
1743 5 : if (!dl)
1744 1 : return NS_ERROR_FAILURE;
1745 :
1746 4 : return dl->Resume();
1747 : }
1748 :
1749 : NS_IMETHODIMP
1750 110 : nsDownloadManager::GetDBConnection(mozIStorageConnection **aDBConn)
1751 : {
1752 110 : NS_ADDREF(*aDBConn = mDBConn);
1753 :
1754 110 : return NS_OK;
1755 : }
1756 :
1757 : NS_IMETHODIMP
1758 17 : nsDownloadManager::AddListener(nsIDownloadProgressListener *aListener)
1759 : {
1760 17 : mListeners.AppendObject(aListener);
1761 :
1762 17 : return NS_OK;
1763 : }
1764 :
1765 : NS_IMETHODIMP
1766 4 : nsDownloadManager::RemoveListener(nsIDownloadProgressListener *aListener)
1767 : {
1768 4 : mListeners.RemoveObject(aListener);
1769 :
1770 4 : return NS_OK;
1771 : }
1772 :
1773 : void
1774 80 : nsDownloadManager::NotifyListenersOnDownloadStateChange(PRInt16 aOldState,
1775 : nsIDownload *aDownload)
1776 : {
1777 167 : for (PRInt32 i = mListeners.Count() - 1; i >= 0; --i)
1778 87 : mListeners[i]->OnDownloadStateChange(aOldState, aDownload);
1779 80 : }
1780 :
1781 : void
1782 7 : nsDownloadManager::NotifyListenersOnProgressChange(nsIWebProgress *aProgress,
1783 : nsIRequest *aRequest,
1784 : PRInt64 aCurSelfProgress,
1785 : PRInt64 aMaxSelfProgress,
1786 : PRInt64 aCurTotalProgress,
1787 : PRInt64 aMaxTotalProgress,
1788 : nsIDownload *aDownload)
1789 : {
1790 17 : for (PRInt32 i = mListeners.Count() - 1; i >= 0; --i)
1791 10 : mListeners[i]->OnProgressChange(aProgress, aRequest, aCurSelfProgress,
1792 : aMaxSelfProgress, aCurTotalProgress,
1793 10 : aMaxTotalProgress, aDownload);
1794 7 : }
1795 :
1796 : void
1797 54 : nsDownloadManager::NotifyListenersOnStateChange(nsIWebProgress *aProgress,
1798 : nsIRequest *aRequest,
1799 : PRUint32 aStateFlags,
1800 : nsresult aStatus,
1801 : nsIDownload *aDownload)
1802 : {
1803 118 : for (PRInt32 i = mListeners.Count() - 1; i >= 0; --i)
1804 64 : mListeners[i]->OnStateChange(aProgress, aRequest, aStateFlags, aStatus,
1805 64 : aDownload);
1806 54 : }
1807 :
1808 : nsresult
1809 14 : nsDownloadManager::SwitchDatabaseTypeTo(enum nsDownloadManager::DatabaseType aType)
1810 : {
1811 14 : if (aType == mDBType)
1812 2 : return NS_OK; // no-op
1813 :
1814 12 : mDBType = aType;
1815 :
1816 12 : (void)PauseAllDownloads(true);
1817 12 : (void)RemoveAllDownloads();
1818 :
1819 12 : nsresult rv = InitDB();
1820 12 : NS_ENSURE_SUCCESS(rv, rv);
1821 :
1822 : // Do things *after* initializing various download manager properties such as
1823 : // restoring downloads to a consistent state
1824 12 : rv = RestoreDatabaseState();
1825 12 : NS_ENSURE_SUCCESS(rv, rv);
1826 :
1827 12 : rv = RestoreActiveDownloads();
1828 12 : NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to restore all active downloads");
1829 :
1830 12 : return rv;
1831 : }
1832 :
1833 : ////////////////////////////////////////////////////////////////////////////////
1834 : //// nsINavHistoryObserver
1835 :
1836 : NS_IMETHODIMP
1837 47 : nsDownloadManager::OnBeginUpdateBatch()
1838 : {
1839 : // We already have a transaction, so don't make another
1840 47 : if (mHistoryTransaction)
1841 0 : return NS_OK;
1842 :
1843 : // Start a transaction that commits when deleted
1844 94 : mHistoryTransaction = new mozStorageTransaction(mDBConn, true);
1845 :
1846 47 : return NS_OK;
1847 : }
1848 :
1849 : NS_IMETHODIMP
1850 47 : nsDownloadManager::OnEndUpdateBatch()
1851 : {
1852 : // Get rid of the transaction and cause it to commit
1853 47 : mHistoryTransaction = nsnull;
1854 :
1855 47 : return NS_OK;
1856 : }
1857 :
1858 : NS_IMETHODIMP
1859 21 : nsDownloadManager::OnVisit(nsIURI *aURI, PRInt64 aVisitID, PRTime aTime,
1860 : PRInt64 aSessionID, PRInt64 aReferringID,
1861 : PRUint32 aTransitionType, const nsACString& aGUID,
1862 : PRUint32 *aAdded)
1863 : {
1864 21 : return NS_OK;
1865 : }
1866 :
1867 : NS_IMETHODIMP
1868 19 : nsDownloadManager::OnTitleChanged(nsIURI *aURI,
1869 : const nsAString &aPageTitle,
1870 : const nsACString &aGUID)
1871 : {
1872 19 : return NS_OK;
1873 : }
1874 :
1875 : NS_IMETHODIMP
1876 2 : nsDownloadManager::OnBeforeDeleteURI(nsIURI *aURI,
1877 : const nsACString& aGUID,
1878 : PRUint16 aReason)
1879 : {
1880 2 : return NS_OK;
1881 : }
1882 :
1883 : NS_IMETHODIMP
1884 3 : nsDownloadManager::OnDeleteURI(nsIURI *aURI,
1885 : const nsACString& aGUID,
1886 : PRUint16 aReason)
1887 : {
1888 3 : return RemoveDownloadsForURI(aURI);
1889 : }
1890 :
1891 : NS_IMETHODIMP
1892 2 : nsDownloadManager::OnClearHistory()
1893 : {
1894 2 : return CleanUp();
1895 : }
1896 :
1897 : NS_IMETHODIMP
1898 0 : nsDownloadManager::OnPageChanged(nsIURI *aURI,
1899 : PRUint32 aChangedAttribute,
1900 : const nsAString& aNewValue,
1901 : const nsACString &aGUID)
1902 : {
1903 0 : return NS_OK;
1904 : }
1905 :
1906 : NS_IMETHODIMP
1907 0 : nsDownloadManager::OnDeleteVisits(nsIURI *aURI, PRTime aVisitTime,
1908 : const nsACString& aGUID,
1909 : PRUint16 aReason)
1910 : {
1911 : // Don't bother removing downloads until the page is removed.
1912 0 : return NS_OK;
1913 : }
1914 :
1915 : ////////////////////////////////////////////////////////////////////////////////
1916 : //// nsIObserver
1917 :
1918 : NS_IMETHODIMP
1919 166 : nsDownloadManager::Observe(nsISupports *aSubject,
1920 : const char *aTopic,
1921 : const PRUnichar *aData)
1922 : {
1923 166 : PRInt32 currDownloadCount = mCurrentDownloads.Count();
1924 :
1925 : // If we don't need to cancel all the downloads on quit, only count the ones
1926 : // that aren't resumable.
1927 166 : if (GetQuitBehavior() != QUIT_AND_CANCEL)
1928 180 : for (PRInt32 i = currDownloadCount - 1; i >= 0; --i)
1929 14 : if (mCurrentDownloads[i]->IsResumable())
1930 11 : currDownloadCount--;
1931 :
1932 : nsresult rv;
1933 166 : if (strcmp(aTopic, "oncancel") == 0) {
1934 0 : nsCOMPtr<nsIDownload> dl = do_QueryInterface(aSubject, &rv);
1935 0 : NS_ENSURE_SUCCESS(rv, rv);
1936 :
1937 : PRUint32 id;
1938 0 : dl->GetId(&id);
1939 0 : nsDownload *dl2 = FindDownload(id);
1940 0 : if (dl2)
1941 0 : return CancelDownload(id);
1942 166 : } else if (strcmp(aTopic, "profile-before-change") == 0) {
1943 34 : mGetIdsForURIStatement->Finalize();
1944 34 : mUpdateDownloadStatement->Finalize();
1945 68 : mozilla::DebugOnly<nsresult> rv = mDBConn->Close();
1946 34 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1947 132 : } else if (strcmp(aTopic, "quit-application") == 0) {
1948 : // Try to pause all downloads and, if appropriate, mark them as auto-resume
1949 : // unless user has specified that downloads should be canceled
1950 34 : enum QuitBehavior behavior = GetQuitBehavior();
1951 34 : if (behavior != QUIT_AND_CANCEL)
1952 34 : (void)PauseAllDownloads(bool(behavior != QUIT_AND_PAUSE));
1953 :
1954 : // Remove downloads to break cycles and cancel downloads
1955 34 : (void)RemoveAllDownloads();
1956 :
1957 : // Now that active downloads have been canceled, remove all completed or
1958 : // aborted downloads if the user's retention policy specifies it.
1959 34 : if (GetRetentionBehavior() == 1)
1960 0 : CleanUp();
1961 98 : } else if (strcmp(aTopic, "quit-application-requested") == 0 &&
1962 : currDownloadCount) {
1963 : nsCOMPtr<nsISupportsPRBool> cancelDownloads =
1964 0 : do_QueryInterface(aSubject, &rv);
1965 0 : NS_ENSURE_SUCCESS(rv, rv);
1966 : #ifndef XP_MACOSX
1967 : ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
1968 0 : NS_LITERAL_STRING("quitCancelDownloadsAlertTitle").get(),
1969 0 : NS_LITERAL_STRING("quitCancelDownloadsAlertMsgMultiple").get(),
1970 0 : NS_LITERAL_STRING("quitCancelDownloadsAlertMsg").get(),
1971 0 : NS_LITERAL_STRING("dontQuitButtonWin").get());
1972 : #else
1973 : ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
1974 : NS_LITERAL_STRING("quitCancelDownloadsAlertTitle").get(),
1975 : NS_LITERAL_STRING("quitCancelDownloadsAlertMsgMacMultiple").get(),
1976 : NS_LITERAL_STRING("quitCancelDownloadsAlertMsgMac").get(),
1977 : NS_LITERAL_STRING("dontQuitButtonMac").get());
1978 : #endif
1979 98 : } else if (strcmp(aTopic, "offline-requested") == 0 && currDownloadCount) {
1980 : nsCOMPtr<nsISupportsPRBool> cancelDownloads =
1981 0 : do_QueryInterface(aSubject, &rv);
1982 0 : NS_ENSURE_SUCCESS(rv, rv);
1983 : ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
1984 0 : NS_LITERAL_STRING("offlineCancelDownloadsAlertTitle").get(),
1985 0 : NS_LITERAL_STRING("offlineCancelDownloadsAlertMsgMultiple").get(),
1986 0 : NS_LITERAL_STRING("offlineCancelDownloadsAlertMsg").get(),
1987 0 : NS_LITERAL_STRING("dontGoOfflineButton").get());
1988 : }
1989 98 : else if (strcmp(aTopic, NS_IOSERVICE_GOING_OFFLINE_TOPIC) == 0) {
1990 : // Pause all downloads, and mark them to auto-resume.
1991 35 : (void)PauseAllDownloads(true);
1992 : }
1993 196 : else if (strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) == 0 &&
1994 133 : nsDependentString(aData).EqualsLiteral(NS_IOSERVICE_ONLINE)) {
1995 : // We can now resume all downloads that are supposed to auto-resume.
1996 1 : (void)ResumeAllDownloads(false);
1997 : }
1998 62 : else if (strcmp(aTopic, "dlmgr-switchdb") == 0) {
1999 4 : if (NS_LITERAL_STRING("memory").Equals(aData))
2000 2 : return SwitchDatabaseTypeTo(DATABASE_MEMORY);
2001 2 : else if (NS_LITERAL_STRING("disk").Equals(aData))
2002 2 : return SwitchDatabaseTypeTo(DATABASE_DISK);
2003 : }
2004 58 : else if (strcmp(aTopic, "alertclickcallback") == 0) {
2005 : nsCOMPtr<nsIDownloadManagerUI> dmui =
2006 0 : do_GetService("@mozilla.org/download-manager-ui;1", &rv);
2007 0 : NS_ENSURE_SUCCESS(rv, rv);
2008 0 : return dmui->Show(nsnull, 0, nsIDownloadManagerUI::REASON_USER_INTERACTED);
2009 58 : } else if (strcmp(aTopic, "sleep_notification") == 0) {
2010 : // Pause downloads if we're sleeping, and mark the downloads as auto-resume
2011 1 : (void)PauseAllDownloads(true);
2012 57 : } else if (strcmp(aTopic, "wake_notification") == 0) {
2013 1 : PRInt32 resumeOnWakeDelay = 10000;
2014 2 : nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID);
2015 1 : if (pref)
2016 1 : (void)pref->GetIntPref(PREF_BDM_RESUMEONWAKEDELAY, &resumeOnWakeDelay);
2017 :
2018 : // Wait a little bit before trying to resume to avoid resuming when network
2019 : // connections haven't restarted yet
2020 1 : mResumeOnWakeTimer = do_CreateInstance("@mozilla.org/timer;1");
2021 1 : if (resumeOnWakeDelay >= 0 && mResumeOnWakeTimer) {
2022 1 : (void)mResumeOnWakeTimer->InitWithFuncCallback(ResumeOnWakeCallback,
2023 1 : this, resumeOnWakeDelay, nsITimer::TYPE_ONE_SHOT);
2024 : }
2025 : }
2026 56 : else if (strcmp(aTopic, NS_PRIVATE_BROWSING_REQUEST_TOPIC) == 0) {
2027 12 : if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData) &&
2028 : currDownloadCount) {
2029 : nsCOMPtr<nsISupportsPRBool> cancelDownloads =
2030 4 : do_QueryInterface(aSubject, &rv);
2031 2 : NS_ENSURE_SUCCESS(rv, rv);
2032 : ConfirmCancelDownloads(currDownloadCount, cancelDownloads,
2033 2 : NS_LITERAL_STRING("enterPrivateBrowsingCancelDownloadsAlertTitle").get(),
2034 2 : NS_LITERAL_STRING("enterPrivateBrowsingCancelDownloadsAlertMsgMultiple").get(),
2035 2 : NS_LITERAL_STRING("enterPrivateBrowsingCancelDownloadsAlertMsg").get(),
2036 6 : NS_LITERAL_STRING("dontEnterPrivateBrowsingButton").get());
2037 : }
2038 16 : else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData) &&
2039 6 : mCurrentDownloads.Count()) {
2040 : nsCOMPtr<nsISupportsPRBool> cancelDownloads =
2041 4 : do_QueryInterface(aSubject, &rv);
2042 2 : NS_ENSURE_SUCCESS(rv, rv);
2043 : ConfirmCancelDownloads(mCurrentDownloads.Count(), cancelDownloads,
2044 2 : NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertTitle").get(),
2045 2 : NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertMsgMultiple").get(),
2046 2 : NS_LITERAL_STRING("leavePrivateBrowsingCancelDownloadsAlertMsg").get(),
2047 6 : NS_LITERAL_STRING("dontLeavePrivateBrowsingButton").get());
2048 : }
2049 : }
2050 44 : else if (strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) {
2051 10 : if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData))
2052 5 : OnEnterPrivateBrowsingMode();
2053 5 : else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData))
2054 5 : OnLeavePrivateBrowsingMode();
2055 : }
2056 :
2057 162 : return NS_OK;
2058 : }
2059 :
2060 : void
2061 5 : nsDownloadManager::OnEnterPrivateBrowsingMode()
2062 : {
2063 : // Pause all downloads, and mark them to auto-resume.
2064 5 : (void)PauseAllDownloads(true);
2065 :
2066 : // Switch to using an in-memory DB
2067 5 : (void)SwitchDatabaseTypeTo(DATABASE_MEMORY);
2068 :
2069 5 : mInPrivateBrowsing = true;
2070 5 : }
2071 :
2072 : void
2073 5 : nsDownloadManager::OnLeavePrivateBrowsingMode()
2074 : {
2075 : // We can now resume all downloads that are supposed to auto-resume.
2076 5 : (void)ResumeAllDownloads(false);
2077 :
2078 : // Switch back to the on-disk DB again
2079 5 : (void)SwitchDatabaseTypeTo(DATABASE_DISK);
2080 :
2081 5 : mInPrivateBrowsing = false;
2082 5 : }
2083 :
2084 : void
2085 4 : nsDownloadManager::ConfirmCancelDownloads(PRInt32 aCount,
2086 : nsISupportsPRBool *aCancelDownloads,
2087 : const PRUnichar *aTitle,
2088 : const PRUnichar *aCancelMessageMultiple,
2089 : const PRUnichar *aCancelMessageSingle,
2090 : const PRUnichar *aDontCancelButton)
2091 : {
2092 : // If user has already dismissed quit request, then do nothing
2093 4 : bool quitRequestCancelled = false;
2094 4 : aCancelDownloads->GetData(&quitRequestCancelled);
2095 4 : if (quitRequestCancelled)
2096 0 : return;
2097 :
2098 8 : nsXPIDLString title, message, quitButton, dontQuitButton;
2099 :
2100 4 : mBundle->GetStringFromName(aTitle, getter_Copies(title));
2101 :
2102 8 : nsAutoString countString;
2103 4 : countString.AppendInt(aCount);
2104 4 : const PRUnichar *strings[1] = { countString.get() };
2105 4 : if (aCount > 1) {
2106 0 : mBundle->FormatStringFromName(aCancelMessageMultiple, strings, 1,
2107 0 : getter_Copies(message));
2108 0 : mBundle->FormatStringFromName(NS_LITERAL_STRING("cancelDownloadsOKTextMultiple").get(),
2109 0 : strings, 1, getter_Copies(quitButton));
2110 : } else {
2111 4 : mBundle->GetStringFromName(aCancelMessageSingle, getter_Copies(message));
2112 8 : mBundle->GetStringFromName(NS_LITERAL_STRING("cancelDownloadsOKText").get(),
2113 8 : getter_Copies(quitButton));
2114 : }
2115 :
2116 4 : mBundle->GetStringFromName(aDontCancelButton, getter_Copies(dontQuitButton));
2117 :
2118 : // Get Download Manager window, to be parent of alert.
2119 8 : nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
2120 8 : nsCOMPtr<nsIDOMWindow> dmWindow;
2121 4 : if (wm) {
2122 8 : wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(),
2123 8 : getter_AddRefs(dmWindow));
2124 : }
2125 :
2126 : // Show alert.
2127 8 : nsCOMPtr<nsIPromptService> prompter(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
2128 4 : if (prompter) {
2129 4 : PRInt32 flags = (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_0) + (nsIPromptService::BUTTON_TITLE_IS_STRING * nsIPromptService::BUTTON_POS_1);
2130 4 : bool nothing = false;
2131 : PRInt32 button;
2132 4 : prompter->ConfirmEx(dmWindow, title, message, flags, quitButton.get(), dontQuitButton.get(), nsnull, nsnull, ¬hing, &button);
2133 :
2134 4 : aCancelDownloads->SetData(button == 1);
2135 : }
2136 : }
2137 :
2138 : ////////////////////////////////////////////////////////////////////////////////
2139 : //// nsDownload
2140 :
2141 : NS_IMPL_CLASSINFO(nsDownload, NULL, 0, NS_DOWNLOAD_CID)
2142 2913 : NS_IMPL_ISUPPORTS4_CI(
2143 : nsDownload
2144 : , nsIDownload
2145 : , nsITransfer
2146 : , nsIWebProgressListener
2147 : , nsIWebProgressListener2
2148 63 : )
2149 :
2150 35 : nsDownload::nsDownload() : mDownloadState(nsIDownloadManager::DOWNLOAD_NOTSTARTED),
2151 : mID(0),
2152 : mPercentComplete(0),
2153 : mCurrBytes(0),
2154 : mMaxBytes(-1),
2155 : mStartTime(0),
2156 35 : mLastUpdate(PR_Now() - (PRUint32)gUpdateInterval),
2157 : mResumedAt(-1),
2158 : mSpeed(0),
2159 : mHasMultipleFiles(false),
2160 70 : mAutoResume(DONT_RESUME)
2161 : {
2162 35 : }
2163 :
2164 70 : nsDownload::~nsDownload()
2165 : {
2166 140 : }
2167 :
2168 : nsresult
2169 80 : nsDownload::SetState(DownloadState aState)
2170 : {
2171 80 : NS_ASSERTION(mDownloadState != aState,
2172 : "Trying to set the download state to what it already is set to!");
2173 :
2174 80 : PRInt16 oldState = mDownloadState;
2175 80 : mDownloadState = aState;
2176 :
2177 : // We don't want to lose access to our member variables
2178 160 : nsRefPtr<nsDownload> kungFuDeathGrip = this;
2179 :
2180 : // When the state changed listener is dispatched, queries to the database and
2181 : // the download manager api should reflect what the nsIDownload object would
2182 : // return. So, if a download is done (finished, canceled, etc.), it should
2183 : // first be removed from the current downloads. We will also have to update
2184 : // the database *before* notifying listeners. At this point, you can safely
2185 : // dispatch to the observers as well.
2186 80 : switch (aState) {
2187 : case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL:
2188 : case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY:
2189 : case nsIDownloadManager::DOWNLOAD_DIRTY:
2190 : case nsIDownloadManager::DOWNLOAD_CANCELED:
2191 : case nsIDownloadManager::DOWNLOAD_FAILED:
2192 : // Transfers are finished, so break the reference cycle
2193 11 : Finalize();
2194 11 : break;
2195 : #ifdef DOWNLOAD_SCANNER
2196 : case nsIDownloadManager::DOWNLOAD_SCANNING:
2197 : {
2198 : nsresult rv = mDownloadManager->mScanner ? mDownloadManager->mScanner->ScanDownload(this) : NS_ERROR_NOT_INITIALIZED;
2199 : // If we failed, then fall through to 'download finished'
2200 : if (NS_SUCCEEDED(rv))
2201 : break;
2202 : mDownloadState = aState = nsIDownloadManager::DOWNLOAD_FINISHED;
2203 : }
2204 : #endif
2205 : case nsIDownloadManager::DOWNLOAD_FINISHED:
2206 : {
2207 : // Do what exthandler would have done if necessary
2208 14 : nsresult rv = ExecuteDesiredAction();
2209 14 : if (NS_FAILED(rv)) {
2210 : // We've failed to execute the desired action. As a result, we should
2211 : // fail the download so the user can try again.
2212 0 : (void)FailDownload(rv, nsnull);
2213 0 : return rv;
2214 : }
2215 :
2216 : // Now that we're done with handling the download, clean it up
2217 14 : Finalize();
2218 :
2219 28 : nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID));
2220 :
2221 : // Master pref to control this function.
2222 14 : bool showTaskbarAlert = true;
2223 14 : if (pref)
2224 14 : pref->GetBoolPref(PREF_BDM_SHOWALERTONCOMPLETE, &showTaskbarAlert);
2225 :
2226 14 : if (showTaskbarAlert) {
2227 0 : PRInt32 alertInterval = 2000;
2228 0 : if (pref)
2229 0 : pref->GetIntPref(PREF_BDM_SHOWALERTINTERVAL, &alertInterval);
2230 :
2231 0 : PRInt64 alertIntervalUSec = alertInterval * PR_USEC_PER_MSEC;
2232 0 : PRInt64 goat = PR_Now() - mStartTime;
2233 0 : showTaskbarAlert = goat > alertIntervalUSec;
2234 :
2235 0 : PRInt32 size = mDownloadManager->mCurrentDownloads.Count();
2236 0 : if (showTaskbarAlert && size == 0) {
2237 : nsCOMPtr<nsIAlertsService> alerts =
2238 0 : do_GetService("@mozilla.org/alerts-service;1");
2239 0 : if (alerts) {
2240 0 : nsXPIDLString title, message;
2241 :
2242 0 : mDownloadManager->mBundle->GetStringFromName(
2243 0 : NS_LITERAL_STRING("downloadsCompleteTitle").get(),
2244 0 : getter_Copies(title));
2245 0 : mDownloadManager->mBundle->GetStringFromName(
2246 0 : NS_LITERAL_STRING("downloadsCompleteMsg").get(),
2247 0 : getter_Copies(message));
2248 :
2249 : bool removeWhenDone =
2250 0 : mDownloadManager->GetRetentionBehavior() == 0;
2251 :
2252 : // If downloads are automatically removed per the user's
2253 : // retention policy, there's no reason to make the text clickable
2254 : // because if it is, they'll click open the download manager and
2255 : // the items they downloaded will have been removed.
2256 0 : alerts->ShowAlertNotification(
2257 0 : NS_LITERAL_STRING(DOWNLOAD_MANAGER_ALERT_ICON), title,
2258 0 : message, !removeWhenDone, EmptyString(), mDownloadManager,
2259 0 : EmptyString());
2260 : }
2261 : }
2262 : }
2263 :
2264 : #if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
2265 : nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget);
2266 : nsCOMPtr<nsIFile> file;
2267 : nsAutoString path;
2268 :
2269 : if (fileURL &&
2270 : NS_SUCCEEDED(fileURL->GetFile(getter_AddRefs(file))) &&
2271 : file &&
2272 : NS_SUCCEEDED(file->GetPath(path))) {
2273 :
2274 : #ifdef XP_WIN
2275 : // On windows, add the download to the system's "recent documents"
2276 : // list, with a pref to disable.
2277 : {
2278 : bool addToRecentDocs = true;
2279 : if (pref)
2280 : pref->GetBoolPref(PREF_BDM_ADDTORECENTDOCS, &addToRecentDocs);
2281 :
2282 : if (addToRecentDocs &&
2283 : !nsDownloadManager::gDownloadManagerService->mInPrivateBrowsing) {
2284 : ::SHAddToRecentDocs(SHARD_PATHW, path.get());
2285 : }
2286 : }
2287 : #endif
2288 : #ifdef XP_MACOSX
2289 : // On OS X, make the downloads stack bounce.
2290 : CFStringRef observedObject = ::CFStringCreateWithCString(kCFAllocatorDefault,
2291 : NS_ConvertUTF16toUTF8(path).get(),
2292 : kCFStringEncodingUTF8);
2293 : CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter();
2294 : ::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"),
2295 : observedObject, NULL, TRUE);
2296 : ::CFRelease(observedObject);
2297 : #endif
2298 : #ifdef MOZ_WIDGET_ANDROID
2299 : nsCOMPtr<nsIMIMEInfo> mimeInfo;
2300 : nsCAutoString contentType;
2301 : GetMIMEInfo(getter_AddRefs(mimeInfo));
2302 :
2303 : if (mimeInfo)
2304 : mimeInfo->GetMIMEType(contentType);
2305 :
2306 : mozilla::AndroidBridge::Bridge()->ScanMedia(path, contentType);
2307 : #endif
2308 : }
2309 :
2310 : #ifdef XP_WIN
2311 : // Adjust file attributes so that by default, new files are indexed
2312 : // by desktop search services. Skip off those that land in the temp
2313 : // folder.
2314 : nsCOMPtr<nsIFile> tempDir, fileDir;
2315 : rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tempDir));
2316 : NS_ENSURE_SUCCESS(rv, rv);
2317 : (void)file->GetParent(getter_AddRefs(fileDir));
2318 :
2319 : bool isTemp = false;
2320 : if (fileDir)
2321 : (void)fileDir->Equals(tempDir, &isTemp);
2322 :
2323 : nsCOMPtr<nsILocalFileWin> localFileWin(do_QueryInterface(file));
2324 : if (!isTemp && localFileWin)
2325 : (void)localFileWin->SetFileAttributesWin(nsILocalFileWin::WFA_SEARCH_INDEXED);
2326 : #endif
2327 :
2328 : #endif
2329 : // Now remove the download if the user's retention policy is "Remove when Done"
2330 14 : if (mDownloadManager->GetRetentionBehavior() == 0)
2331 0 : mDownloadManager->RemoveDownload(mID);
2332 : }
2333 14 : break;
2334 : default:
2335 55 : break;
2336 : }
2337 :
2338 : // Before notifying the listener, we must update the database so that calls
2339 : // to it work out properly.
2340 80 : nsresult rv = UpdateDB();
2341 80 : NS_ENSURE_SUCCESS(rv, rv);
2342 :
2343 80 : mDownloadManager->NotifyListenersOnDownloadStateChange(oldState, this);
2344 :
2345 80 : switch (mDownloadState) {
2346 : case nsIDownloadManager::DOWNLOAD_DOWNLOADING:
2347 : // Only send the dl-start event to downloads that are actually starting.
2348 18 : if (oldState == nsIDownloadManager::DOWNLOAD_QUEUED)
2349 11 : mDownloadManager->SendEvent(this, "dl-start");
2350 18 : break;
2351 : case nsIDownloadManager::DOWNLOAD_FAILED:
2352 0 : mDownloadManager->SendEvent(this, "dl-failed");
2353 0 : break;
2354 : case nsIDownloadManager::DOWNLOAD_SCANNING:
2355 0 : mDownloadManager->SendEvent(this, "dl-scanning");
2356 0 : break;
2357 : case nsIDownloadManager::DOWNLOAD_FINISHED:
2358 14 : mDownloadManager->SendEvent(this, "dl-done");
2359 14 : break;
2360 : case nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL:
2361 : case nsIDownloadManager::DOWNLOAD_BLOCKED_POLICY:
2362 0 : mDownloadManager->SendEvent(this, "dl-blocked");
2363 0 : break;
2364 : case nsIDownloadManager::DOWNLOAD_DIRTY:
2365 0 : mDownloadManager->SendEvent(this, "dl-dirty");
2366 0 : break;
2367 : case nsIDownloadManager::DOWNLOAD_CANCELED:
2368 12 : mDownloadManager->SendEvent(this, "dl-cancel");
2369 12 : break;
2370 : default:
2371 36 : break;
2372 : }
2373 80 : return NS_OK;
2374 : }
2375 :
2376 : ////////////////////////////////////////////////////////////////////////////////
2377 : //// nsIWebProgressListener2
2378 :
2379 : NS_IMETHODIMP
2380 60 : nsDownload::OnProgressChange64(nsIWebProgress *aWebProgress,
2381 : nsIRequest *aRequest,
2382 : PRInt64 aCurSelfProgress,
2383 : PRInt64 aMaxSelfProgress,
2384 : PRInt64 aCurTotalProgress,
2385 : PRInt64 aMaxTotalProgress)
2386 : {
2387 60 : if (!mRequest)
2388 17 : mRequest = aRequest; // used for pause/resume
2389 :
2390 60 : if (mDownloadState == nsIDownloadManager::DOWNLOAD_QUEUED) {
2391 : // Obtain the referrer
2392 : nsresult rv;
2393 34 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
2394 34 : nsCOMPtr<nsIURI> referrer = mReferrer;
2395 17 : if (channel)
2396 17 : (void)NS_GetReferrerFromChannel(channel, getter_AddRefs(mReferrer));
2397 :
2398 : // Restore the original referrer if the new one isn't useful
2399 17 : if (!mReferrer)
2400 17 : mReferrer = referrer;
2401 :
2402 : // If we have a MIME info, we know that exthandler has already added this to
2403 : // the history, but if we do not, we'll have to add it ourselves.
2404 17 : if (!mMIMEInfo) {
2405 : nsCOMPtr<nsIDownloadHistory> dh =
2406 22 : do_GetService(NS_DOWNLOADHISTORY_CONTRACTID);
2407 11 : if (dh)
2408 11 : (void)dh->AddDownload(mSource, mReferrer, mStartTime, mTarget);
2409 : }
2410 :
2411 : // Fetch the entityID, but if we can't get it, don't panic (non-resumable)
2412 34 : nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(aRequest));
2413 17 : if (resumableChannel)
2414 17 : (void)resumableChannel->GetEntityID(mEntityID);
2415 :
2416 : // Before we update the state and dispatch state notifications, we want to
2417 : // ensure that we have the correct state for this download with regards to
2418 : // its percent completion and size.
2419 17 : SetProgressBytes(0, aMaxTotalProgress);
2420 :
2421 : // Update the state and the database
2422 17 : rv = SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
2423 17 : NS_ENSURE_SUCCESS(rv, rv);
2424 : }
2425 :
2426 : // filter notifications since they come in so frequently
2427 60 : PRTime now = PR_Now();
2428 60 : PRIntervalTime delta = now - mLastUpdate;
2429 60 : if (delta < gUpdateInterval)
2430 53 : return NS_OK;
2431 :
2432 7 : mLastUpdate = now;
2433 :
2434 : // Calculate the speed using the elapsed delta time and bytes downloaded
2435 : // during that time for more accuracy.
2436 7 : double elapsedSecs = double(delta) / PR_USEC_PER_SEC;
2437 7 : if (elapsedSecs > 0) {
2438 7 : double speed = double(aCurTotalProgress - mCurrBytes) / elapsedSecs;
2439 7 : if (mCurrBytes == 0) {
2440 7 : mSpeed = speed;
2441 : } else {
2442 : // Calculate 'smoothed average' of 10 readings.
2443 0 : mSpeed = mSpeed * 0.9 + speed * 0.1;
2444 : }
2445 : }
2446 :
2447 7 : SetProgressBytes(aCurTotalProgress, aMaxTotalProgress);
2448 :
2449 : // Report to the listener our real sizes
2450 : PRInt64 currBytes, maxBytes;
2451 7 : (void)GetAmountTransferred(&currBytes);
2452 7 : (void)GetSize(&maxBytes);
2453 : mDownloadManager->NotifyListenersOnProgressChange(
2454 7 : aWebProgress, aRequest, currBytes, maxBytes, currBytes, maxBytes, this);
2455 :
2456 : // If the maximums are different, then there must be more than one file
2457 7 : if (aMaxSelfProgress != aMaxTotalProgress)
2458 0 : mHasMultipleFiles = true;
2459 :
2460 7 : return NS_OK;
2461 : }
2462 :
2463 : NS_IMETHODIMP
2464 0 : nsDownload::OnRefreshAttempted(nsIWebProgress *aWebProgress,
2465 : nsIURI *aUri,
2466 : PRInt32 aDelay,
2467 : bool aSameUri,
2468 : bool *allowRefresh)
2469 : {
2470 0 : *allowRefresh = true;
2471 0 : return NS_OK;
2472 : }
2473 :
2474 : ////////////////////////////////////////////////////////////////////////////////
2475 : //// nsIWebProgressListener
2476 :
2477 : NS_IMETHODIMP
2478 0 : nsDownload::OnProgressChange(nsIWebProgress *aWebProgress,
2479 : nsIRequest *aRequest,
2480 : PRInt32 aCurSelfProgress,
2481 : PRInt32 aMaxSelfProgress,
2482 : PRInt32 aCurTotalProgress,
2483 : PRInt32 aMaxTotalProgress)
2484 : {
2485 : return OnProgressChange64(aWebProgress, aRequest,
2486 : aCurSelfProgress, aMaxSelfProgress,
2487 0 : aCurTotalProgress, aMaxTotalProgress);
2488 : }
2489 :
2490 : NS_IMETHODIMP
2491 0 : nsDownload::OnLocationChange(nsIWebProgress *aWebProgress,
2492 : nsIRequest *aRequest, nsIURI *aLocation,
2493 : PRUint32 aFlags)
2494 : {
2495 0 : return NS_OK;
2496 : }
2497 :
2498 : NS_IMETHODIMP
2499 0 : nsDownload::OnStatusChange(nsIWebProgress *aWebProgress,
2500 : nsIRequest *aRequest, nsresult aStatus,
2501 : const PRUnichar *aMessage)
2502 : {
2503 0 : if (NS_FAILED(aStatus))
2504 0 : return FailDownload(aStatus, aMessage);
2505 0 : return NS_OK;
2506 : }
2507 :
2508 : NS_IMETHODIMP
2509 54 : nsDownload::OnStateChange(nsIWebProgress *aWebProgress,
2510 : nsIRequest *aRequest, PRUint32 aStateFlags,
2511 : nsresult aStatus)
2512 : {
2513 : // We don't want to lose access to our member variables
2514 108 : nsRefPtr<nsDownload> kungFuDeathGrip = this;
2515 :
2516 : // Check if we're starting a request; the NETWORK flag is necessary to not
2517 : // pick up the START of *each* file but only for the whole request
2518 54 : if ((aStateFlags & STATE_START) && (aStateFlags & STATE_IS_NETWORK)) {
2519 : nsresult rv;
2520 58 : nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
2521 29 : if (NS_SUCCEEDED(rv)) {
2522 : PRUint32 status;
2523 26 : rv = channel->GetResponseStatus(&status);
2524 : // HTTP 450 - Blocked by parental control proxies
2525 26 : if (NS_SUCCEEDED(rv) && status == 450) {
2526 : // Cancel using the provided object
2527 0 : (void)Cancel();
2528 :
2529 : // Fail the download
2530 0 : (void)SetState(nsIDownloadManager::DOWNLOAD_BLOCKED_PARENTAL);
2531 : }
2532 29 : }
2533 39 : } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK) &&
2534 14 : IsFinishable()) {
2535 : // We got both STOP and NETWORK so that means the whole request is done
2536 : // (and not just a single file if there are multiple files)
2537 14 : if (NS_SUCCEEDED(aStatus)) {
2538 : // We can't completely trust the bytes we've added up because we might be
2539 : // missing on some/all of the progress updates (especially from cache).
2540 : // Our best bet is the file itself, but if for some reason it's gone or
2541 : // if we have multiple files, the next best is what we've calculated.
2542 : PRInt64 fileSize;
2543 28 : nsCOMPtr<nsILocalFile> file;
2544 : // We need a nsIFile clone to deal with file size caching issues. :(
2545 28 : nsCOMPtr<nsIFile> clone;
2546 98 : if (!mHasMultipleFiles &&
2547 42 : NS_SUCCEEDED(GetTargetFile(getter_AddRefs(file))) &&
2548 42 : NS_SUCCEEDED(file->Clone(getter_AddRefs(clone))) &&
2549 14 : NS_SUCCEEDED(clone->GetFileSize(&fileSize)) && fileSize > 0) {
2550 13 : mCurrBytes = mMaxBytes = fileSize;
2551 :
2552 : // If we resumed, keep the fact that we did and fix size calculations
2553 13 : if (WasResumed())
2554 4 : mResumedAt = 0;
2555 1 : } else if (mMaxBytes == -1) {
2556 0 : mMaxBytes = mCurrBytes;
2557 : } else {
2558 1 : mCurrBytes = mMaxBytes;
2559 : }
2560 :
2561 14 : mPercentComplete = 100;
2562 14 : mLastUpdate = PR_Now();
2563 :
2564 : #ifdef DOWNLOAD_SCANNER
2565 : bool scan = true;
2566 : nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
2567 : if (prefs)
2568 : (void)prefs->GetBoolPref(PREF_BDM_SCANWHENDONE, &scan);
2569 :
2570 : if (scan)
2571 : (void)SetState(nsIDownloadManager::DOWNLOAD_SCANNING);
2572 : else
2573 : (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED);
2574 : #else
2575 14 : (void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED);
2576 : #endif
2577 : } else {
2578 : // We failed for some unknown reason -- fail with a generic message
2579 0 : (void)FailDownload(aStatus, nsnull);
2580 : }
2581 : }
2582 :
2583 : mDownloadManager->NotifyListenersOnStateChange(aWebProgress, aRequest,
2584 54 : aStateFlags, aStatus, this);
2585 54 : return NS_OK;
2586 : }
2587 :
2588 : NS_IMETHODIMP
2589 0 : nsDownload::OnSecurityChange(nsIWebProgress *aWebProgress,
2590 : nsIRequest *aRequest, PRUint32 aState)
2591 : {
2592 0 : return NS_OK;
2593 : }
2594 :
2595 : ////////////////////////////////////////////////////////////////////////////////
2596 : //// nsIDownload
2597 :
2598 : NS_IMETHODIMP
2599 0 : nsDownload::Init(nsIURI *aSource,
2600 : nsIURI *aTarget,
2601 : const nsAString& aDisplayName,
2602 : nsIMIMEInfo *aMIMEInfo,
2603 : PRTime aStartTime,
2604 : nsILocalFile *aTempFile,
2605 : nsICancelable *aCancelable)
2606 : {
2607 0 : NS_WARNING("Huh...how did we get here?!");
2608 0 : return NS_OK;
2609 : }
2610 :
2611 : NS_IMETHODIMP
2612 214 : nsDownload::GetState(PRInt16 *aState)
2613 : {
2614 214 : *aState = mDownloadState;
2615 214 : return NS_OK;
2616 : }
2617 :
2618 : NS_IMETHODIMP
2619 11 : nsDownload::GetDisplayName(nsAString &aDisplayName)
2620 : {
2621 11 : aDisplayName = mDisplayName;
2622 11 : return NS_OK;
2623 : }
2624 :
2625 : NS_IMETHODIMP
2626 0 : nsDownload::GetCancelable(nsICancelable **aCancelable)
2627 : {
2628 0 : *aCancelable = mCancelable;
2629 0 : NS_IF_ADDREF(*aCancelable);
2630 0 : return NS_OK;
2631 : }
2632 :
2633 : NS_IMETHODIMP
2634 0 : nsDownload::GetTarget(nsIURI **aTarget)
2635 : {
2636 0 : *aTarget = mTarget;
2637 0 : NS_IF_ADDREF(*aTarget);
2638 0 : return NS_OK;
2639 : }
2640 :
2641 : NS_IMETHODIMP
2642 55 : nsDownload::GetSource(nsIURI **aSource)
2643 : {
2644 55 : *aSource = mSource;
2645 55 : NS_IF_ADDREF(*aSource);
2646 55 : return NS_OK;
2647 : }
2648 :
2649 : NS_IMETHODIMP
2650 0 : nsDownload::GetStartTime(PRInt64 *aStartTime)
2651 : {
2652 0 : *aStartTime = mStartTime;
2653 0 : return NS_OK;
2654 : }
2655 :
2656 : NS_IMETHODIMP
2657 0 : nsDownload::GetPercentComplete(PRInt32 *aPercentComplete)
2658 : {
2659 0 : *aPercentComplete = mPercentComplete;
2660 0 : return NS_OK;
2661 : }
2662 :
2663 : NS_IMETHODIMP
2664 136 : nsDownload::GetAmountTransferred(PRInt64 *aAmountTransferred)
2665 : {
2666 136 : *aAmountTransferred = mCurrBytes + (WasResumed() ? mResumedAt : 0);
2667 136 : return NS_OK;
2668 : }
2669 :
2670 : NS_IMETHODIMP
2671 143 : nsDownload::GetSize(PRInt64 *aSize)
2672 : {
2673 143 : *aSize = mMaxBytes + (WasResumed() && mMaxBytes != -1 ? mResumedAt : 0);
2674 143 : return NS_OK;
2675 : }
2676 :
2677 : NS_IMETHODIMP
2678 0 : nsDownload::GetMIMEInfo(nsIMIMEInfo **aMIMEInfo)
2679 : {
2680 0 : *aMIMEInfo = mMIMEInfo;
2681 0 : NS_IF_ADDREF(*aMIMEInfo);
2682 0 : return NS_OK;
2683 : }
2684 :
2685 : NS_IMETHODIMP
2686 97 : nsDownload::GetTargetFile(nsILocalFile **aTargetFile)
2687 : {
2688 : nsresult rv;
2689 :
2690 194 : nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget, &rv);
2691 97 : if (NS_FAILED(rv)) return rv;
2692 :
2693 194 : nsCOMPtr<nsIFile> file;
2694 97 : rv = fileURL->GetFile(getter_AddRefs(file));
2695 97 : if (NS_SUCCEEDED(rv))
2696 97 : rv = CallQueryInterface(file, aTargetFile);
2697 97 : return rv;
2698 : }
2699 :
2700 : NS_IMETHODIMP
2701 0 : nsDownload::GetSpeed(double *aSpeed)
2702 : {
2703 0 : *aSpeed = mSpeed;
2704 0 : return NS_OK;
2705 : }
2706 :
2707 : NS_IMETHODIMP
2708 57 : nsDownload::GetId(PRUint32 *aId)
2709 : {
2710 57 : *aId = mID;
2711 57 : return NS_OK;
2712 : }
2713 :
2714 : NS_IMETHODIMP
2715 2 : nsDownload::GetReferrer(nsIURI **referrer)
2716 : {
2717 2 : NS_IF_ADDREF(*referrer = mReferrer);
2718 2 : return NS_OK;
2719 : }
2720 :
2721 : NS_IMETHODIMP
2722 4 : nsDownload::GetResumable(bool *resumable)
2723 : {
2724 4 : *resumable = IsResumable();
2725 4 : return NS_OK;
2726 : }
2727 :
2728 : ////////////////////////////////////////////////////////////////////////////////
2729 : //// nsDownload Helper Functions
2730 :
2731 : void
2732 25 : nsDownload::Finalize()
2733 : {
2734 : // We're stopping, so break the cycle we created at download start
2735 25 : mCancelable = nsnull;
2736 :
2737 : // Reset values that aren't needed anymore, so the DB can be updated as well
2738 25 : mEntityID.Truncate();
2739 25 : mTempFile = nsnull;
2740 :
2741 : // Remove ourself from the active downloads
2742 25 : (void)mDownloadManager->mCurrentDownloads.RemoveObject(this);
2743 :
2744 : // Make sure we do not automatically resume
2745 25 : mAutoResume = DONT_RESUME;
2746 25 : }
2747 :
2748 : nsresult
2749 14 : nsDownload::ExecuteDesiredAction()
2750 : {
2751 : // If we have a temp file and we have resumed, we have to do what the
2752 : // external helper app service would have done.
2753 14 : if (!mTempFile || !WasResumed())
2754 13 : return NS_OK;
2755 :
2756 : // We need to bail if for some reason the temp file got removed
2757 : bool fileExists;
2758 1 : if (NS_FAILED(mTempFile->Exists(&fileExists)) || !fileExists)
2759 0 : return NS_ERROR_FILE_NOT_FOUND;
2760 :
2761 : // Assume an unknown action is save to disk
2762 1 : nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
2763 1 : if (mMIMEInfo) {
2764 1 : nsresult rv = mMIMEInfo->GetPreferredAction(&action);
2765 1 : NS_ENSURE_SUCCESS(rv, rv);
2766 : }
2767 :
2768 1 : nsresult retVal = NS_OK;
2769 1 : switch (action) {
2770 : case nsIMIMEInfo::saveToDisk:
2771 : // Move the file to the proper location
2772 1 : retVal = MoveTempToTarget();
2773 1 : break;
2774 : case nsIMIMEInfo::useHelperApp:
2775 : case nsIMIMEInfo::useSystemDefault:
2776 : // For these cases we have to move the file to the target location and
2777 : // open with the appropriate application
2778 0 : retVal = OpenWithApplication();
2779 0 : break;
2780 : default:
2781 0 : break;
2782 : }
2783 :
2784 1 : return retVal;
2785 : }
2786 :
2787 : nsresult
2788 1 : nsDownload::MoveTempToTarget()
2789 : {
2790 2 : nsCOMPtr<nsILocalFile> target;
2791 1 : nsresult rv = GetTargetFile(getter_AddRefs(target));
2792 1 : NS_ENSURE_SUCCESS(rv, rv);
2793 :
2794 : // MoveTo will fail if the file already exists, but we've already obtained
2795 : // confirmation from the user that this is OK, so remove it if it exists.
2796 : bool fileExists;
2797 1 : if (NS_SUCCEEDED(target->Exists(&fileExists)) && fileExists) {
2798 0 : rv = target->Remove(false);
2799 0 : NS_ENSURE_SUCCESS(rv, rv);
2800 : }
2801 :
2802 : // Extract the new leaf name from the file location
2803 2 : nsAutoString fileName;
2804 1 : rv = target->GetLeafName(fileName);
2805 1 : NS_ENSURE_SUCCESS(rv, rv);
2806 2 : nsCOMPtr<nsIFile> dir;
2807 1 : rv = target->GetParent(getter_AddRefs(dir));
2808 1 : NS_ENSURE_SUCCESS(rv, rv);
2809 1 : rv = mTempFile->MoveTo(dir, fileName);
2810 1 : NS_ENSURE_SUCCESS(rv, rv);
2811 :
2812 1 : return NS_OK;
2813 : }
2814 :
2815 : nsresult
2816 0 : nsDownload::OpenWithApplication()
2817 : {
2818 : // First move the temporary file to the target location
2819 0 : nsCOMPtr<nsILocalFile> target;
2820 0 : nsresult rv = GetTargetFile(getter_AddRefs(target));
2821 0 : NS_ENSURE_SUCCESS(rv, rv);
2822 :
2823 : // Move the temporary file to the target location
2824 0 : rv = MoveTempToTarget();
2825 0 : NS_ENSURE_SUCCESS(rv, rv);
2826 :
2827 : // We do not verify the return value here because, irrespective of success
2828 : // or failure of the method, the deletion of temp file has to take place, as
2829 : // per the corresponding preference. But we store this separately as this is
2830 : // what we ultimately return from this function.
2831 0 : nsresult retVal = mMIMEInfo->LaunchWithFile(target);
2832 :
2833 : bool deleteTempFileOnExit;
2834 0 : nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
2835 0 : if (!prefs || NS_FAILED(prefs->GetBoolPref(PREF_BH_DELETETEMPFILEONEXIT,
2836 : &deleteTempFileOnExit))) {
2837 : // No prefservice or no pref set; use default value
2838 : #if !defined(XP_MACOSX)
2839 : // Mac users have been very verbal about temp files being deleted on
2840 : // app exit - they don't like it - but we'll continue to do this on
2841 : // other platforms for now.
2842 0 : deleteTempFileOnExit = true;
2843 : #else
2844 : deleteTempFileOnExit = false;
2845 : #endif
2846 : }
2847 :
2848 : // Always schedule files to be deleted at the end of the private browsing
2849 : // mode, regardless of the value of the pref.
2850 0 : if (deleteTempFileOnExit ||
2851 : nsDownloadManager::gDownloadManagerService->mInPrivateBrowsing) {
2852 : // Use the ExternalHelperAppService to push the temporary file to the list
2853 : // of files to be deleted on exit.
2854 : nsCOMPtr<nsPIExternalAppLauncher> appLauncher(do_GetService
2855 0 : (NS_EXTERNALHELPERAPPSERVICE_CONTRACTID));
2856 :
2857 : // Even if we are unable to get this service we return the result
2858 : // of LaunchWithFile() which makes more sense.
2859 0 : if (appLauncher)
2860 0 : (void)appLauncher->DeleteTemporaryFileOnExit(target);
2861 : }
2862 :
2863 0 : return retVal;
2864 : }
2865 :
2866 : void
2867 22 : nsDownload::SetStartTime(PRInt64 aStartTime)
2868 : {
2869 22 : mStartTime = aStartTime;
2870 22 : mLastUpdate = aStartTime;
2871 22 : }
2872 :
2873 : void
2874 48 : nsDownload::SetProgressBytes(PRInt64 aCurrBytes, PRInt64 aMaxBytes)
2875 : {
2876 48 : mCurrBytes = aCurrBytes;
2877 48 : mMaxBytes = aMaxBytes;
2878 :
2879 : // Get the real bytes that include resume position
2880 : PRInt64 currBytes, maxBytes;
2881 48 : (void)GetAmountTransferred(&currBytes);
2882 48 : (void)GetSize(&maxBytes);
2883 :
2884 48 : if (currBytes == maxBytes)
2885 11 : mPercentComplete = 100;
2886 37 : else if (maxBytes <= 0)
2887 11 : mPercentComplete = -1;
2888 : else
2889 26 : mPercentComplete = (PRInt32)((PRFloat64)currBytes / maxBytes * 100 + .5);
2890 48 : }
2891 :
2892 : nsresult
2893 11 : nsDownload::Pause()
2894 : {
2895 11 : if (!IsResumable())
2896 2 : return NS_ERROR_UNEXPECTED;
2897 :
2898 9 : nsresult rv = Cancel();
2899 9 : NS_ENSURE_SUCCESS(rv, rv);
2900 :
2901 9 : return SetState(nsIDownloadManager::DOWNLOAD_PAUSED);
2902 : }
2903 :
2904 : nsresult
2905 20 : nsDownload::Cancel()
2906 : {
2907 20 : nsresult rv = NS_OK;
2908 20 : if (mCancelable) {
2909 15 : rv = mCancelable->Cancel(NS_BINDING_ABORTED);
2910 : // we're done with this, so break the cycle
2911 15 : mCancelable = nsnull;
2912 : }
2913 :
2914 20 : return rv;
2915 : }
2916 :
2917 : nsresult
2918 7 : nsDownload::Resume()
2919 : {
2920 7 : if (!IsPaused() || !IsResumable())
2921 0 : return NS_ERROR_UNEXPECTED;
2922 :
2923 : nsresult rv;
2924 : nsCOMPtr<nsIWebBrowserPersist> wbp =
2925 14 : do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
2926 7 : NS_ENSURE_SUCCESS(rv, rv);
2927 :
2928 7 : rv = wbp->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_APPEND_TO_FILE |
2929 7 : nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
2930 7 : NS_ENSURE_SUCCESS(rv, rv);
2931 :
2932 : // Create a new channel for the source URI
2933 14 : nsCOMPtr<nsIChannel> channel;
2934 14 : nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(wbp));
2935 7 : rv = NS_NewChannel(getter_AddRefs(channel), mSource, nsnull, nsnull, ir);
2936 7 : NS_ENSURE_SUCCESS(rv, rv);
2937 :
2938 : // Make sure we can get a file, either the temporary or the real target, for
2939 : // both purposes of file size and a target to write to
2940 14 : nsCOMPtr<nsILocalFile> targetLocalFile(mTempFile);
2941 7 : if (!targetLocalFile) {
2942 5 : rv = GetTargetFile(getter_AddRefs(targetLocalFile));
2943 5 : NS_ENSURE_SUCCESS(rv, rv);
2944 : }
2945 :
2946 : // Get the file size to be used as an offset, but if anything goes wrong
2947 : // along the way, we'll silently restart at 0.
2948 : PRInt64 fileSize;
2949 : // We need a nsIFile clone to deal with file size caching issues. :(
2950 14 : nsCOMPtr<nsIFile> clone;
2951 14 : if (NS_FAILED(targetLocalFile->Clone(getter_AddRefs(clone))) ||
2952 7 : NS_FAILED(clone->GetFileSize(&fileSize)))
2953 4 : fileSize = 0;
2954 :
2955 : // Set the channel to resume at the right position along with the entityID
2956 14 : nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(channel));
2957 7 : if (!resumableChannel)
2958 0 : return NS_ERROR_UNEXPECTED;
2959 7 : rv = resumableChannel->ResumeAt(fileSize, mEntityID);
2960 7 : NS_ENSURE_SUCCESS(rv, rv);
2961 :
2962 : // If we know the max size, we know what it should be when resuming
2963 : PRInt64 maxBytes;
2964 7 : GetSize(&maxBytes);
2965 7 : SetProgressBytes(0, maxBytes != -1 ? maxBytes - fileSize : -1);
2966 : // Track where we resumed because progress notifications restart at 0
2967 7 : mResumedAt = fileSize;
2968 :
2969 : // Set the referrer
2970 7 : if (mReferrer) {
2971 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
2972 0 : if (httpChannel) {
2973 0 : rv = httpChannel->SetReferrer(mReferrer);
2974 0 : NS_ENSURE_SUCCESS(rv, rv);
2975 : }
2976 : }
2977 :
2978 : // Creates a cycle that will be broken when the download finishes
2979 7 : mCancelable = wbp;
2980 7 : (void)wbp->SetProgressListener(this);
2981 :
2982 : // Save the channel using nsIWBP
2983 7 : rv = wbp->SaveChannel(channel, targetLocalFile);
2984 7 : if (NS_FAILED(rv)) {
2985 0 : mCancelable = nsnull;
2986 0 : (void)wbp->SetProgressListener(nsnull);
2987 0 : return rv;
2988 : }
2989 :
2990 7 : return SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
2991 : }
2992 :
2993 : bool
2994 32 : nsDownload::IsPaused()
2995 : {
2996 32 : return mDownloadState == nsIDownloadManager::DOWNLOAD_PAUSED;
2997 : }
2998 :
2999 : bool
3000 41 : nsDownload::IsResumable()
3001 : {
3002 41 : return !mEntityID.IsEmpty();
3003 : }
3004 :
3005 : bool
3006 295 : nsDownload::WasResumed()
3007 : {
3008 295 : return mResumedAt != -1;
3009 : }
3010 :
3011 : bool
3012 10 : nsDownload::ShouldAutoResume()
3013 : {
3014 10 : return mAutoResume == AUTO_RESUME;
3015 : }
3016 :
3017 : bool
3018 14 : nsDownload::IsFinishable()
3019 : {
3020 : return mDownloadState == nsIDownloadManager::DOWNLOAD_NOTSTARTED ||
3021 : mDownloadState == nsIDownloadManager::DOWNLOAD_QUEUED ||
3022 14 : mDownloadState == nsIDownloadManager::DOWNLOAD_DOWNLOADING;
3023 : }
3024 :
3025 : bool
3026 11 : nsDownload::IsFinished()
3027 : {
3028 11 : return mDownloadState == nsIDownloadManager::DOWNLOAD_FINISHED;
3029 : }
3030 :
3031 : nsresult
3032 80 : nsDownload::UpdateDB()
3033 : {
3034 80 : NS_ASSERTION(mID, "Download ID is stored as zero. This is bad!");
3035 80 : NS_ASSERTION(mDownloadManager, "Egads! We have no download manager!");
3036 :
3037 80 : mozIStorageStatement *stmt = mDownloadManager->mUpdateDownloadStatement;
3038 :
3039 160 : nsAutoString tempPath;
3040 80 : if (mTempFile)
3041 17 : (void)mTempFile->GetPath(tempPath);
3042 80 : nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("tempPath"), tempPath);
3043 :
3044 80 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("startTime"), mStartTime);
3045 80 : NS_ENSURE_SUCCESS(rv, rv);
3046 :
3047 80 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("endTime"), mLastUpdate);
3048 80 : NS_ENSURE_SUCCESS(rv, rv);
3049 :
3050 80 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("state"), mDownloadState);
3051 80 : NS_ENSURE_SUCCESS(rv, rv);
3052 :
3053 80 : if (mReferrer) {
3054 6 : nsCAutoString referrer;
3055 3 : rv = mReferrer->GetSpec(referrer);
3056 3 : NS_ENSURE_SUCCESS(rv, rv);
3057 6 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("referrer"), referrer);
3058 : } else {
3059 77 : rv = stmt->BindNullByName(NS_LITERAL_CSTRING("referrer"));
3060 : }
3061 80 : NS_ENSURE_SUCCESS(rv, rv);
3062 :
3063 80 : rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("entityID"), mEntityID);
3064 80 : NS_ENSURE_SUCCESS(rv, rv);
3065 :
3066 : PRInt64 currBytes;
3067 80 : (void)GetAmountTransferred(&currBytes);
3068 80 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("currBytes"), currBytes);
3069 80 : NS_ENSURE_SUCCESS(rv, rv);
3070 :
3071 : PRInt64 maxBytes;
3072 80 : (void)GetSize(&maxBytes);
3073 80 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("maxBytes"), maxBytes);
3074 80 : NS_ENSURE_SUCCESS(rv, rv);
3075 :
3076 80 : rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("autoResume"), mAutoResume);
3077 80 : NS_ENSURE_SUCCESS(rv, rv);
3078 :
3079 80 : rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mID);
3080 80 : NS_ENSURE_SUCCESS(rv, rv);
3081 :
3082 80 : return stmt->Execute();
3083 : }
3084 :
3085 : nsresult
3086 0 : nsDownload::FailDownload(nsresult aStatus, const PRUnichar *aMessage)
3087 : {
3088 : // Grab the bundle before potentially losing our member variables
3089 0 : nsCOMPtr<nsIStringBundle> bundle = mDownloadManager->mBundle;
3090 :
3091 0 : (void)SetState(nsIDownloadManager::DOWNLOAD_FAILED);
3092 :
3093 : // Get title for alert.
3094 0 : nsXPIDLString title;
3095 0 : nsresult rv = bundle->GetStringFromName(
3096 0 : NS_LITERAL_STRING("downloadErrorAlertTitle").get(), getter_Copies(title));
3097 0 : NS_ENSURE_SUCCESS(rv, rv);
3098 :
3099 : // Get a generic message if we weren't supplied one
3100 0 : nsXPIDLString message;
3101 0 : message = aMessage;
3102 0 : if (message.IsEmpty()) {
3103 0 : rv = bundle->GetStringFromName(
3104 0 : NS_LITERAL_STRING("downloadErrorGeneric").get(), getter_Copies(message));
3105 0 : NS_ENSURE_SUCCESS(rv, rv);
3106 : }
3107 :
3108 : // Get Download Manager window to be parent of alert
3109 : nsCOMPtr<nsIWindowMediator> wm =
3110 0 : do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
3111 0 : NS_ENSURE_SUCCESS(rv, rv);
3112 0 : nsCOMPtr<nsIDOMWindow> dmWindow;
3113 0 : rv = wm->GetMostRecentWindow(NS_LITERAL_STRING("Download:Manager").get(),
3114 0 : getter_AddRefs(dmWindow));
3115 0 : NS_ENSURE_SUCCESS(rv, rv);
3116 :
3117 : // Show alert
3118 : nsCOMPtr<nsIPromptService> prompter =
3119 0 : do_GetService("@mozilla.org/embedcomp/prompt-service;1", &rv);
3120 0 : NS_ENSURE_SUCCESS(rv, rv);
3121 0 : return prompter->Alert(dmWindow, title, message);
3122 : }
|