Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / file_system / download_operation.cc
index 0df9748..f1f2af9 100644 (file)
@@ -4,9 +4,11 @@
 
 #include "chrome/browser/chromeos/drive/file_system/download_operation.h"
 
+#include "base/callback_helpers.h"
 #include "base/file_util.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/strings/stringprintf.h"
 #include "base/task_runner_util.h"
 #include "chrome/browser/chromeos/drive/drive.pb.h"
 #include "chrome/browser/chromeos/drive/file_cache.h"
@@ -25,6 +27,21 @@ namespace drive {
 namespace file_system {
 namespace {
 
+// Generates an unused file path with |extension| to |out_path|, as a descendant
+// of |dir|, with its parent directory created.
+bool GeneratesUniquePathWithExtension(
+    const base::FilePath& dir,
+    const base::FilePath::StringType& extension,
+    base::FilePath* out_path) {
+  base::FilePath subdir;
+  if (!base::CreateTemporaryDirInDir(dir, base::FilePath::StringType(),
+                                     &subdir)) {
+    return false;
+  }
+  *out_path = subdir.Append(FILE_PATH_LITERAL("tmp") + extension);
+  return true;
+}
+
 // If the resource is a hosted document, creates a JSON file representing the
 // resource locally, and returns FILE_ERROR_OK with |cache_file_path| storing
 // the path to the JSON file.
@@ -52,19 +69,22 @@ FileError CheckPreConditionForEnsureFileDownloaded(
   if (entry->file_info().is_directory())
     return FILE_ERROR_NOT_A_FILE;
 
-  // The file's entry should have its file specific info.
-  DCHECK(entry->has_file_specific_info());
-
   // For a hosted document, we create a special JSON file to represent the
   // document instead of fetching the document content in one of the exported
   // formats. The JSON file contains the edit URL and resource ID of the
   // document.
   if (entry->file_specific_info().is_hosted_document()) {
+    base::FilePath::StringType extension = base::FilePath::FromUTF8Unsafe(
+        entry->file_specific_info().document_extension()).value();
     base::FilePath gdoc_file_path;
     // TODO(rvargas): Convert this code to use base::File::Info.
     base::File::Info file_info;
-    if (!base::CreateTemporaryFileInDir(temporary_file_directory,
-                                        &gdoc_file_path) ||
+    // We add the gdoc file extension in the temporary file, so that in cross
+    // profile drag-and-drop between Drive folders, the destination profiles's
+    // CopyOperation can detect the special JSON file only by the path.
+    if (!GeneratesUniquePathWithExtension(temporary_file_directory,
+                                          extension,
+                                          &gdoc_file_path) ||
         !util::CreateGDocFile(gdoc_file_path,
                               GURL(entry->file_specific_info().alternate_url()),
                               entry->resource_id()) ||
@@ -77,10 +97,28 @@ FileError CheckPreConditionForEnsureFileDownloaded(
     return FILE_ERROR_OK;
   }
 
-  // Leave |cache_file_path| empty when no cache entry is found.
   FileCacheEntry cache_entry;
-  if (!cache->GetCacheEntry(local_id, &cache_entry))
-    return FILE_ERROR_OK;
+  if (!cache->GetCacheEntry(local_id, &cache_entry) ||
+      !cache_entry.is_present()) {  // This file has no cache file.
+    if (!entry->resource_id().empty()) {
+      // This entry exists on the server, leave |cache_file_path| empty to
+      // start download.
+      return FILE_ERROR_OK;
+    }
+
+    // This entry does not exist on the server, store an empty file and mark it
+    // as dirty.
+    base::FilePath empty_file;
+    if (!base::CreateTemporaryFileInDir(temporary_file_directory, &empty_file))
+      return FILE_ERROR_FAILED;
+    error = cache->Store(local_id, std::string(), empty_file,
+                         internal::FileCache::FILE_OPERATION_MOVE);
+    if (error != FILE_ERROR_OK)
+      return error;
+
+    if (!cache->GetCacheEntry(local_id, &cache_entry))
+      return FILE_ERROR_NOT_FOUND;
+  }
 
   // Leave |cache_file_path| empty when the stored file is obsolete and has no
   // local modification.
@@ -93,17 +131,13 @@ FileError CheckPreConditionForEnsureFileDownloaded(
   if (error != FILE_ERROR_OK)
     return error;
 
-  // If the cache file is dirty, the modified file info needs to be stored in
-  // |entry|.
-  // TODO(kinaba): crbug.com/246469. The logic below is a duplicate of that in
-  // drive::FileSystem::CheckLocalModificationAndRun. We should merge them once
-  // the drive::FS side is also converted to run fully on blocking pool.
-  if (cache_entry.is_dirty()) {
-    base::File::Info file_info;
-    if (base::GetFileInfo(*cache_file_path,
-                          reinterpret_cast<base::File::Info*>(&file_info)))
-      SetPlatformFileInfoToResourceEntry(file_info, entry);
-  }
+  // If the cache file is to be returned as the download result, the file info
+  // of the cache needs to be returned via |entry|.
+  // TODO(kinaba): crbug.com/246469. The logic below is similar to that in
+  // drive::FileSystem::CheckLocalModificationAndRun. We should merge them.
+  base::File::Info file_info;
+  if (base::GetFileInfo(*cache_file_path, &file_info))
+    SetPlatformFileInfoToResourceEntry(file_info, entry);
 
   return FILE_ERROR_OK;
 }
@@ -193,19 +227,26 @@ FileError UpdateLocalStateForDownloadFile(
     base::FilePath* cache_file_path) {
   DCHECK(cache);
 
+  // Downloaded file should be deleted on errors.
+  base::ScopedClosureRunner file_deleter(base::Bind(
+      base::IgnoreResult(&base::DeleteFile),
+      downloaded_file_path, false /* recursive */));
+
   FileError error = GDataToFileError(gdata_error);
-  if (error != FILE_ERROR_OK) {
-    base::DeleteFile(downloaded_file_path, false /* recursive */);
+  if (error != FILE_ERROR_OK)
     return error;
-  }
+
+  // Do not overwrite locally edited file with server side contents.
+  FileCacheEntry cache_entry;
+  if (cache->GetCacheEntry(local_id, &cache_entry) && cache_entry.is_dirty())
+    return FILE_ERROR_IN_USE;
 
   // Here the download is completed successfully, so store it into the cache.
   error = cache->Store(local_id, md5, downloaded_file_path,
                        internal::FileCache::FILE_OPERATION_MOVE);
-  if (error != FILE_ERROR_OK) {
-    base::DeleteFile(downloaded_file_path, false /* recursive */);
+  if (error != FILE_ERROR_OK)
     return error;
-  }
+  base::Closure unused_file_deleter_closure = file_deleter.Release();
 
   return cache->GetFile(local_id, cache_file_path);
 }
@@ -383,6 +424,8 @@ void DownloadOperation::EnsureFileDownloadedAfterCheckPreCondition(
     return;
   }
 
+  DCHECK(!params->entry().resource_id().empty());
+
   // If cache file is not found, try to download the file from the server
   // instead. Check if we have enough space, based on the expected file size.
   // - if we don't have enough space, try to free up the disk space