Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / chromeos / drive / file_system / download_operation.cc
blob1aeb5e7a2c19db7d682a4bd81319315222406c42
1 // Copyright 2013 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/file_system/download_operation.h"
7 #include "base/callback_helpers.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/logging.h"
11 #include "base/task_runner_util.h"
12 #include "chrome/browser/chromeos/drive/drive.pb.h"
13 #include "chrome/browser/chromeos/drive/file_cache.h"
14 #include "chrome/browser/chromeos/drive/file_change.h"
15 #include "chrome/browser/chromeos/drive/file_errors.h"
16 #include "chrome/browser/chromeos/drive/file_system/operation_delegate.h"
17 #include "chrome/browser/chromeos/drive/file_system_util.h"
18 #include "chrome/browser/chromeos/drive/job_scheduler.h"
19 #include "chrome/browser/chromeos/drive/resource_metadata.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "google_apis/drive/drive_api_error_codes.h"
23 using content::BrowserThread;
25 namespace drive {
26 namespace file_system {
27 namespace {
29 // Generates an unused file path with |extension| to |out_path|, as a descendant
30 // of |dir|, with its parent directory created.
31 bool GeneratesUniquePathWithExtension(
32 const base::FilePath& dir,
33 const base::FilePath::StringType& extension,
34 base::FilePath* out_path) {
35 base::FilePath subdir;
36 if (!base::CreateTemporaryDirInDir(dir, base::FilePath::StringType(),
37 &subdir)) {
38 return false;
40 *out_path = subdir.Append(FILE_PATH_LITERAL("tmp") + extension);
41 return true;
44 // Prepares for downloading the file. Allocates the enough space for the file
45 // in the cache.
46 // If succeeded, returns FILE_ERROR_OK with |temp_download_file| storing the
47 // path to the file in the cache.
48 FileError PrepareForDownloadFile(internal::FileCache* cache,
49 int64 expected_file_size,
50 const base::FilePath& temporary_file_directory,
51 base::FilePath* temp_download_file) {
52 DCHECK(cache);
53 DCHECK(temp_download_file);
55 // Ensure enough space in the cache.
56 if (!cache->FreeDiskSpaceIfNeededFor(expected_file_size))
57 return FILE_ERROR_NO_LOCAL_SPACE;
59 return base::CreateTemporaryFileInDir(
60 temporary_file_directory,
61 temp_download_file) ? FILE_ERROR_OK : FILE_ERROR_FAILED;
64 // If the resource is a hosted document, creates a JSON file representing the
65 // resource locally, and returns FILE_ERROR_OK with |cache_file_path| storing
66 // the path to the JSON file.
67 // If the resource is a regular file and its local cache is available,
68 // returns FILE_ERROR_OK with |cache_file_path| storing the path to the
69 // cache file.
70 // If the resource is a regular file but its local cache is NOT available,
71 // returns FILE_ERROR_OK, but |cache_file_path| is kept empty.
72 // Otherwise returns error code.
73 FileError CheckPreConditionForEnsureFileDownloaded(
74 internal::ResourceMetadata* metadata,
75 internal::FileCache* cache,
76 const base::FilePath& temporary_file_directory,
77 const std::string& local_id,
78 ResourceEntry* entry,
79 base::FilePath* cache_file_path,
80 base::FilePath* temp_download_file_path) {
81 DCHECK(metadata);
82 DCHECK(cache);
83 DCHECK(cache_file_path);
85 FileError error = metadata->GetResourceEntryById(local_id, entry);
86 if (error != FILE_ERROR_OK)
87 return error;
89 if (entry->file_info().is_directory())
90 return FILE_ERROR_NOT_A_FILE;
92 // For a hosted document, we create a special JSON file to represent the
93 // document instead of fetching the document content in one of the exported
94 // formats. The JSON file contains the edit URL and resource ID of the
95 // document.
96 if (entry->file_specific_info().is_hosted_document()) {
97 base::FilePath::StringType extension = base::FilePath::FromUTF8Unsafe(
98 entry->file_specific_info().document_extension()).value();
99 base::FilePath gdoc_file_path;
100 base::File::Info file_info;
101 // We add the gdoc file extension in the temporary file, so that in cross
102 // profile drag-and-drop between Drive folders, the destination profiles's
103 // CopyOperation can detect the special JSON file only by the path.
104 if (!GeneratesUniquePathWithExtension(temporary_file_directory,
105 extension,
106 &gdoc_file_path) ||
107 !util::CreateGDocFile(gdoc_file_path,
108 GURL(entry->file_specific_info().alternate_url()),
109 entry->resource_id()) ||
110 !base::GetFileInfo(gdoc_file_path,
111 reinterpret_cast<base::File::Info*>(&file_info)))
112 return FILE_ERROR_FAILED;
114 *cache_file_path = gdoc_file_path;
115 entry->mutable_file_info()->set_size(file_info.size);
116 return FILE_ERROR_OK;
119 if (!entry->file_specific_info().cache_state().is_present()) {
120 // This file has no cache file.
121 if (!entry->resource_id().empty()) {
122 // This entry exists on the server, leave |cache_file_path| empty to
123 // start download.
124 return PrepareForDownloadFile(cache, entry->file_info().size(),
125 temporary_file_directory,
126 temp_download_file_path);
129 // This entry does not exist on the server, store an empty file and mark it
130 // as dirty.
131 base::FilePath empty_file;
132 if (!base::CreateTemporaryFileInDir(temporary_file_directory, &empty_file))
133 return FILE_ERROR_FAILED;
134 error = cache->Store(local_id, std::string(), empty_file,
135 internal::FileCache::FILE_OPERATION_MOVE);
136 if (error != FILE_ERROR_OK)
137 return error;
139 error = metadata->GetResourceEntryById(local_id, entry);
140 if (error != FILE_ERROR_OK)
141 return error;
144 // Leave |cache_file_path| empty when the stored file is obsolete and has no
145 // local modification.
146 if (!entry->file_specific_info().cache_state().is_dirty() &&
147 entry->file_specific_info().md5() !=
148 entry->file_specific_info().cache_state().md5()) {
149 return PrepareForDownloadFile(cache, entry->file_info().size(),
150 temporary_file_directory,
151 temp_download_file_path);
154 // Fill |cache_file_path| with the path to the cached file.
155 error = cache->GetFile(local_id, cache_file_path);
156 if (error != FILE_ERROR_OK)
157 return error;
159 // If the cache file is to be returned as the download result, the file info
160 // of the cache needs to be returned via |entry|.
161 // TODO(kinaba): crbug.com/246469. The logic below is similar to that in
162 // drive::FileSystem::CheckLocalModificationAndRun. We should merge them.
163 base::File::Info file_info;
164 if (base::GetFileInfo(*cache_file_path, &file_info))
165 entry->mutable_file_info()->set_size(file_info.size);
167 return FILE_ERROR_OK;
170 struct CheckPreconditionForEnsureFileDownloadedParams {
171 internal::ResourceMetadata* metadata;
172 internal::FileCache* cache;
173 base::FilePath temporary_file_directory;
176 // Calls CheckPreConditionForEnsureFileDownloaded() with the entry specified by
177 // the given ID. Also fills |drive_file_path| with the path of the entry.
178 FileError CheckPreConditionForEnsureFileDownloadedByLocalId(
179 const CheckPreconditionForEnsureFileDownloadedParams& params,
180 const std::string& local_id,
181 base::FilePath* drive_file_path,
182 base::FilePath* cache_file_path,
183 base::FilePath* temp_download_file_path,
184 ResourceEntry* entry) {
185 FileError error = params.metadata->GetFilePath(local_id, drive_file_path);
186 if (error != FILE_ERROR_OK)
187 return error;
188 return CheckPreConditionForEnsureFileDownloaded(
189 params.metadata, params.cache, params.temporary_file_directory, local_id,
190 entry, cache_file_path, temp_download_file_path);
193 // Calls CheckPreConditionForEnsureFileDownloaded() with the entry specified by
194 // the given file path.
195 FileError CheckPreConditionForEnsureFileDownloadedByPath(
196 const CheckPreconditionForEnsureFileDownloadedParams& params,
197 const base::FilePath& file_path,
198 base::FilePath* cache_file_path,
199 base::FilePath* temp_download_file_path,
200 ResourceEntry* entry) {
201 std::string local_id;
202 FileError error = params.metadata->GetIdByPath(file_path, &local_id);
203 if (error != FILE_ERROR_OK)
204 return error;
205 return CheckPreConditionForEnsureFileDownloaded(
206 params.metadata, params.cache, params.temporary_file_directory, local_id,
207 entry, cache_file_path, temp_download_file_path);
210 // Stores the downloaded file at |downloaded_file_path| into |cache|.
211 // If succeeded, returns FILE_ERROR_OK with |cache_file_path| storing the
212 // path to the cache file.
213 // If failed, returns an error code with deleting |downloaded_file_path|.
214 FileError UpdateLocalStateForDownloadFile(
215 internal::ResourceMetadata* metadata,
216 internal::FileCache* cache,
217 const ResourceEntry& entry_before_download,
218 google_apis::DriveApiErrorCode gdata_error,
219 const base::FilePath& downloaded_file_path,
220 ResourceEntry* entry_after_update,
221 base::FilePath* cache_file_path) {
222 DCHECK(cache);
224 // Downloaded file should be deleted on errors.
225 base::ScopedClosureRunner file_deleter(base::Bind(
226 base::IgnoreResult(&base::DeleteFile),
227 downloaded_file_path, false /* recursive */));
229 FileError error = GDataToFileError(gdata_error);
230 if (error != FILE_ERROR_OK)
231 return error;
233 const std::string& local_id = entry_before_download.local_id();
235 // Do not overwrite locally edited file with server side contents.
236 ResourceEntry entry;
237 error = metadata->GetResourceEntryById(local_id, &entry);
238 if (error != FILE_ERROR_OK)
239 return error;
240 if (entry.file_specific_info().cache_state().is_dirty())
241 return FILE_ERROR_IN_USE;
243 // Here the download is completed successfully, so store it into the cache.
244 error = cache->Store(local_id,
245 entry_before_download.file_specific_info().md5(),
246 downloaded_file_path,
247 internal::FileCache::FILE_OPERATION_MOVE);
248 if (error != FILE_ERROR_OK)
249 return error;
250 base::Closure unused_file_deleter_closure = file_deleter.Release();
252 error = metadata->GetResourceEntryById(local_id, entry_after_update);
253 if (error != FILE_ERROR_OK)
254 return error;
256 return cache->GetFile(local_id, cache_file_path);
259 } // namespace
261 class DownloadOperation::DownloadParams {
262 public:
263 DownloadParams(
264 const GetFileContentInitializedCallback initialized_callback,
265 const google_apis::GetContentCallback get_content_callback,
266 const GetFileCallback completion_callback,
267 scoped_ptr<ResourceEntry> entry)
268 : initialized_callback_(initialized_callback),
269 get_content_callback_(get_content_callback),
270 completion_callback_(completion_callback),
271 entry_(entry.Pass()),
272 was_cancelled_(false),
273 weak_ptr_factory_(this) {
274 DCHECK(!completion_callback_.is_null());
275 DCHECK(entry_);
278 base::Closure GetCancelClosure() {
279 return base::Bind(&DownloadParams::Cancel, weak_ptr_factory_.GetWeakPtr());
282 void OnCacheFileFound(const base::FilePath& cache_file_path) {
283 if (!initialized_callback_.is_null()) {
284 initialized_callback_.Run(FILE_ERROR_OK, cache_file_path,
285 make_scoped_ptr(new ResourceEntry(*entry_)));
287 completion_callback_.Run(FILE_ERROR_OK, cache_file_path, entry_.Pass());
290 void OnStartDownloading(const base::Closure& cancel_download_closure) {
291 cancel_download_closure_ = cancel_download_closure;
292 if (initialized_callback_.is_null()) {
293 return;
296 DCHECK(entry_);
297 initialized_callback_.Run(FILE_ERROR_OK, base::FilePath(),
298 make_scoped_ptr(new ResourceEntry(*entry_)));
301 void OnError(FileError error) const {
302 completion_callback_.Run(
303 error, base::FilePath(), scoped_ptr<ResourceEntry>());
306 void OnDownloadCompleted(const base::FilePath& cache_file_path,
307 scoped_ptr<ResourceEntry> entry) const {
308 completion_callback_.Run(FILE_ERROR_OK, cache_file_path, entry.Pass());
311 const google_apis::GetContentCallback& get_content_callback() const {
312 return get_content_callback_;
315 const ResourceEntry& entry() const { return *entry_; }
317 bool was_cancelled() const { return was_cancelled_; }
319 private:
320 void Cancel() {
321 was_cancelled_ = true;
322 if (!cancel_download_closure_.is_null())
323 cancel_download_closure_.Run();
326 const GetFileContentInitializedCallback initialized_callback_;
327 const google_apis::GetContentCallback get_content_callback_;
328 const GetFileCallback completion_callback_;
330 scoped_ptr<ResourceEntry> entry_;
331 base::Closure cancel_download_closure_;
332 bool was_cancelled_;
334 base::WeakPtrFactory<DownloadParams> weak_ptr_factory_;
335 DISALLOW_COPY_AND_ASSIGN(DownloadParams);
338 DownloadOperation::DownloadOperation(
339 base::SequencedTaskRunner* blocking_task_runner,
340 OperationDelegate* delegate,
341 JobScheduler* scheduler,
342 internal::ResourceMetadata* metadata,
343 internal::FileCache* cache,
344 const base::FilePath& temporary_file_directory)
345 : blocking_task_runner_(blocking_task_runner),
346 delegate_(delegate),
347 scheduler_(scheduler),
348 metadata_(metadata),
349 cache_(cache),
350 temporary_file_directory_(temporary_file_directory),
351 weak_ptr_factory_(this) {
354 DownloadOperation::~DownloadOperation() {
357 base::Closure DownloadOperation::EnsureFileDownloadedByLocalId(
358 const std::string& local_id,
359 const ClientContext& context,
360 const GetFileContentInitializedCallback& initialized_callback,
361 const google_apis::GetContentCallback& get_content_callback,
362 const GetFileCallback& completion_callback) {
363 DCHECK_CURRENTLY_ON(BrowserThread::UI);
364 DCHECK(!completion_callback.is_null());
366 CheckPreconditionForEnsureFileDownloadedParams params;
367 params.metadata = metadata_;
368 params.cache = cache_;
369 params.temporary_file_directory = temporary_file_directory_;
370 base::FilePath* drive_file_path = new base::FilePath;
371 base::FilePath* cache_file_path = new base::FilePath;
372 base::FilePath* temp_download_file_path = new base::FilePath;
373 ResourceEntry* entry = new ResourceEntry;
374 scoped_ptr<DownloadParams> download_params(new DownloadParams(
375 initialized_callback, get_content_callback, completion_callback,
376 make_scoped_ptr(entry)));
377 base::Closure cancel_closure = download_params->GetCancelClosure();
378 base::PostTaskAndReplyWithResult(
379 blocking_task_runner_.get(),
380 FROM_HERE,
381 base::Bind(&CheckPreConditionForEnsureFileDownloadedByLocalId,
382 params,
383 local_id,
384 drive_file_path,
385 cache_file_path,
386 temp_download_file_path,
387 entry),
388 base::Bind(&DownloadOperation::EnsureFileDownloadedAfterCheckPreCondition,
389 weak_ptr_factory_.GetWeakPtr(),
390 base::Passed(&download_params),
391 context,
392 base::Owned(drive_file_path),
393 base::Owned(cache_file_path),
394 base::Owned(temp_download_file_path)));
395 return cancel_closure;
398 base::Closure DownloadOperation::EnsureFileDownloadedByPath(
399 const base::FilePath& file_path,
400 const ClientContext& context,
401 const GetFileContentInitializedCallback& initialized_callback,
402 const google_apis::GetContentCallback& get_content_callback,
403 const GetFileCallback& completion_callback) {
404 DCHECK_CURRENTLY_ON(BrowserThread::UI);
405 DCHECK(!completion_callback.is_null());
407 CheckPreconditionForEnsureFileDownloadedParams params;
408 params.metadata = metadata_;
409 params.cache = cache_;
410 params.temporary_file_directory = temporary_file_directory_;
411 base::FilePath* drive_file_path = new base::FilePath(file_path);
412 base::FilePath* cache_file_path = new base::FilePath;
413 base::FilePath* temp_download_file_path = new base::FilePath;
414 ResourceEntry* entry = new ResourceEntry;
415 scoped_ptr<DownloadParams> download_params(new DownloadParams(
416 initialized_callback, get_content_callback, completion_callback,
417 make_scoped_ptr(entry)));
418 base::Closure cancel_closure = download_params->GetCancelClosure();
419 base::PostTaskAndReplyWithResult(
420 blocking_task_runner_.get(),
421 FROM_HERE,
422 base::Bind(&CheckPreConditionForEnsureFileDownloadedByPath,
423 params,
424 file_path,
425 cache_file_path,
426 temp_download_file_path,
427 entry),
428 base::Bind(&DownloadOperation::EnsureFileDownloadedAfterCheckPreCondition,
429 weak_ptr_factory_.GetWeakPtr(),
430 base::Passed(&download_params),
431 context,
432 base::Owned(drive_file_path),
433 base::Owned(cache_file_path),
434 base::Owned(temp_download_file_path)));
435 return cancel_closure;
438 void DownloadOperation::EnsureFileDownloadedAfterCheckPreCondition(
439 scoped_ptr<DownloadParams> params,
440 const ClientContext& context,
441 base::FilePath* drive_file_path,
442 base::FilePath* cache_file_path,
443 base::FilePath* temp_download_file_path,
444 FileError error) {
445 DCHECK_CURRENTLY_ON(BrowserThread::UI);
446 DCHECK(params);
447 DCHECK(drive_file_path);
448 DCHECK(cache_file_path);
450 if (error != FILE_ERROR_OK) {
451 // During precondition check, an error is found.
452 params->OnError(error);
453 return;
456 if (!cache_file_path->empty()) {
457 // The cache file is found.
458 params->OnCacheFileFound(*cache_file_path);
459 return;
462 if (params->was_cancelled()) {
463 params->OnError(FILE_ERROR_ABORT);
464 return;
467 DCHECK(!params->entry().resource_id().empty());
468 DownloadParams* params_ptr = params.get();
469 JobID id = scheduler_->DownloadFile(
470 *drive_file_path,
471 params_ptr->entry().file_info().size(),
472 *temp_download_file_path,
473 params_ptr->entry().resource_id(),
474 context,
475 base::Bind(&DownloadOperation::EnsureFileDownloadedAfterDownloadFile,
476 weak_ptr_factory_.GetWeakPtr(),
477 *drive_file_path,
478 base::Passed(&params)),
479 params_ptr->get_content_callback());
481 // Notify via |initialized_callback| if necessary.
482 params_ptr->OnStartDownloading(
483 base::Bind(&DownloadOperation::CancelJob,
484 weak_ptr_factory_.GetWeakPtr(), id));
487 void DownloadOperation::EnsureFileDownloadedAfterDownloadFile(
488 const base::FilePath& drive_file_path,
489 scoped_ptr<DownloadParams> params,
490 google_apis::DriveApiErrorCode gdata_error,
491 const base::FilePath& downloaded_file_path) {
492 DCHECK_CURRENTLY_ON(BrowserThread::UI);
494 DownloadParams* params_ptr = params.get();
495 ResourceEntry* entry_after_update = new ResourceEntry;
496 base::FilePath* cache_file_path = new base::FilePath;
497 base::PostTaskAndReplyWithResult(
498 blocking_task_runner_.get(),
499 FROM_HERE,
500 base::Bind(&UpdateLocalStateForDownloadFile,
501 metadata_,
502 cache_,
503 params_ptr->entry(),
504 gdata_error,
505 downloaded_file_path,
506 entry_after_update,
507 cache_file_path),
508 base::Bind(&DownloadOperation::EnsureFileDownloadedAfterUpdateLocalState,
509 weak_ptr_factory_.GetWeakPtr(),
510 drive_file_path,
511 base::Passed(&params),
512 base::Passed(make_scoped_ptr(entry_after_update)),
513 base::Owned(cache_file_path)));
516 void DownloadOperation::EnsureFileDownloadedAfterUpdateLocalState(
517 const base::FilePath& file_path,
518 scoped_ptr<DownloadParams> params,
519 scoped_ptr<ResourceEntry> entry_after_update,
520 base::FilePath* cache_file_path,
521 FileError error) {
522 DCHECK_CURRENTLY_ON(BrowserThread::UI);
524 if (error != FILE_ERROR_OK) {
525 params->OnError(error);
526 return;
528 DCHECK(!entry_after_update->file_info().is_directory());
530 FileChange changed_files;
531 changed_files.Update(
532 file_path, FileChange::FILE_TYPE_FILE, FileChange::ADD_OR_UPDATE);
533 // Storing to cache changes the "offline available" status, hence notify.
534 delegate_->OnFileChangedByOperation(changed_files);
535 params->OnDownloadCompleted(*cache_file_path, entry_after_update.Pass());
538 void DownloadOperation::CancelJob(JobID job_id) {
539 scheduler_->CancelJob(job_id);
542 } // namespace file_system
543 } // namespace drive