1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/download/download_file_manager.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h" 8201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#include "base/logging.h" 93345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/stl_util-inl.h" 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/task.h" 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "build/build_config.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/download/download_manager.h" 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/download/download_util.h" 153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/history/download_create_info.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/net/chrome_url_request_context.h" 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/platform_util.h" 1821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h" 1972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/safe_browsing/safe_browsing_service.h" 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/tab_contents/tab_util.h" 21dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h" 22dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/resource_dispatcher_host.h" 23dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h" 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "googleurl/src/gurl.h" 253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "net/base/io_buffer.h" 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Throttle updates to the UI thread so that a fast moving download doesn't 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// cause it to become unresponsive (in milliseconds). 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kUpdatePeriodMs = 500; 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 333345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickDownloadManager* DownloadManagerForRenderViewHost(int render_process_id, 343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick int render_view_id) { 35731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick TabContents* contents = tab_util::GetTabContentsByID(render_process_id, 383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick render_view_id); 393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (contents) { 403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick Profile* profile = contents->profile(); 413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (profile) 423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return profile->GetDownloadManager(); 433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return NULL; 463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochDownloadFileManager::DownloadFileManager(ResourceDispatcherHost* rdh) 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : next_id_(0), 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch resource_dispatcher_host_(rdh) { 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochDownloadFileManager::~DownloadFileManager() { 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(downloads_.empty()); 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadFileManager::Shutdown() { 60731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 61731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 62731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::FILE, FROM_HERE, 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableMethod(this, &DownloadFileManager::OnShutdown)); 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadFileManager::OnShutdown() { 67731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick StopUpdateTimer(); 693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick STLDeleteValues(&downloads_); 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid DownloadFileManager::CreateDownloadFile(DownloadCreateInfo* info, 7372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DownloadManager* download_manager, 7472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen bool get_hash) { 75201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch VLOG(20) << __FUNCTION__ << "()" << " info = " << info->DebugString(); 76731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen scoped_ptr<DownloadFile> 7972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen download_file(new DownloadFile(info, download_manager)); 8072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!download_file->Initialize(get_hash)) { 81731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 82731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::IO, FROM_HERE, 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableFunction(&download_util::CancelDownloadRequest, 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch resource_dispatcher_host_, 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch info->child_id, 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch info->request_id)); 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delete info; 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DCHECK(GetDownloadFile(info->download_id) == NULL); 923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick downloads_[info->download_id] = download_file.release(); 933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // TODO(phajdan.jr): fix the duplication of path info below. 943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick info->path = info->save_info.file_path; 953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // The file is now ready, we can un-pause the request and start saving data. 97731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 98731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::IO, FROM_HERE, 993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick NewRunnableMethod(this, &DownloadFileManager::ResumeDownloadRequest, 1003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick info->child_id, info->request_id)); 1013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StartUpdateTimer(); 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 104731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 105731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::UI, FROM_HERE, 1063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick NewRunnableMethod(download_manager, 1073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick &DownloadManager::StartDownload, info)); 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 1103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid DownloadFileManager::ResumeDownloadRequest(int child_id, int request_id) { 111731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 1123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 1133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // This balances the pause in DownloadResourceHandler::OnResponseStarted. 1143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick resource_dispatcher_host_->PauseRequest(child_id, request_id, false); 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochDownloadFile* DownloadFileManager::GetDownloadFile(int id) { 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DownloadFileMap::iterator it = downloads_.find(id); 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return it == downloads_.end() ? NULL : it->second; 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadFileManager::StartUpdateTimer() { 123731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!update_timer_.IsRunning()) { 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdatePeriodMs), 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this, &DownloadFileManager::UpdateInProgressDownloads); 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadFileManager::StopUpdateTimer() { 131731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch update_timer_.Stop(); 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadFileManager::UpdateInProgressDownloads() { 136731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 1373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick for (DownloadFileMap::iterator i = downloads_.begin(); 1383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick i != downloads_.end(); ++i) { 1393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick int id = i->first; 1403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DownloadFile* download_file = i->second; 1413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DownloadManager* manager = download_file->GetDownloadManager(); 1423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (manager) { 143731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 1443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick NewRunnableMethod(manager, &DownloadManager::UpdateDownload, 1453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick id, download_file->bytes_so_far())); 1463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Called on the IO thread once the ResourceDispatcherHost has decided that a 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// request is a download. 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint DownloadFileManager::GetNextId() { 153731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return next_id_++; 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadFileManager::StartDownload(DownloadCreateInfo* info) { 158731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(info); 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 1613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DownloadManager* manager = DownloadManagerForRenderViewHost( 1623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick info->child_id, info->render_view_id); 1633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!manager) { 164731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 165731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::IO, FROM_HERE, 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableFunction(&download_util::CancelDownloadRequest, 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch resource_dispatcher_host_, 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch info->child_id, 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch info->request_id)); 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delete info; 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 17421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen manager->CreateDownloadItem(info); 17521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen 17672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen bool hash_needed = resource_dispatcher_host_->safe_browsing_service()-> 17772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DownloadBinHashNeeded(); 17872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 179731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 1803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick NewRunnableMethod(this, &DownloadFileManager::CreateDownloadFile, 18172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen info, make_scoped_refptr(manager), hash_needed)); 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// We don't forward an update to the UI thread here, since we want to throttle 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// the UI update rate via a periodic timer. If the user has cancelled the 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// download (in the UI thread), we may receive a few more updates before the IO 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// thread gets the cancel message: we just delete the data since the 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// DownloadFile has been deleted. 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) { 190731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::vector<DownloadBuffer::Contents> contents; 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { 19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock auto_lock(buffer->lock); 194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch contents.swap(buffer->contents); 195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DownloadFile* download = GetDownloadFile(id); 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < contents.size(); ++i) { 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch net::IOBuffer* data = contents[i].first; 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int data_len = contents[i].second; 2013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (download) 2023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick download->AppendDataToFile(data->data(), data_len); 203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch data->Release(); 204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 207ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid DownloadFileManager::OnResponseCompleted( 208ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int id, 209ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DownloadBuffer* buffer, 210ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int os_error, 211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const std::string& security_info) { 212ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen VLOG(20) << __FUNCTION__ << "()" << " id = " << id 213ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen << " os_error = " << os_error 214ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen << " security_info = \"" << security_info << "\""; 215731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delete buffer; 217ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DownloadFile* download = GetDownloadFile(id); 218ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!download) 219ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return; 220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen download->Finish(); 222ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DownloadManager* download_manager = download->GetDownloadManager(); 224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!download_manager) { 225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen CancelDownload(id); 226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return; 227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 229ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::string hash; 230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!download->GetSha256Hash(&hash)) 231ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen hash.clear(); 232ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 233ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen BrowserThread::PostTask( 234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen BrowserThread::UI, FROM_HERE, 235ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen NewRunnableMethod( 236ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen download_manager, &DownloadManager::OnResponseCompleted, 237ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen id, download->bytes_so_far(), os_error, hash)); 238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // We need to keep the download around until the UI thread has finalized 239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // the name. 240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// This method will be sent via a user action, or shutdown on the UI thread, and 243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// run on the download thread. Since this message has been sent from the UI 244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// thread, the download may have already completed and won't exist in our map. 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadFileManager::CancelDownload(int id) { 246201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch VLOG(20) << __FUNCTION__ << "()" << " id = " << id; 247731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DownloadFileMap::iterator it = downloads_.find(id); 249ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (it == downloads_.end()) 250ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return; 251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 252ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DownloadFile* download = it->second; 253ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen VLOG(20) << __FUNCTION__ << "()" 254ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen << " download = " << download->DebugString(); 255ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen download->Cancel(); 256ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 257ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen EraseDownload(id); 258ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 259ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 260ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid DownloadFileManager::CompleteDownload(int id) { 261ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 262ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!ContainsKey(downloads_, id)) 264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return; 265ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 266ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DownloadFile* download_file = downloads_[id]; 267ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen VLOG(20) << " " << __FUNCTION__ << "()" 269ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen << " id = " << id 270ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen << " download_file = " << download_file->DebugString(); 271ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 272ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen download_file->Detach(); 273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 274ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen EraseDownload(id); 275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 2773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid DownloadFileManager::OnDownloadManagerShutdown(DownloadManager* manager) { 278731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(manager); 280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 2813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick std::set<DownloadFile*> to_remove; 2823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 2833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick for (DownloadFileMap::iterator i = downloads_.begin(); 2843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick i != downloads_.end(); ++i) { 2853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DownloadFile* download_file = i->second; 2863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (download_file->GetDownloadManager() == manager) { 2873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick download_file->CancelDownloadRequest(resource_dispatcher_host_); 2883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick to_remove.insert(download_file); 289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 2923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick for (std::set<DownloadFile*>::iterator i = to_remove.begin(); 2933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick i != to_remove.end(); ++i) { 2943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick downloads_.erase((*i)->id()); 2953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick delete *i; 2963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Actions from the UI thread and run on the download thread 300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The DownloadManager in the UI thread has provided an intermediate .crdownload 302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// name for the download specified by 'id'. Rename the in progress download. 303ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// 304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// There are 2 possible rename cases where this method can be called: 305ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// 1. tmp -> foo.crdownload (not final, safe) 306ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// 2. tmp-> Unconfirmed.xxx.crdownload (not final, dangerous) 307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid DownloadFileManager::RenameInProgressDownloadFile( 308ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int id, const FilePath& full_path) { 309201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch VLOG(20) << __FUNCTION__ << "()" << " id = " << id 310201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch << " full_path = \"" << full_path.value() << "\""; 311731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 312ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 313ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DownloadFile* download = GetDownloadFile(id); 314ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!download) 315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 317201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch VLOG(20) << __FUNCTION__ << "()" << " download = " << download->DebugString(); 318ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 319dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!download->Rename(full_path)) { 320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Error. Between the time the UI thread generated 'full_path' to the time 321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // this code runs, something happened that prevents us from renaming. 322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CancelDownloadOnRename(id); 323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The DownloadManager in the UI thread has provided a final name for the 327ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// download specified by 'id'. Rename the download that's in the process 328ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// of completing. 329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// 330dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// There are 2 possible rename cases where this method can be called: 331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// 1. foo.crdownload -> foo (final, safe) 332ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// 2. Unconfirmed.xxx.crdownload -> xxx (final, validated) 333ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid DownloadFileManager::RenameCompletingDownloadFile( 334ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int id, const FilePath& full_path, bool overwrite_existing_file) { 335201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch VLOG(20) << __FUNCTION__ << "()" << " id = " << id 336ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen << " overwrite_existing_file = " << overwrite_existing_file 337dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen << " full_path = \"" << full_path.value() << "\""; 338731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 3393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 3403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DownloadFile* download = GetDownloadFile(id); 3413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!download) 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 343ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 344ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(download->GetDownloadManager()); 345ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DownloadManager* download_manager = download->GetDownloadManager(); 346ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 347201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch VLOG(20) << __FUNCTION__ << "()" << " download = " << download->DebugString(); 348ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 349ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen int uniquifier = 0; 350ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen FilePath new_path = full_path; 351ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!overwrite_existing_file) { 352ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Make our name unique at this point, as if a dangerous file is 353ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // downloading and a 2nd download is started for a file with the same 354ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // name, they would have the same path. This is because we uniquify 355ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // the name on download start, and at that time the first file does 356ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // not exists yet, so the second file gets the same name. 357ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // This should not happen in the SAFE case, and we check for that in the UI 358ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // thread. 359ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen uniquifier = download_util::GetUniquePathNumber(new_path); 360ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (uniquifier > 0) { 361ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen download_util::AppendNumberToPath(&new_path, uniquifier); 362ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 363ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 364ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 365ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Rename the file, overwriting if necessary. 366ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!download->Rename(new_path)) { 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Error. Between the time the UI thread generated 'full_path' to the time 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // this code runs, something happened that prevents us from renaming. 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CancelDownloadOnRename(id); 370ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return; 371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 373ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if defined(OS_MACOSX) 374ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Done here because we only want to do this once; see 375ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // http://crbug.com/13120 for details. 376ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen download->AnnotateWithSourceInformation(); 377ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif 378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 379ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen BrowserThread::PostTask( 380ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen BrowserThread::UI, FROM_HERE, 381ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen NewRunnableMethod( 382ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen download_manager, &DownloadManager::OnDownloadRenamedToFinalName, id, 383ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen new_path, uniquifier)); 384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 386ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Called only from RenameInProgressDownloadFile and 387ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// RenameCompletingDownloadFile on the FILE thread. 388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadFileManager::CancelDownloadOnRename(int id) { 389731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 3903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 3913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DownloadFile* download = GetDownloadFile(id); 3923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!download) 393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 3953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick DownloadManager* download_manager = download->GetDownloadManager(); 3963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!download_manager) { 397ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Without a download manager, we can't cancel the request normally, so we 398ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // need to do it here. The normal path will also update the download 399ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // history before cancelling the request. 400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch download->CancelDownloadRequest(resource_dispatcher_host_); 4013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; 402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 4033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 404731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 405731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::UI, FROM_HERE, 4063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick NewRunnableMethod(download_manager, 4073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick &DownloadManager::DownloadCancelled, id)); 408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 409dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 410dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenvoid DownloadFileManager::EraseDownload(int id) { 411ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 412ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 413dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!ContainsKey(downloads_, id)) 414dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return; 415dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 416dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DownloadFile* download_file = downloads_[id]; 417dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 418ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen VLOG(20) << " " << __FUNCTION__ << "()" 419ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen << " id = " << id 420ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen << " download_file = " << download_file->DebugString(); 421dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 422ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen downloads_.erase(id); 423dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 424dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen delete download_file; 425ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 426ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (downloads_.empty()) 427ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen StopUpdateTimer(); 428dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen} 429