- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / file_system / create_file_operation.cc
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.
4
5 #include "chrome/browser/chromeos/drive/file_system/create_file_operation.h"
6
7 #include <string>
8
9 #include "base/file_util.h"
10 #include "chrome/browser/chromeos/drive/drive.pb.h"
11 #include "chrome/browser/chromeos/drive/file_cache.h"
12 #include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
13 #include "chrome/browser/chromeos/drive/file_system_util.h"
14 #include "chrome/browser/chromeos/drive/job_scheduler.h"
15 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
16 #include "chrome/browser/chromeos/drive/resource_metadata.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "net/base/mime_util.h"
19
20 using content::BrowserThread;
21
22 namespace drive {
23 namespace file_system {
24
25 namespace {
26
27 const char kMimeTypeOctetStream[] = "application/octet-stream";
28
29 // Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_|
30 // of the operation, before server-side file creation.
31 FileError CheckPreConditionForCreateFile(internal::ResourceMetadata* metadata,
32                                          const base::FilePath& file_path,
33                                          bool is_exclusive,
34                                          std::string* parent_resource_id,
35                                          std::string* mime_type) {
36   DCHECK(metadata);
37   DCHECK(parent_resource_id);
38   DCHECK(mime_type);
39
40   ResourceEntry entry;
41   FileError error = metadata->GetResourceEntryByPath(file_path, &entry);
42   if (error == FILE_ERROR_OK) {
43     // Error if an exclusive mode is requested, or the entry is not a file.
44     return (is_exclusive ||
45             entry.file_info().is_directory() ||
46             entry.file_specific_info().is_hosted_document()) ?
47         FILE_ERROR_EXISTS : FILE_ERROR_OK;
48   }
49
50   // If the file is not found, an actual request to create a new file will be
51   // sent to the server.
52   if (error == FILE_ERROR_NOT_FOUND) {
53     // If parent path is not a directory, it is an error.
54     ResourceEntry parent;
55     if (metadata->GetResourceEntryByPath(
56             file_path.DirName(), &parent) != FILE_ERROR_OK ||
57         !parent.file_info().is_directory())
58       return FILE_ERROR_NOT_A_DIRECTORY;
59
60     // In the request, parent_resource_id and mime_type are needed.
61     // Here, populate them.
62     *parent_resource_id = parent.resource_id();
63
64     // If mime_type is not set or "application/octet-stream", guess from the
65     // |file_path|. If it is still unsure, use octet-stream by default.
66     if ((mime_type->empty() || *mime_type == kMimeTypeOctetStream) &&
67         !net::GetMimeTypeFromFile(file_path, mime_type)) {
68       *mime_type = kMimeTypeOctetStream;
69     }
70   }
71
72   return error;
73 }
74
75 // Part of CreateFileOperation::CreateFile(), runs on |blocking_task_runner_|
76 // of the operation, after server side file creation.
77 FileError UpdateLocalStateForCreateFile(
78     internal::ResourceMetadata* metadata,
79     internal::FileCache* cache,
80     scoped_ptr<google_apis::ResourceEntry> resource_entry,
81     base::FilePath* file_path) {
82   DCHECK(metadata);
83   DCHECK(cache);
84   DCHECK(resource_entry);
85   DCHECK(file_path);
86
87   // Add the entry to the local resource metadata.
88   ResourceEntry entry;
89   std::string parent_resource_id;
90   if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id))
91     return FILE_ERROR_NOT_A_FILE;
92
93   std::string parent_local_id;
94   FileError error = metadata->GetIdByResourceId(parent_resource_id,
95                                                 &parent_local_id);
96   if (error != FILE_ERROR_OK)
97     return error;
98   entry.set_parent_local_id(parent_local_id);
99
100   std::string local_id;
101   error = metadata->AddEntry(entry, &local_id);
102
103   // Depending on timing, the metadata may have inserted via change list
104   // already. So, FILE_ERROR_EXISTS is not an error.
105   if (error == FILE_ERROR_EXISTS)
106     error = metadata->GetIdByResourceId(entry.resource_id(), &local_id);
107
108   if (error == FILE_ERROR_OK) {
109     // At this point, upload to the server is fully succeeded.
110     // Populate the |file_path| which will be used to notify the observer.
111     *file_path = metadata->GetFilePath(local_id);
112
113     // Also store an empty file to the cache.
114     // Here, failure is not a fatal error, so ignore the returned code.
115     FileError cache_store_error = FILE_ERROR_FAILED;
116     base::FilePath empty_file;
117     if (file_util::CreateTemporaryFile(&empty_file)) {
118       cache_store_error =  cache->Store(
119           local_id,
120           entry.file_specific_info().md5(),
121           empty_file,
122           internal::FileCache::FILE_OPERATION_MOVE);
123     }
124     DLOG_IF(WARNING, cache_store_error != FILE_ERROR_OK)
125         << "Failed to store a cache file: "
126         << FileErrorToString(cache_store_error)
127         << ", local_id: " << local_id;
128   }
129
130   return error;
131 }
132
133 }  // namespace
134
135 CreateFileOperation::CreateFileOperation(
136     base::SequencedTaskRunner* blocking_task_runner,
137     OperationObserver* observer,
138     JobScheduler* scheduler,
139     internal::ResourceMetadata* metadata,
140     internal::FileCache* cache)
141     : blocking_task_runner_(blocking_task_runner),
142       observer_(observer),
143       scheduler_(scheduler),
144       metadata_(metadata),
145       cache_(cache),
146       weak_ptr_factory_(this) {
147   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
148 }
149
150 CreateFileOperation::~CreateFileOperation() {
151   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
152 }
153
154 void CreateFileOperation::CreateFile(const base::FilePath& file_path,
155                                      bool is_exclusive,
156                                      const std::string& mime_type,
157                                      const FileOperationCallback& callback) {
158   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159   DCHECK(!callback.is_null());
160
161   std::string* parent_resource_id = new std::string;
162   std::string* determined_mime_type = new std::string(mime_type);
163   base::PostTaskAndReplyWithResult(
164       blocking_task_runner_.get(),
165       FROM_HERE,
166       base::Bind(&CheckPreConditionForCreateFile,
167                  metadata_,
168                  file_path,
169                  is_exclusive,
170                  parent_resource_id,
171                  determined_mime_type),
172       base::Bind(&CreateFileOperation::CreateFileAfterCheckPreCondition,
173                  weak_ptr_factory_.GetWeakPtr(),
174                  file_path,
175                  callback,
176                  base::Owned(parent_resource_id),
177                  base::Owned(determined_mime_type)));
178 }
179
180 void CreateFileOperation::CreateFileAfterCheckPreCondition(
181     const base::FilePath& file_path,
182     const FileOperationCallback& callback,
183     std::string* parent_resource_id,
184     std::string* mime_type,
185     FileError error) {
186   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
187   DCHECK(!callback.is_null());
188   DCHECK(parent_resource_id);
189   DCHECK(mime_type);
190
191   // If the file is found, or an error other than "not found" is found,
192   // runs callback and quit the operation.
193   if (error != FILE_ERROR_NOT_FOUND) {
194     callback.Run(error);
195     return;
196   }
197
198   scheduler_->CreateFile(
199       *parent_resource_id,
200       file_path,
201       file_path.BaseName().value(),
202       *mime_type,
203       ClientContext(USER_INITIATED),
204       base::Bind(&CreateFileOperation::CreateFileAfterUpload,
205                  weak_ptr_factory_.GetWeakPtr(),
206                  callback));
207 }
208
209 void CreateFileOperation::CreateFileAfterUpload(
210     const FileOperationCallback& callback,
211     google_apis::GDataErrorCode gdata_error,
212     scoped_ptr<google_apis::ResourceEntry> resource_entry) {
213   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
214   DCHECK(!callback.is_null());
215
216   FileError error = GDataToFileError(gdata_error);
217   if (error != FILE_ERROR_OK) {
218     callback.Run(error);
219     return;
220   }
221   DCHECK(resource_entry);
222
223   base::FilePath* file_path = new base::FilePath;
224   base::PostTaskAndReplyWithResult(
225       blocking_task_runner_.get(),
226       FROM_HERE,
227       base::Bind(&UpdateLocalStateForCreateFile,
228                  metadata_,
229                  cache_,
230                  base::Passed(&resource_entry),
231                  file_path),
232       base::Bind(&CreateFileOperation::CreateFileAfterUpdateLocalState,
233                  weak_ptr_factory_.GetWeakPtr(),
234                  callback,
235                  base::Owned(file_path)));
236 }
237
238 void CreateFileOperation::CreateFileAfterUpdateLocalState(
239     const FileOperationCallback& callback,
240     base::FilePath* file_path,
241     FileError error) {
242   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
243   DCHECK(!callback.is_null());
244   DCHECK(file_path);
245
246   // Notify observer if the file creation process is successfully done.
247   if (error == FILE_ERROR_OK)
248     observer_->OnDirectoryChangedByOperation(file_path->DirName());
249
250   callback.Run(error);
251 }
252
253 }  // namespace file_system
254 }  // namespace drive