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/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_integration_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/write_on_cache_file.h"
15 #include "chrome/browser/download/download_history.h"
16 #include "chrome/browser/download/download_service.h"
17 #include "chrome/browser/download/download_service_factory.h"
18 #include "components/drive/drive.pb.h"
19 #include "content/public/browser/browser_thread.h"
21 using content::BrowserThread
;
22 using content::DownloadManager
;
23 using content::DownloadItem
;
28 // Key for base::SupportsUserData::Data.
29 const char kDrivePathKey
[] = "DrivePath";
31 // User Data stored in DownloadItem for drive path.
32 class DriveUserData
: public base::SupportsUserData::Data
{
34 explicit DriveUserData(const base::FilePath
& path
) : file_path_(path
),
35 is_complete_(false) {}
36 ~DriveUserData() override
{}
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
;
43 bool is_complete() const { return is_complete_
; }
44 void set_complete() { is_complete_
= true; }
47 const base::FilePath file_path_
;
48 base::FilePath cache_file_path_
;
52 // Extracts DriveUserData* from |download|.
53 const DriveUserData
* GetDriveUserData(const DownloadItem
* download
) {
54 return static_cast<const DriveUserData
*>(
55 download
->GetUserData(&kDrivePathKey
));
58 DriveUserData
* GetDriveUserData(DownloadItem
* download
) {
59 return static_cast<DriveUserData
*>(download
->GetUserData(&kDrivePathKey
));
62 // Creates a temporary file |drive_tmp_download_path| in
63 // |drive_tmp_download_dir|. Must be called on a thread that allows file
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
;
77 // Moves downloaded file to Drive.
78 void MoveDownloadedFile(const base::FilePath
& downloaded_file
,
79 base::FilePath
* cache_file_path
,
81 const base::FilePath
& dest_path
) {
82 if (error
!= FILE_ERROR_OK
||
83 !base::Move(downloaded_file
, dest_path
))
85 *cache_file_path
= dest_path
;
88 // Used to implement CheckForFileExistence().
89 void ContinueCheckingForFileExistence(
90 const content::CheckForFileExistenceCallback
& callback
,
92 scoped_ptr
<ResourceEntry
> entry
) {
93 callback
.Run(error
== FILE_ERROR_OK
);
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()))
103 DownloadService
* download_service
=
104 DownloadServiceFactory::GetForBrowserContext(
105 download
->GetBrowserContext());
106 DownloadHistory
* download_history
= download_service
->GetDownloadHistory();
108 return download_history
&& download_history
->WasRestoredFromHistory(download
);
113 DownloadHandler::DownloadHandler(FileSystemInterface
* file_system
)
114 : file_system_(file_system
),
115 weak_ptr_factory_(this) {
118 DownloadHandler::~DownloadHandler() {
122 DownloadHandler
* DownloadHandler::GetForProfile(Profile
* profile
) {
123 DriveIntegrationService
* service
=
124 DriveIntegrationServiceFactory::FindForProfile(profile
);
125 if (!service
|| !service
->IsMounted())
127 return service
->download_handler();
130 void DownloadHandler::Initialize(
131 DownloadManager
* download_manager
,
132 const base::FilePath
& drive_tmp_download_path
) {
133 DCHECK(!drive_tmp_download_path
.empty());
135 drive_tmp_download_path_
= drive_tmp_download_path
;
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();
149 void DownloadHandler::ObserveIncognitoDownloadManager(
150 DownloadManager
* download_manager
) {
151 notifier_incognito_
.reset(new AllDownloadItemNotifier(download_manager
,
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();
161 SetDownloadParams(drive_path
, download
);
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(),
173 callback
.Run(drive_path
);
177 void DownloadHandler::SetDownloadParams(const base::FilePath
& drive_path
,
178 DownloadItem
* download
) {
179 if (!download
|| (download
->GetState() != DownloadItem::IN_PROGRESS
))
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
189 download
->SetUserData(&kDrivePathKey
, NULL
);
190 download
->SetDisplayName(base::FilePath());
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
200 return data
? data
->file_path() : base::FilePath();
203 base::FilePath
DownloadHandler::GetCacheFilePath(const DownloadItem
* download
) {
204 const DriveUserData
* data
= GetDriveUserData(download
);
205 return data
? data
->cache_file_path() : base::FilePath();
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
;
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
,
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
,
230 base::Bind(&DownloadHandler::RemoveDownload
,
231 weak_ptr_factory_
.GetWeakPtr(),
232 static_cast<void*>(manager
),
237 void DownloadHandler::RemoveDownload(void* manager_id
, int id
) {
238 DownloadManager
* manager
= GetDownloadManager(manager_id
);
241 DownloadItem
* download
= manager
->GetDownload(id
);
247 void DownloadHandler::OnDownloadUpdated(
248 DownloadManager
* manager
, DownloadItem
* download
) {
249 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
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()) ||
258 switch (download
->GetState()) {
259 case DownloadItem::IN_PROGRESS
:
262 case DownloadItem::COMPLETE
:
263 UploadDownloadItem(manager
, download
);
264 data
->set_complete();
267 case DownloadItem::CANCELLED
:
268 download
->SetUserData(&kDrivePathKey
, NULL
);
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.
282 void DownloadHandler::OnCreateDirectory(
283 const SubstituteDriveDownloadPathCallback
& callback
,
285 DVLOG(1) << "OnCreateDirectory " << FileErrorToString(error
);
286 if (error
== FILE_ERROR_OK
) {
287 base::PostTaskAndReplyWithResult(
288 BrowserThread::GetBlockingPool(),
290 base::Bind(&GetDriveTempDownloadPath
, drive_tmp_download_path_
),
293 LOG(WARNING
) << "Failed to create directory, error = "
294 << FileErrorToString(error
);
295 callback
.Run(base::FilePath());
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(
305 util::ExtractDrivePath(GetTargetPath(download
)),
306 download
->GetMimeType(),
307 base::Bind(&MoveDownloadedFile
, download
->GetTargetFilePath(),
309 base::Bind(&DownloadHandler::SetCacheFilePath
,
310 weak_ptr_factory_
.GetWeakPtr(),
311 static_cast<void*>(manager
),
313 base::Owned(cache_file_path
)));
316 void DownloadHandler::SetCacheFilePath(void* manager_id
,
318 const base::FilePath
* cache_file_path
,
320 if (error
!= FILE_ERROR_OK
)
322 DownloadManager
* manager
= GetDownloadManager(manager_id
);
325 DownloadItem
* download
= manager
->GetDownload(id
);
328 DriveUserData
* data
= GetDriveUserData(download
);
331 data
->set_cache_file_path(*cache_file_path
);
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();