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.
5 #include "chrome/browser/chromeos/drive/download_handler.h"
8 #include "base/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 "content/public/browser/browser_thread.h"
18 using content::BrowserThread
;
19 using content::DownloadManager
;
20 using content::DownloadItem
;
25 // Key for base::SupportsUserData::Data.
26 const char kDrivePathKey
[] = "DrivePath";
28 // User Data stored in DownloadItem for drive path.
29 class DriveUserData
: public base::SupportsUserData::Data
{
31 explicit DriveUserData(const base::FilePath
& path
) : file_path_(path
),
32 is_complete_(false) {}
33 virtual ~DriveUserData() {}
35 const base::FilePath
& file_path() const { return file_path_
; }
36 const base::FilePath
& cache_file_path() const { return cache_file_path_
; }
37 void set_cache_file_path(const base::FilePath
& path
) {
38 cache_file_path_
= path
;
40 bool is_complete() const { return is_complete_
; }
41 void set_complete() { is_complete_
= true; }
44 const base::FilePath file_path_
;
45 base::FilePath cache_file_path_
;
49 // Extracts DriveUserData* from |download|.
50 const DriveUserData
* GetDriveUserData(const DownloadItem
* download
) {
51 return static_cast<const DriveUserData
*>(
52 download
->GetUserData(&kDrivePathKey
));
55 DriveUserData
* GetDriveUserData(DownloadItem
* download
) {
56 return static_cast<DriveUserData
*>(download
->GetUserData(&kDrivePathKey
));
59 // Creates a temporary file |drive_tmp_download_path| in
60 // |drive_tmp_download_dir|. Must be called on a thread that allows file
62 base::FilePath
GetDriveTempDownloadPath(
63 const base::FilePath
& drive_tmp_download_dir
) {
64 bool created
= base::CreateDirectory(drive_tmp_download_dir
);
65 DCHECK(created
) << "Can not create temp download directory at "
66 << drive_tmp_download_dir
.value();
67 base::FilePath drive_tmp_download_path
;
68 created
= base::CreateTemporaryFileInDir(drive_tmp_download_dir
,
69 &drive_tmp_download_path
);
70 DCHECK(created
) << "Temporary download file creation failed";
71 return drive_tmp_download_path
;
74 // Moves downloaded file to Drive.
75 void MoveDownloadedFile(const base::FilePath
& downloaded_file
,
76 base::FilePath
* cache_file_path
,
78 const base::FilePath
& dest_path
) {
79 if (error
!= FILE_ERROR_OK
||
80 !base::Move(downloaded_file
, dest_path
))
82 *cache_file_path
= dest_path
;
85 // Used to implement CheckForFileExistence().
86 void ContinueCheckingForFileExistence(
87 const content::CheckForFileExistenceCallback
& callback
,
89 scoped_ptr
<ResourceEntry
> entry
) {
90 callback
.Run(error
== FILE_ERROR_OK
);
93 // Returns true if |download| is a Drive download created from data persisted
94 // on the download history DB.
95 bool IsPersistedDriveDownload(const base::FilePath
& drive_tmp_download_path
,
96 DownloadItem
* download
) {
97 // Persisted downloads are not in IN_PROGRESS state when created, while newly
98 // created downloads are.
99 return drive_tmp_download_path
.IsParent(download
->GetTargetFilePath()) &&
100 download
->GetState() != DownloadItem::IN_PROGRESS
;
105 DownloadHandler::DownloadHandler(FileSystemInterface
* file_system
)
106 : file_system_(file_system
),
107 weak_ptr_factory_(this) {
110 DownloadHandler::~DownloadHandler() {
114 DownloadHandler
* DownloadHandler::GetForProfile(Profile
* profile
) {
115 DriveIntegrationService
* service
=
116 DriveIntegrationServiceFactory::FindForProfile(profile
);
117 if (!service
|| !service
->IsMounted())
119 return service
->download_handler();
122 void DownloadHandler::Initialize(
123 DownloadManager
* download_manager
,
124 const base::FilePath
& drive_tmp_download_path
) {
125 DCHECK(!drive_tmp_download_path
.empty());
127 drive_tmp_download_path_
= drive_tmp_download_path
;
129 if (download_manager
) {
130 notifier_
.reset(new AllDownloadItemNotifier(download_manager
, this));
131 // Remove any persisted Drive DownloadItem. crbug.com/171384
132 content::DownloadManager::DownloadVector downloads
;
133 download_manager
->GetAllDownloads(&downloads
);
134 for (size_t i
= 0; i
< downloads
.size(); ++i
) {
135 if (IsPersistedDriveDownload(drive_tmp_download_path_
, downloads
[i
]))
136 RemoveDownload(downloads
[i
]->GetId());
141 void DownloadHandler::SubstituteDriveDownloadPath(
142 const base::FilePath
& drive_path
,
143 content::DownloadItem
* download
,
144 const SubstituteDriveDownloadPathCallback
& callback
) {
145 DVLOG(1) << "SubstituteDriveDownloadPath " << drive_path
.value();
147 SetDownloadParams(drive_path
, download
);
149 if (util::IsUnderDriveMountPoint(drive_path
)) {
150 // Prepare the destination directory.
151 const bool is_exclusive
= false, is_recursive
= true;
152 file_system_
->CreateDirectory(
153 util::ExtractDrivePath(drive_path
.DirName()),
154 is_exclusive
, is_recursive
,
155 base::Bind(&DownloadHandler::OnCreateDirectory
,
156 weak_ptr_factory_
.GetWeakPtr(),
159 callback
.Run(drive_path
);
163 void DownloadHandler::SetDownloadParams(const base::FilePath
& drive_path
,
164 DownloadItem
* download
) {
165 if (!download
|| (download
->GetState() != DownloadItem::IN_PROGRESS
))
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
175 download
->SetUserData(&kDrivePathKey
, NULL
);
176 download
->SetDisplayName(base::FilePath());
180 base::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
186 return data
? data
->file_path() : base::FilePath();
189 base::FilePath
DownloadHandler::GetCacheFilePath(const DownloadItem
* download
) {
190 const DriveUserData
* data
= GetDriveUserData(download
);
191 return data
? data
->cache_file_path() : base::FilePath();
194 bool DownloadHandler::IsDriveDownload(const DownloadItem
* download
) {
195 // We use the existence of the DriveUserData object in download as a
196 // signal that this is a download to Drive.
197 return GetDriveUserData(download
) != NULL
;
200 void DownloadHandler::CheckForFileExistence(
201 const DownloadItem
* download
,
202 const content::CheckForFileExistenceCallback
& callback
) {
203 file_system_
->GetResourceEntry(
204 util::ExtractDrivePath(GetTargetPath(download
)),
205 base::Bind(&ContinueCheckingForFileExistence
,
209 void DownloadHandler::OnDownloadCreated(DownloadManager
* manager
,
210 DownloadItem
* download
) {
211 // Remove any persisted Drive DownloadItem. crbug.com/171384
212 if (IsPersistedDriveDownload(drive_tmp_download_path_
, download
)) {
213 // Remove download later, since doing it here results in a crash.
214 BrowserThread::PostTask(BrowserThread::UI
,
216 base::Bind(&DownloadHandler::RemoveDownload
,
217 weak_ptr_factory_
.GetWeakPtr(),
222 void DownloadHandler::RemoveDownload(int id
) {
223 DownloadManager
* manager
= notifier_
->GetManager();
226 DownloadItem
* download
= manager
->GetDownload(id
);
232 void DownloadHandler::OnDownloadUpdated(
233 DownloadManager
* manager
, DownloadItem
* download
) {
234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
236 // Only accept downloads that have the Drive meta data associated with them.
237 DriveUserData
* data
= GetDriveUserData(download
);
238 if (!drive_tmp_download_path_
.IsParent(download
->GetTargetFilePath()) ||
243 switch (download
->GetState()) {
244 case DownloadItem::IN_PROGRESS
:
247 case DownloadItem::COMPLETE
:
248 UploadDownloadItem(download
);
249 data
->set_complete();
252 case DownloadItem::CANCELLED
:
253 download
->SetUserData(&kDrivePathKey
, NULL
);
256 case DownloadItem::INTERRUPTED
:
257 // Interrupted downloads can be resumed. Keep the Drive user data around
258 // so that it can be used when the download resumes. The download is truly
259 // done when it's complete, is cancelled or is removed.
267 void DownloadHandler::OnCreateDirectory(
268 const SubstituteDriveDownloadPathCallback
& callback
,
270 DVLOG(1) << "OnCreateDirectory " << FileErrorToString(error
);
271 if (error
== FILE_ERROR_OK
) {
272 base::PostTaskAndReplyWithResult(
273 BrowserThread::GetBlockingPool(),
275 base::Bind(&GetDriveTempDownloadPath
, drive_tmp_download_path_
),
278 LOG(WARNING
) << "Failed to create directory, error = "
279 << FileErrorToString(error
);
280 callback
.Run(base::FilePath());
284 void DownloadHandler::UploadDownloadItem(DownloadItem
* download
) {
285 DCHECK_EQ(DownloadItem::COMPLETE
, download
->GetState());
286 base::FilePath
* cache_file_path
= new base::FilePath
;
287 WriteOnCacheFileAndReply(
289 util::ExtractDrivePath(GetTargetPath(download
)),
290 download
->GetMimeType(),
291 base::Bind(&MoveDownloadedFile
, download
->GetTargetFilePath(),
293 base::Bind(&DownloadHandler::SetCacheFilePath
,
294 weak_ptr_factory_
.GetWeakPtr(),
296 base::Owned(cache_file_path
)));
299 void DownloadHandler::SetCacheFilePath(int id
,
300 const base::FilePath
* cache_file_path
,
302 if (error
!= FILE_ERROR_OK
)
304 DownloadManager
* manager
= notifier_
->GetManager();
307 DownloadItem
* download
= manager
->GetDownload(id
);
310 DriveUserData
* data
= GetDriveUserData(download
);
313 data
->set_cache_file_path(*cache_file_path
);