download_handler.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/chromeos/drive/download_handler.h" 6 7#include "base/bind.h" 8#include "base/file_util.h" 9#include "base/supports_user_data.h" 10#include "chrome/browser/chromeos/drive/drive.pb.h" 11#include "chrome/browser/chromeos/drive/drive_system_service.h" 12#include "chrome/browser/chromeos/drive/file_system_interface.h" 13#include "chrome/browser/chromeos/drive/file_system_util.h" 14#include "chrome/browser/chromeos/drive/file_write_helper.h" 15#include "content/public/browser/browser_thread.h" 16 17using content::BrowserThread; 18using content::DownloadManager; 19using content::DownloadItem; 20 21namespace drive { 22namespace { 23 24// Key for base::SupportsUserData::Data. 25const char kDrivePathKey[] = "DrivePath"; 26 27// User Data stored in DownloadItem for drive path. 28class DriveUserData : public base::SupportsUserData::Data { 29 public: 30 explicit DriveUserData(const base::FilePath& path) : file_path_(path), 31 is_complete_(false) {} 32 virtual ~DriveUserData() {} 33 34 const base::FilePath& file_path() const { return file_path_; } 35 bool is_complete() const { return is_complete_; } 36 void set_complete() { is_complete_ = true; } 37 38 private: 39 const base::FilePath file_path_; 40 bool is_complete_; 41}; 42 43// Extracts DriveUserData* from |download|. 44const DriveUserData* GetDriveUserData(const DownloadItem* download) { 45 return static_cast<const DriveUserData*>( 46 download->GetUserData(&kDrivePathKey)); 47} 48 49DriveUserData* GetDriveUserData(DownloadItem* download) { 50 return static_cast<DriveUserData*>(download->GetUserData(&kDrivePathKey)); 51} 52 53// Creates a temporary file |drive_tmp_download_path| in 54// |drive_tmp_download_dir|. Must be called on a thread that allows file 55// operations. 56base::FilePath GetDriveTempDownloadPath( 57 const base::FilePath& drive_tmp_download_dir) { 58 bool created = file_util::CreateDirectory(drive_tmp_download_dir); 59 DCHECK(created) << "Can not create temp download directory at " 60 << drive_tmp_download_dir.value(); 61 base::FilePath drive_tmp_download_path; 62 created = file_util::CreateTemporaryFileInDir(drive_tmp_download_dir, 63 &drive_tmp_download_path); 64 DCHECK(created) << "Temporary download file creation failed"; 65 return drive_tmp_download_path; 66} 67 68// Moves downloaded file to Drive. 69void MoveDownloadedFile(const base::FilePath& downloaded_file, 70 FileError error, 71 const base::FilePath& dest_path) { 72 if (error != FILE_ERROR_OK) 73 return; 74 file_util::Move(downloaded_file, dest_path); 75} 76 77// Used to implement CheckForFileExistence(). 78void ContinueCheckingForFileExistence( 79 const content::CheckForFileExistenceCallback& callback, 80 FileError error, 81 scoped_ptr<ResourceEntry> entry) { 82 callback.Run(error == FILE_ERROR_OK); 83} 84 85// Returns true if |download| is a Drive download created from data persisted 86// on the download history DB. 87bool IsPersistedDriveDownload(const base::FilePath& drive_tmp_download_path, 88 DownloadItem* download) { 89 // Persisted downloads are not in IN_PROGRESS state when created, while newly 90 // created downloads are. 91 return drive_tmp_download_path.IsParent(download->GetFullPath()) && 92 download->GetState() != DownloadItem::IN_PROGRESS; 93} 94 95} // namespace 96 97DownloadHandler::DownloadHandler( 98 FileWriteHelper* file_write_helper, 99 FileSystemInterface* file_system) 100 : file_write_helper_(file_write_helper), 101 file_system_(file_system), 102 weak_ptr_factory_(this) { 103} 104 105DownloadHandler::~DownloadHandler() { 106} 107 108// static 109DownloadHandler* DownloadHandler::GetForProfile(Profile* profile) { 110 DriveSystemService* system_service = 111 DriveSystemServiceFactory::FindForProfile(profile); 112 return system_service ? system_service->download_handler() : NULL; 113} 114 115void DownloadHandler::Initialize( 116 DownloadManager* download_manager, 117 const base::FilePath& drive_tmp_download_path) { 118 DCHECK(!drive_tmp_download_path.empty()); 119 120 drive_tmp_download_path_ = drive_tmp_download_path; 121 122 if (download_manager) { 123 notifier_.reset(new AllDownloadItemNotifier(download_manager, this)); 124 // Remove any persisted Drive DownloadItem. crbug.com/171384 125 content::DownloadManager::DownloadVector downloads; 126 download_manager->GetAllDownloads(&downloads); 127 for (size_t i = 0; i < downloads.size(); ++i) { 128 if (IsPersistedDriveDownload(drive_tmp_download_path_, downloads[i])) 129 RemoveDownload(downloads[i]->GetId()); 130 } 131 } 132} 133 134void DownloadHandler::SubstituteDriveDownloadPath( 135 const base::FilePath& drive_path, 136 content::DownloadItem* download, 137 const SubstituteDriveDownloadPathCallback& callback) { 138 DVLOG(1) << "SubstituteDriveDownloadPath " << drive_path.value(); 139 140 SetDownloadParams(drive_path, download); 141 142 if (util::IsUnderDriveMountPoint(drive_path)) { 143 // Can't access drive if the directory does not exist on Drive. 144 // We set off a chain of callbacks as follows: 145 // FileSystem::GetEntryInfoByPath 146 // OnEntryFound calls FileSystem::CreateDirectory (if necessary) 147 // OnCreateDirectory calls SubstituteDriveDownloadPathInternal 148 const base::FilePath drive_dir_path = 149 util::ExtractDrivePath(drive_path.DirName()); 150 // Ensure the directory exists. This also forces FileSystem to 151 // initialize DriveRootDirectory. 152 file_system_->GetEntryInfoByPath( 153 drive_dir_path, 154 base::Bind(&DownloadHandler::OnEntryFound, 155 weak_ptr_factory_.GetWeakPtr(), 156 drive_dir_path, 157 callback)); 158 } else { 159 callback.Run(drive_path); 160 } 161} 162 163void DownloadHandler::SetDownloadParams(const base::FilePath& drive_path, 164 DownloadItem* download) { 165 if (!download || (download->GetState() != DownloadItem::IN_PROGRESS)) 166 return; 167 168 if (util::IsUnderDriveMountPoint(drive_path)) { 169 download->SetUserData(&kDrivePathKey, new DriveUserData(drive_path)); 170 download->SetDisplayName(drive_path.BaseName()); 171 } else if (IsDriveDownload(download)) { 172 // This may have been previously set if the default download folder is 173 // /drive, and the user has now changed the download target to a local 174 // folder. 175 download->SetUserData(&kDrivePathKey, NULL); 176 download->SetDisplayName(base::FilePath()); 177 } 178} 179 180base::FilePath DownloadHandler::GetTargetPath( 181 const DownloadItem* download) { 182 const DriveUserData* data = GetDriveUserData(download); 183 // If data is NULL, we've somehow lost the drive path selected by the file 184 // picker. 185 DCHECK(data); 186 return data ? data->file_path() : base::FilePath(); 187} 188 189bool DownloadHandler::IsDriveDownload(const DownloadItem* download) { 190 // We use the existence of the DriveUserData object in download as a 191 // signal that this is a DriveDownload. 192 return GetDriveUserData(download) != NULL; 193} 194 195void DownloadHandler::CheckForFileExistence( 196 const DownloadItem* download, 197 const content::CheckForFileExistenceCallback& callback) { 198 file_system_->GetEntryInfoByPath( 199 util::ExtractDrivePath(GetTargetPath(download)), 200 base::Bind(&ContinueCheckingForFileExistence, 201 callback)); 202} 203 204void DownloadHandler::OnDownloadCreated(DownloadManager* manager, 205 DownloadItem* download) { 206 // Remove any persisted Drive DownloadItem. crbug.com/171384 207 if (IsPersistedDriveDownload(drive_tmp_download_path_, download)) { 208 // Remove download later, since doing it here results in a crash. 209 BrowserThread::PostTask(BrowserThread::UI, 210 FROM_HERE, 211 base::Bind(&DownloadHandler::RemoveDownload, 212 weak_ptr_factory_.GetWeakPtr(), 213 download->GetId())); 214 } 215} 216 217void DownloadHandler::RemoveDownload(int id) { 218 DownloadManager* manager = notifier_->GetManager(); 219 if (!manager) 220 return; 221 DownloadItem* download = manager->GetDownload(id); 222 if (!download) 223 return; 224 download->Remove(); 225} 226 227void DownloadHandler::OnDownloadUpdated( 228 DownloadManager* manager, DownloadItem* download) { 229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 230 231 // Only accept downloads that have the Drive meta data associated with them. 232 DriveUserData* data = GetDriveUserData(download); 233 if (!drive_tmp_download_path_.IsParent(download->GetTargetFilePath()) || 234 !data || 235 data->is_complete()) 236 return; 237 238 switch (download->GetState()) { 239 case DownloadItem::IN_PROGRESS: 240 break; 241 242 case DownloadItem::COMPLETE: 243 UploadDownloadItem(download); 244 data->set_complete(); 245 break; 246 247 case DownloadItem::CANCELLED: 248 case DownloadItem::INTERRUPTED: 249 download->SetUserData(&kDrivePathKey, NULL); 250 break; 251 252 default: 253 NOTREACHED(); 254 } 255} 256 257void DownloadHandler::OnEntryFound( 258 const base::FilePath& drive_dir_path, 259 const SubstituteDriveDownloadPathCallback& callback, 260 FileError error, 261 scoped_ptr<ResourceEntry> entry) { 262 if (error == FILE_ERROR_NOT_FOUND) { 263 // Destination Drive directory doesn't exist, so create it. 264 const bool is_exclusive = false, is_recursive = true; 265 file_system_->CreateDirectory( 266 drive_dir_path, is_exclusive, is_recursive, 267 base::Bind(&DownloadHandler::OnCreateDirectory, 268 weak_ptr_factory_.GetWeakPtr(), 269 callback)); 270 } else if (error == FILE_ERROR_OK) { 271 // Directory is already ready. 272 OnCreateDirectory(callback, FILE_ERROR_OK); 273 } else { 274 LOG(WARNING) << "Failed to get entry info for path: " 275 << drive_dir_path.value() << ", error = " 276 << FileErrorToString(error); 277 callback.Run(base::FilePath()); 278 } 279} 280 281void DownloadHandler::OnCreateDirectory( 282 const SubstituteDriveDownloadPathCallback& callback, 283 FileError error) { 284 DVLOG(1) << "OnCreateDirectory " << FileErrorToString(error); 285 if (error == FILE_ERROR_OK) { 286 base::PostTaskAndReplyWithResult( 287 BrowserThread::GetBlockingPool(), 288 FROM_HERE, 289 base::Bind(&GetDriveTempDownloadPath, drive_tmp_download_path_), 290 callback); 291 } else { 292 LOG(WARNING) << "Failed to create directory, error = " 293 << FileErrorToString(error); 294 callback.Run(base::FilePath()); 295 } 296} 297 298void DownloadHandler::UploadDownloadItem(DownloadItem* download) { 299 DCHECK(download->IsComplete()); 300 file_write_helper_->PrepareWritableFileAndRun( 301 util::ExtractDrivePath(GetTargetPath(download)), 302 base::Bind(&MoveDownloadedFile, download->GetTargetFilePath())); 303} 304 305} // namespace drive 306