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