BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / components / drive / file_system / download_operation.cc
blob150009c630caa307a47d95e5aa34dfd37c9e1256
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 "components/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 "components/drive/drive.pb.h"
13 #include "components/drive/file_cache.h"
14 #include "components/drive/file_change.h"
15 #include "components/drive/file_errors.h"
16 #include "components/drive/file_system/operation_delegate.h"
17 #include "components/drive/file_system_core_util.h"
18 #include "components/drive/job_scheduler.h"
19 #include "components/drive/resource_metadata.h"
20 #include "google_apis/drive/drive_api_error_codes.h"
22 namespace drive {
23 namespace file_system {
24 namespace {
26 // Generates an unused file path with |extension| to |out_path|, as a descendant
27 // of |dir|, with its parent directory created.
28 bool GeneratesUniquePathWithExtension(
29 const base::FilePath& dir,
30 const base::FilePath::StringType& extension,
31 base::FilePath* out_path) {
32 base::FilePath subdir;
33 if (!base::CreateTemporaryDirInDir(dir, base::FilePath::StringType(),
34 &subdir)) {
35 return false;
37 *out_path = subdir.Append(FILE_PATH_LITERAL("tmp") + extension);
38 return true;
41 // Prepares for downloading the file. Allocates the enough space for the file
42 // in the cache.
43 // If succeeded, returns FILE_ERROR_OK with |temp_download_file| storing the
44 // path to the file in the cache.
45 FileError PrepareForDownloadFile(internal::FileCache* cache,
46 int64 expected_file_size,
47 const base::FilePath& temporary_file_directory,
48 base::FilePath* temp_download_file) {
49 DCHECK(cache);
50 DCHECK(temp_download_file);
52 // Ensure enough space in the cache.
53 if (!cache->FreeDiskSpaceIfNeededFor(expected_file_size))
54 return FILE_ERROR_NO_LOCAL_SPACE;
56 return base::CreateTemporaryFileInDir(
57 temporary_file_directory,
58 temp_download_file) ? FILE_ERROR_OK : FILE_ERROR_FAILED;
61 // If the resource is a hosted document, creates a JSON file representing the
62 // resource locally, and returns FILE_ERROR_OK with |cache_file_path| storing
63 // the path to the JSON file.
64 // If the resource is a regular file and its local cache is available,
65 // returns FILE_ERROR_OK with |cache_file_path| storing the path to the
66 // cache file.
67 // If the resource is a regular file but its local cache is NOT available,
68 // returns FILE_ERROR_OK, but |cache_file_path| is kept empty.
69 // Otherwise returns error code.
70 FileError CheckPreConditionForEnsureFileDownloaded(
71 internal::ResourceMetadata* metadata,
72 internal::FileCache* cache,
73 const base::FilePath& temporary_file_directory,
74 const std::string& local_id,
75 ResourceEntry* entry,
76 base::FilePath* cache_file_path,
77 base::FilePath* temp_download_file_path) {
78 DCHECK(metadata);
79 DCHECK(cache);
80 DCHECK(cache_file_path);
82 FileError error = metadata->GetResourceEntryById(local_id, entry);
83 if (error != FILE_ERROR_OK)
84 return error;
86 if (entry->file_info().is_directory())
87 return FILE_ERROR_NOT_A_FILE;
89 // For a hosted document, we create a special JSON file to represent the
90 // document instead of fetching the document content in one of the exported
91 // formats. The JSON file contains the edit URL and resource ID of the
92 // document.
93 if (entry->file_specific_info().is_hosted_document()) {
94 base::FilePath::StringType extension = base::FilePath::FromUTF8Unsafe(
95 entry->file_specific_info().document_extension()).value();
96 base::FilePath gdoc_file_path;
97 base::File::Info file_info;
98 // We add the gdoc file extension in the temporary file, so that in cross
99 // profile drag-and-drop between Drive folders, the destination profiles's
100 // CopyOperation can detect the special JSON file only by the path.
101 if (!GeneratesUniquePathWithExtension(temporary_file_directory,
102 extension,
103 &gdoc_file_path) ||
104 !util::CreateGDocFile(gdoc_file_path,
105 GURL(entry->file_specific_info().alternate_url()),
106 entry->resource_id()) ||
107 !base::GetFileInfo(gdoc_file_path,
108 reinterpret_cast<base::File::Info*>(&file_info)))
109 return FILE_ERROR_FAILED;
111 *cache_file_path = gdoc_file_path;
112 entry->mutable_file_info()->set_size(file_info.size);
113 return FILE_ERROR_OK;
116 if (!entry->file_specific_info().cache_state().is_present()) {
117 // This file has no cache file.
118 if (!entry->resource_id().empty()) {
119 // This entry exists on the server, leave |cache_file_path| empty to
120 // start download.
121 return PrepareForDownloadFile(cache, entry->file_info().size(),
122 temporary_file_directory,
123 temp_download_file_path);
126 // This entry does not exist on the server, store an empty file and mark it
127 // as dirty.
128 base::FilePath empty_file;
129 if (!base::CreateTemporaryFileInDir(temporary_file_directory, &empty_file))
130 return FILE_ERROR_FAILED;
131 error = cache->Store(local_id, std::string(), empty_file,
132 internal::FileCache::FILE_OPERATION_MOVE);
133 if (error != FILE_ERROR_OK)
134 return error;
136 error = metadata->GetResourceEntryById(local_id, entry);
137 if (error != FILE_ERROR_OK)
138 return error;
141 // Leave |cache_file_path| empty when the stored file is obsolete and has no
142 // local modification.
143 if (!entry->file_specific_info().cache_state().is_dirty() &&
144 entry->file_specific_info().md5() !=
145 entry->file_specific_info().cache_state().md5()) {
146 return PrepareForDownloadFile(cache, entry->file_info().size(),
147 temporary_file_directory,
148 temp_download_file_path);
151 // Fill |cache_file_path| with the path to the cached file.
152 error = cache->GetFile(local_id, cache_file_path);
153 if (error != FILE_ERROR_OK)
154 return error;
156 // If the cache file is to be returned as the download result, the file info
157 // of the cache needs to be returned via |entry|.
158 // TODO(kinaba): crbug.com/246469. The logic below is similar to that in
159 // drive::FileSystem::CheckLocalModificationAndRun. We should merge them.
160 base::File::Info file_info;
161 if (base::GetFileInfo(*cache_file_path, &file_info))
162 entry->mutable_file_info()->set_size(file_info.size);
164 return FILE_ERROR_OK;
167 struct CheckPreconditionForEnsureFileDownloadedParams {
168 internal::ResourceMetadata* metadata;
169 internal::FileCache* cache;
170 base::FilePath temporary_file_directory;
173 // Calls CheckPreConditionForEnsureFileDownloaded() with the entry specified by
174 // the given ID. Also fills |drive_file_path| with the path of the entry.
175 FileError CheckPreConditionForEnsureFileDownloadedByLocalId(
176 const CheckPreconditionForEnsureFileDownloadedParams& params,
177 const std::string& local_id,
178 base::FilePath* drive_file_path,
179 base::FilePath* cache_file_path,
180 base::FilePath* temp_download_file_path,
181 ResourceEntry* entry) {
182 FileError error = params.metadata->GetFilePath(local_id, drive_file_path);
183 if (error != FILE_ERROR_OK)
184 return error;
185 return CheckPreConditionForEnsureFileDownloaded(
186 params.metadata, params.cache, params.temporary_file_directory, local_id,
187 entry, cache_file_path, temp_download_file_path);
190 // Calls CheckPreConditionForEnsureFileDownloaded() with the entry specified by
191 // the given file path.
192 FileError CheckPreConditionForEnsureFileDownloadedByPath(
193 const CheckPreconditionForEnsureFileDownloadedParams& params,
194 const base::FilePath& file_path,
195 base::FilePath* cache_file_path,
196 base::FilePath* temp_download_file_path,
197 ResourceEntry* entry) {
198 std::string local_id;
199 FileError error = params.metadata->GetIdByPath(file_path, &local_id);
200 if (error != FILE_ERROR_OK)
201 return error;
202 return CheckPreConditionForEnsureFileDownloaded(
203 params.metadata, params.cache, params.temporary_file_directory, local_id,
204 entry, cache_file_path, temp_download_file_path);
207 // Stores the downloaded file at |downloaded_file_path| into |cache|.
208 // If succeeded, returns FILE_ERROR_OK with |cache_file_path| storing the
209 // path to the cache file.
210 // If failed, returns an error code with deleting |downloaded_file_path|.
211 FileError UpdateLocalStateForDownloadFile(
212 internal::ResourceMetadata* metadata,
213 internal::FileCache* cache,
214 const ResourceEntry& entry_before_download,
215 google_apis::DriveApiErrorCode gdata_error,
216 const base::FilePath& downloaded_file_path,
217 ResourceEntry* entry_after_update,
218 base::FilePath* cache_file_path) {
219 DCHECK(cache);
221 // Downloaded file should be deleted on errors.
222 base::ScopedClosureRunner file_deleter(base::Bind(
223 base::IgnoreResult(&base::DeleteFile),
224 downloaded_file_path, false /* recursive */));
226 FileError error = GDataToFileError(gdata_error);
227 if (error != FILE_ERROR_OK)
228 return error;
230 const std::string& local_id = entry_before_download.local_id();
232 // Do not overwrite locally edited file with server side contents.
233 ResourceEntry entry;
234 error = metadata->GetResourceEntryById(local_id, &entry);
235 if (error != FILE_ERROR_OK)
236 return error;
237 if (entry.file_specific_info().cache_state().is_dirty())
238 return FILE_ERROR_IN_USE;
240 // Here the download is completed successfully, so store it into the cache.
241 error = cache->Store(local_id,
242 entry_before_download.file_specific_info().md5(),
243 downloaded_file_path,
244 internal::FileCache::FILE_OPERATION_MOVE);
245 if (error != FILE_ERROR_OK)
246 return error;
247 base::Closure unused_file_deleter_closure = file_deleter.Release();
249 error = metadata->GetResourceEntryById(local_id, entry_after_update);
250 if (error != FILE_ERROR_OK)
251 return error;
253 return cache->GetFile(local_id, cache_file_path);
256 } // namespace
258 class DownloadOperation::DownloadParams {
259 public:
260 DownloadParams(
261 const GetFileContentInitializedCallback initialized_callback,
262 const google_apis::GetContentCallback get_content_callback,
263 const GetFileCallback completion_callback,
264 scoped_ptr<ResourceEntry> entry)
265 : initialized_callback_(initialized_callback),
266 get_content_callback_(get_content_callback),
267 completion_callback_(completion_callback),
268 entry_(entry.Pass()),
269 was_cancelled_(false),
270 weak_ptr_factory_(this) {
271 DCHECK(!completion_callback_.is_null());
272 DCHECK(entry_);
275 base::Closure GetCancelClosure() {
276 return base::Bind(&DownloadParams::Cancel, weak_ptr_factory_.GetWeakPtr());
279 void OnCacheFileFound(const base::FilePath& cache_file_path) {
280 if (!initialized_callback_.is_null()) {
281 initialized_callback_.Run(FILE_ERROR_OK, cache_file_path,
282 make_scoped_ptr(new ResourceEntry(*entry_)));
284 completion_callback_.Run(FILE_ERROR_OK, cache_file_path, entry_.Pass());
287 void OnStartDownloading(const base::Closure& cancel_download_closure) {
288 cancel_download_closure_ = cancel_download_closure;
289 if (initialized_callback_.is_null()) {
290 return;
293 DCHECK(entry_);
294 initialized_callback_.Run(FILE_ERROR_OK, base::FilePath(),
295 make_scoped_ptr(new ResourceEntry(*entry_)));
298 void OnError(FileError error) const {
299 completion_callback_.Run(
300 error, base::FilePath(), scoped_ptr<ResourceEntry>());
303 void OnDownloadCompleted(const base::FilePath& cache_file_path,
304 scoped_ptr<ResourceEntry> entry) const {
305 completion_callback_.Run(FILE_ERROR_OK, cache_file_path, entry.Pass());
308 const google_apis::GetContentCallback& get_content_callback() const {
309 return get_content_callback_;
312 const ResourceEntry& entry() const { return *entry_; }
314 bool was_cancelled() const { return was_cancelled_; }
316 private:
317 void Cancel() {
318 was_cancelled_ = true;
319 if (!cancel_download_closure_.is_null())
320 cancel_download_closure_.Run();
323 const GetFileContentInitializedCallback initialized_callback_;
324 const google_apis::GetContentCallback get_content_callback_;
325 const GetFileCallback completion_callback_;
327 scoped_ptr<ResourceEntry> entry_;
328 base::Closure cancel_download_closure_;
329 bool was_cancelled_;
331 base::WeakPtrFactory<DownloadParams> weak_ptr_factory_;
332 DISALLOW_COPY_AND_ASSIGN(DownloadParams);
335 DownloadOperation::DownloadOperation(
336 base::SequencedTaskRunner* blocking_task_runner,
337 OperationDelegate* delegate,
338 JobScheduler* scheduler,
339 internal::ResourceMetadata* metadata,
340 internal::FileCache* cache,
341 const base::FilePath& temporary_file_directory)
342 : blocking_task_runner_(blocking_task_runner),
343 delegate_(delegate),
344 scheduler_(scheduler),
345 metadata_(metadata),
346 cache_(cache),
347 temporary_file_directory_(temporary_file_directory),
348 weak_ptr_factory_(this) {
351 DownloadOperation::~DownloadOperation() {
354 base::Closure DownloadOperation::EnsureFileDownloadedByLocalId(
355 const std::string& local_id,
356 const ClientContext& context,
357 const GetFileContentInitializedCallback& initialized_callback,
358 const google_apis::GetContentCallback& get_content_callback,
359 const GetFileCallback& completion_callback) {
360 DCHECK(thread_checker_.CalledOnValidThread());
361 DCHECK(!completion_callback.is_null());
363 CheckPreconditionForEnsureFileDownloadedParams params;
364 params.metadata = metadata_;
365 params.cache = cache_;
366 params.temporary_file_directory = temporary_file_directory_;
367 base::FilePath* drive_file_path = new base::FilePath;
368 base::FilePath* cache_file_path = new base::FilePath;
369 base::FilePath* temp_download_file_path = new base::FilePath;
370 ResourceEntry* entry = new ResourceEntry;
371 scoped_ptr<DownloadParams> download_params(new DownloadParams(
372 initialized_callback, get_content_callback, completion_callback,
373 make_scoped_ptr(entry)));
374 base::Closure cancel_closure = download_params->GetCancelClosure();
375 base::PostTaskAndReplyWithResult(
376 blocking_task_runner_.get(),
377 FROM_HERE,
378 base::Bind(&CheckPreConditionForEnsureFileDownloadedByLocalId,
379 params,
380 local_id,
381 drive_file_path,
382 cache_file_path,
383 temp_download_file_path,
384 entry),
385 base::Bind(&DownloadOperation::EnsureFileDownloadedAfterCheckPreCondition,
386 weak_ptr_factory_.GetWeakPtr(),
387 base::Passed(&download_params),
388 context,
389 base::Owned(drive_file_path),
390 base::Owned(cache_file_path),
391 base::Owned(temp_download_file_path)));
392 return cancel_closure;
395 base::Closure DownloadOperation::EnsureFileDownloadedByPath(
396 const base::FilePath& file_path,
397 const ClientContext& context,
398 const GetFileContentInitializedCallback& initialized_callback,
399 const google_apis::GetContentCallback& get_content_callback,
400 const GetFileCallback& completion_callback) {
401 DCHECK(thread_checker_.CalledOnValidThread());
402 DCHECK(!completion_callback.is_null());
404 CheckPreconditionForEnsureFileDownloadedParams params;
405 params.metadata = metadata_;
406 params.cache = cache_;
407 params.temporary_file_directory = temporary_file_directory_;
408 base::FilePath* drive_file_path = new base::FilePath(file_path);
409 base::FilePath* cache_file_path = new base::FilePath;
410 base::FilePath* temp_download_file_path = new base::FilePath;
411 ResourceEntry* entry = new ResourceEntry;
412 scoped_ptr<DownloadParams> download_params(new DownloadParams(
413 initialized_callback, get_content_callback, completion_callback,
414 make_scoped_ptr(entry)));
415 base::Closure cancel_closure = download_params->GetCancelClosure();
416 base::PostTaskAndReplyWithResult(
417 blocking_task_runner_.get(),
418 FROM_HERE,
419 base::Bind(&CheckPreConditionForEnsureFileDownloadedByPath,
420 params,
421 file_path,
422 cache_file_path,
423 temp_download_file_path,
424 entry),
425 base::Bind(&DownloadOperation::EnsureFileDownloadedAfterCheckPreCondition,
426 weak_ptr_factory_.GetWeakPtr(),
427 base::Passed(&download_params),
428 context,
429 base::Owned(drive_file_path),
430 base::Owned(cache_file_path),
431 base::Owned(temp_download_file_path)));
432 return cancel_closure;
435 void DownloadOperation::EnsureFileDownloadedAfterCheckPreCondition(
436 scoped_ptr<DownloadParams> params,
437 const ClientContext& context,
438 base::FilePath* drive_file_path,
439 base::FilePath* cache_file_path,
440 base::FilePath* temp_download_file_path,
441 FileError error) {
442 DCHECK(thread_checker_.CalledOnValidThread());
443 DCHECK(params);
444 DCHECK(drive_file_path);
445 DCHECK(cache_file_path);
447 if (error != FILE_ERROR_OK) {
448 // During precondition check, an error is found.
449 params->OnError(error);
450 return;
453 if (!cache_file_path->empty()) {
454 // The cache file is found.
455 params->OnCacheFileFound(*cache_file_path);
456 return;
459 if (params->was_cancelled()) {
460 params->OnError(FILE_ERROR_ABORT);
461 return;
464 DCHECK(!params->entry().resource_id().empty());
465 DownloadParams* params_ptr = params.get();
466 JobID id = scheduler_->DownloadFile(
467 *drive_file_path,
468 params_ptr->entry().file_info().size(),
469 *temp_download_file_path,
470 params_ptr->entry().resource_id(),
471 context,
472 base::Bind(&DownloadOperation::EnsureFileDownloadedAfterDownloadFile,
473 weak_ptr_factory_.GetWeakPtr(),
474 *drive_file_path,
475 base::Passed(&params)),
476 params_ptr->get_content_callback());
478 // Notify via |initialized_callback| if necessary.
479 params_ptr->OnStartDownloading(
480 base::Bind(&DownloadOperation::CancelJob,
481 weak_ptr_factory_.GetWeakPtr(), id));
484 void DownloadOperation::EnsureFileDownloadedAfterDownloadFile(
485 const base::FilePath& drive_file_path,
486 scoped_ptr<DownloadParams> params,
487 google_apis::DriveApiErrorCode gdata_error,
488 const base::FilePath& downloaded_file_path) {
489 DCHECK(thread_checker_.CalledOnValidThread());
491 DownloadParams* params_ptr = params.get();
492 ResourceEntry* entry_after_update = new ResourceEntry;
493 base::FilePath* cache_file_path = new base::FilePath;
494 base::PostTaskAndReplyWithResult(
495 blocking_task_runner_.get(),
496 FROM_HERE,
497 base::Bind(&UpdateLocalStateForDownloadFile,
498 metadata_,
499 cache_,
500 params_ptr->entry(),
501 gdata_error,
502 downloaded_file_path,
503 entry_after_update,
504 cache_file_path),
505 base::Bind(&DownloadOperation::EnsureFileDownloadedAfterUpdateLocalState,
506 weak_ptr_factory_.GetWeakPtr(),
507 drive_file_path,
508 base::Passed(&params),
509 base::Passed(make_scoped_ptr(entry_after_update)),
510 base::Owned(cache_file_path)));
513 void DownloadOperation::EnsureFileDownloadedAfterUpdateLocalState(
514 const base::FilePath& file_path,
515 scoped_ptr<DownloadParams> params,
516 scoped_ptr<ResourceEntry> entry_after_update,
517 base::FilePath* cache_file_path,
518 FileError error) {
519 DCHECK(thread_checker_.CalledOnValidThread());
521 if (error != FILE_ERROR_OK) {
522 params->OnError(error);
523 return;
525 DCHECK(!entry_after_update->file_info().is_directory());
527 FileChange changed_files;
528 changed_files.Update(file_path, FileChange::FILE_TYPE_FILE,
529 FileChange::CHANGE_TYPE_ADD_OR_UPDATE);
530 // Storing to cache changes the "offline available" status, hence notify.
531 delegate_->OnFileChangedByOperation(changed_files);
532 params->OnDownloadCompleted(*cache_file_path, entry_after_update.Pass());
535 void DownloadOperation::CancelJob(JobID job_id) {
536 scheduler_->CancelJob(job_id);
539 } // namespace file_system
540 } // namespace drive