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/files/file_util.h" 9 #include "base/supports_user_data.h" 10 #include "base/threading/sequenced_worker_pool.h" 11 #include "chrome/browser/chromeos/drive/drive.pb.h" 12 #include "chrome/browser/chromeos/drive/drive_integration_service.h" 13 #include "chrome/browser/chromeos/drive/file_system_interface.h" 14 #include "chrome/browser/chromeos/drive/file_system_util.h" 15 #include "chrome/browser/chromeos/drive/write_on_cache_file.h" 16 #include "chrome/browser/download/download_history.h" 17 #include "chrome/browser/download/download_service.h" 18 #include "chrome/browser/download/download_service_factory.h" 19 #include "content/public/browser/browser_thread.h" 20 21 using content::BrowserThread; 22 using content::DownloadManager; 23 using content::DownloadItem; 24 25 namespace drive { 26 namespace { 27 28 // Key for base::SupportsUserData::Data. 29 const char kDrivePathKey[] = "DrivePath"; 30 31 // User Data stored in DownloadItem for drive path. 32 class DriveUserData : public base::SupportsUserData::Data { 33 public: 34 explicit DriveUserData(const base::FilePath& path) : file_path_(path), 35 is_complete_(false) {} 36 virtual ~DriveUserData() {} 37 38 const base::FilePath& file_path() const { return file_path_; } 39 const base::FilePath& cache_file_path() const { return cache_file_path_; } 40 void set_cache_file_path(const base::FilePath& path) { 41 cache_file_path_ = path; 42 } 43 bool is_complete() const { return is_complete_; } 44 void set_complete() { is_complete_ = true; } 45 46 private: 47 const base::FilePath file_path_; 48 base::FilePath cache_file_path_; 49 bool is_complete_; 50 }; 51 52 // Extracts DriveUserData* from |download|. 53 const DriveUserData* GetDriveUserData(const DownloadItem* download) { 54 return static_cast<const DriveUserData*>( 55 download->GetUserData(&kDrivePathKey)); 56 } 57 58 DriveUserData* GetDriveUserData(DownloadItem* download) { 59 return static_cast<DriveUserData*>(download->GetUserData(&kDrivePathKey)); 60 } 61 62 // Creates a temporary file |drive_tmp_download_path| in 63 // |drive_tmp_download_dir|. Must be called on a thread that allows file 64 // operations. 65 base::FilePath GetDriveTempDownloadPath( 66 const base::FilePath& drive_tmp_download_dir) { 67 bool created = base::CreateDirectory(drive_tmp_download_dir); 68 DCHECK(created) << "Can not create temp download directory at " 69 << drive_tmp_download_dir.value(); 70 base::FilePath drive_tmp_download_path; 71 created = base::CreateTemporaryFileInDir(drive_tmp_download_dir, 72 &drive_tmp_download_path); 73 DCHECK(created) << "Temporary download file creation failed"; 74 return drive_tmp_download_path; 75 } 76 77 // Moves downloaded file to Drive. 78 void MoveDownloadedFile(const base::FilePath& downloaded_file, 79 base::FilePath* cache_file_path, 80 FileError error, 81 const base::FilePath& dest_path) { 82 if (error != FILE_ERROR_OK || 83 !base::Move(downloaded_file, dest_path)) 84 return; 85 *cache_file_path = dest_path; 86 } 87 88 // Used to implement CheckForFileExistence(). 89 void ContinueCheckingForFileExistence( 90 const content::CheckForFileExistenceCallback& callback, 91 FileError error, 92 scoped_ptr<ResourceEntry> entry) { 93 callback.Run(error == FILE_ERROR_OK); 94 } 95 96 // Returns true if |download| is a Drive download created from data persisted 97 // on the download history DB. 98 bool IsPersistedDriveDownload(const base::FilePath& drive_tmp_download_path, 99 DownloadItem* download) { 100 if (!drive_tmp_download_path.IsParent(download->GetTargetFilePath())) 101 return false; 102 103 DownloadService* download_service = 104 DownloadServiceFactory::GetForBrowserContext( 105 download->GetBrowserContext()); 106 DownloadHistory* download_history = download_service->GetDownloadHistory(); 107 108 return download_history && download_history->WasRestoredFromHistory(download); 109 } 110 111 } // namespace 112 113 DownloadHandler::DownloadHandler(FileSystemInterface* file_system) 114 : file_system_(file_system), 115 weak_ptr_factory_(this) { 116 } 117 118 DownloadHandler::~DownloadHandler() { 119 } 120 121 // static 122 DownloadHandler* DownloadHandler::GetForProfile(Profile* profile) { 123 DriveIntegrationService* service = 124 DriveIntegrationServiceFactory::FindForProfile(profile); 125 if (!service || !service->IsMounted()) 126 return NULL; 127 return service->download_handler(); 128 } 129 130 void DownloadHandler::Initialize( 131 DownloadManager* download_manager, 132 const base::FilePath& drive_tmp_download_path) { 133 DCHECK(!drive_tmp_download_path.empty()); 134 135 drive_tmp_download_path_ = drive_tmp_download_path; 136 137 if (download_manager) { 138 notifier_.reset(new AllDownloadItemNotifier(download_manager, this)); 139 // Remove any persisted Drive DownloadItem. crbug.com/171384 140 content::DownloadManager::DownloadVector downloads; 141 download_manager->GetAllDownloads(&downloads); 142 for (size_t i = 0; i < downloads.size(); ++i) { 143 if (IsPersistedDriveDownload(drive_tmp_download_path_, downloads[i])) 144 downloads[i]->Remove(); 145 } 146 } 147 } 148 149 void DownloadHandler::ObserveIncognitoDownloadManager( 150 DownloadManager* download_manager) { 151 notifier_incognito_.reset(new AllDownloadItemNotifier(download_manager, 152 this)); 153 } 154 155 void DownloadHandler::SubstituteDriveDownloadPath( 156 const base::FilePath& drive_path, 157 content::DownloadItem* download, 158 const SubstituteDriveDownloadPathCallback& callback) { 159 DVLOG(1) << "SubstituteDriveDownloadPath " << drive_path.value(); 160 161 SetDownloadParams(drive_path, download); 162 163 if (util::IsUnderDriveMountPoint(drive_path)) { 164 // Prepare the destination directory. 165 const bool is_exclusive = false, is_recursive = true; 166 file_system_->CreateDirectory( 167 util::ExtractDrivePath(drive_path.DirName()), 168 is_exclusive, is_recursive, 169 base::Bind(&DownloadHandler::OnCreateDirectory, 170 weak_ptr_factory_.GetWeakPtr(), 171 callback)); 172 } else { 173 callback.Run(drive_path); 174 } 175 } 176 177 void DownloadHandler::SetDownloadParams(const base::FilePath& drive_path, 178 DownloadItem* download) { 179 if (!download || (download->GetState() != DownloadItem::IN_PROGRESS)) 180 return; 181 182 if (util::IsUnderDriveMountPoint(drive_path)) { 183 download->SetUserData(&kDrivePathKey, new DriveUserData(drive_path)); 184 download->SetDisplayName(drive_path.BaseName()); 185 } else if (IsDriveDownload(download)) { 186 // This may have been previously set if the default download folder is 187 // /drive, and the user has now changed the download target to a local 188 // folder. 189 download->SetUserData(&kDrivePathKey, NULL); 190 download->SetDisplayName(base::FilePath()); 191 } 192 } 193 194 base::FilePath DownloadHandler::GetTargetPath( 195 const DownloadItem* download) { 196 const DriveUserData* data = GetDriveUserData(download); 197 // If data is NULL, we've somehow lost the drive path selected by the file 198 // picker. 199 DCHECK(data); 200 return data ? data->file_path() : base::FilePath(); 201 } 202 203 base::FilePath DownloadHandler::GetCacheFilePath(const DownloadItem* download) { 204 const DriveUserData* data = GetDriveUserData(download); 205 return data ? data->cache_file_path() : base::FilePath(); 206 } 207 208 bool DownloadHandler::IsDriveDownload(const DownloadItem* download) { 209 // We use the existence of the DriveUserData object in download as a 210 // signal that this is a download to Drive. 211 return GetDriveUserData(download) != NULL; 212 } 213 214 void DownloadHandler::CheckForFileExistence( 215 const DownloadItem* download, 216 const content::CheckForFileExistenceCallback& callback) { 217 file_system_->GetResourceEntry( 218 util::ExtractDrivePath(GetTargetPath(download)), 219 base::Bind(&ContinueCheckingForFileExistence, 220 callback)); 221 } 222 223 void DownloadHandler::OnDownloadCreated(DownloadManager* manager, 224 DownloadItem* download) { 225 // Remove any persisted Drive DownloadItem. crbug.com/171384 226 if (IsPersistedDriveDownload(drive_tmp_download_path_, download)) { 227 // Remove download later, since doing it here results in a crash. 228 BrowserThread::PostTask(BrowserThread::UI, 229 FROM_HERE, 230 base::Bind(&DownloadHandler::RemoveDownload, 231 weak_ptr_factory_.GetWeakPtr(), 232 static_cast<void*>(manager), 233 download->GetId())); 234 } 235 } 236 237 void DownloadHandler::RemoveDownload(void* manager_id, int id) { 238 DownloadManager* manager = GetDownloadManager(manager_id); 239 if (!manager) 240 return; 241 DownloadItem* download = manager->GetDownload(id); 242 if (!download) 243 return; 244 download->Remove(); 245 } 246 247 void DownloadHandler::OnDownloadUpdated( 248 DownloadManager* manager, DownloadItem* download) { 249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 250 251 // Only accept downloads that have the Drive meta data associated with them. 252 DriveUserData* data = GetDriveUserData(download); 253 if (!drive_tmp_download_path_.IsParent(download->GetTargetFilePath()) || 254 !data || 255 data->is_complete()) 256 return; 257 258 switch (download->GetState()) { 259 case DownloadItem::IN_PROGRESS: 260 break; 261 262 case DownloadItem::COMPLETE: 263 UploadDownloadItem(manager, download); 264 data->set_complete(); 265 break; 266 267 case DownloadItem::CANCELLED: 268 download->SetUserData(&kDrivePathKey, NULL); 269 break; 270 271 case DownloadItem::INTERRUPTED: 272 // Interrupted downloads can be resumed. Keep the Drive user data around 273 // so that it can be used when the download resumes. The download is truly 274 // done when it's complete, is cancelled or is removed. 275 break; 276 277 default: 278 NOTREACHED(); 279 } 280 } 281 282 void DownloadHandler::OnCreateDirectory( 283 const SubstituteDriveDownloadPathCallback& callback, 284 FileError error) { 285 DVLOG(1) << "OnCreateDirectory " << FileErrorToString(error); 286 if (error == FILE_ERROR_OK) { 287 base::PostTaskAndReplyWithResult( 288 BrowserThread::GetBlockingPool(), 289 FROM_HERE, 290 base::Bind(&GetDriveTempDownloadPath, drive_tmp_download_path_), 291 callback); 292 } else { 293 LOG(WARNING) << "Failed to create directory, error = " 294 << FileErrorToString(error); 295 callback.Run(base::FilePath()); 296 } 297 } 298 299 void DownloadHandler::UploadDownloadItem(DownloadManager* manager, 300 DownloadItem* download) { 301 DCHECK_EQ(DownloadItem::COMPLETE, download->GetState()); 302 base::FilePath* cache_file_path = new base::FilePath; 303 WriteOnCacheFileAndReply( 304 file_system_, 305 util::ExtractDrivePath(GetTargetPath(download)), 306 download->GetMimeType(), 307 base::Bind(&MoveDownloadedFile, download->GetTargetFilePath(), 308 cache_file_path), 309 base::Bind(&DownloadHandler::SetCacheFilePath, 310 weak_ptr_factory_.GetWeakPtr(), 311 static_cast<void*>(manager), 312 download->GetId(), 313 base::Owned(cache_file_path))); 314 } 315 316 void DownloadHandler::SetCacheFilePath(void* manager_id, 317 int id, 318 const base::FilePath* cache_file_path, 319 FileError error) { 320 if (error != FILE_ERROR_OK) 321 return; 322 DownloadManager* manager = GetDownloadManager(manager_id); 323 if (!manager) 324 return; 325 DownloadItem* download = manager->GetDownload(id); 326 if (!download) 327 return; 328 DriveUserData* data = GetDriveUserData(download); 329 if (!data) 330 return; 331 data->set_cache_file_path(*cache_file_path); 332 } 333 334 DownloadManager* DownloadHandler::GetDownloadManager(void* manager_id) { 335 if (manager_id == notifier_->GetManager()) 336 return notifier_->GetManager(); 337 if (notifier_incognito_ && manager_id == notifier_incognito_->GetManager()) 338 return notifier_incognito_->GetManager(); 339 return NULL; 340 } 341 342 } // namespace drive 343