Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / sync / entry_update_performer.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/sync/entry_update_performer.h"
6
7 #include "base/callback_helpers.h"
8 #include "base/files/file_util.h"
9 #include "chrome/browser/chromeos/drive/change_list_loader.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_change.h"
13 #include "chrome/browser/chromeos/drive/file_system/operation_delegate.h"
14 #include "chrome/browser/chromeos/drive/file_system_util.h"
15 #include "chrome/browser/chromeos/drive/job_scheduler.h"
16 #include "chrome/browser/chromeos/drive/resource_metadata.h"
17 #include "chrome/browser/chromeos/drive/sync/entry_revert_performer.h"
18 #include "chrome/browser/chromeos/drive/sync/remove_performer.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "google_apis/drive/drive_api_parser.h"
21
22 using content::BrowserThread;
23
24 namespace drive {
25 namespace internal {
26
27 struct EntryUpdatePerformer::LocalState {
28   LocalState() : should_content_update(false) {
29   }
30
31   ResourceEntry entry;
32   ResourceEntry parent_entry;
33   base::FilePath drive_file_path;
34   base::FilePath cache_file_path;
35   bool should_content_update;
36 };
37
38 namespace {
39
40 // Looks up ResourceEntry for source entry and its parent.
41 FileError PrepareUpdate(ResourceMetadata* metadata,
42                         FileCache* cache,
43                         const std::string& local_id,
44                         EntryUpdatePerformer::LocalState* local_state) {
45   FileError error = metadata->GetResourceEntryById(local_id,
46                                                    &local_state->entry);
47   if (error != FILE_ERROR_OK)
48     return error;
49
50   error = metadata->GetResourceEntryById(local_state->entry.parent_local_id(),
51                                          &local_state->parent_entry);
52   if (error != FILE_ERROR_OK)
53     return error;
54
55   error = metadata->GetFilePath(local_id, &local_state->drive_file_path);
56   if (error != FILE_ERROR_OK)
57     return error;
58
59   if (!local_state->entry.file_info().is_directory() &&
60       !local_state->entry.file_specific_info().cache_state().is_present() &&
61       local_state->entry.resource_id().empty()) {
62     // Locally created file with no cache file, store an empty file.
63     base::FilePath empty_file;
64     if (!base::CreateTemporaryFile(&empty_file))
65       return FILE_ERROR_FAILED;
66     error = cache->Store(local_id, std::string(), empty_file,
67                          FileCache::FILE_OPERATION_MOVE);
68     if (error != FILE_ERROR_OK)
69       return error;
70     error = metadata->GetResourceEntryById(local_id, &local_state->entry);
71     if (error != FILE_ERROR_OK)
72       return error;
73   }
74
75   // Check if content update is needed or not.
76   if (local_state->entry.file_specific_info().cache_state().is_dirty() &&
77       !cache->IsOpenedForWrite(local_id)) {
78     // Update cache entry's MD5 if needed.
79     if (local_state->entry.file_specific_info().cache_state().md5().empty()) {
80       error = cache->UpdateMd5(local_id);
81       if (error != FILE_ERROR_OK)
82         return error;
83       error = metadata->GetResourceEntryById(local_id, &local_state->entry);
84       if (error != FILE_ERROR_OK)
85         return error;
86     }
87
88     if (local_state->entry.file_specific_info().cache_state().md5() ==
89         local_state->entry.file_specific_info().md5()) {
90       error = cache->ClearDirty(local_id);
91       if (error != FILE_ERROR_OK)
92         return error;
93     } else {
94       error = cache->GetFile(local_id, &local_state->cache_file_path);
95       if (error != FILE_ERROR_OK)
96         return error;
97
98       local_state->should_content_update = true;
99     }
100   }
101
102   // Update metadata_edit_state.
103   switch (local_state->entry.metadata_edit_state()) {
104     case ResourceEntry::CLEAN:  // Nothing to do.
105     case ResourceEntry::SYNCING:  // Error during the last update. Go ahead.
106       break;
107
108     case ResourceEntry::DIRTY:
109       local_state->entry.set_metadata_edit_state(ResourceEntry::SYNCING);
110       error = metadata->RefreshEntry(local_state->entry);
111       if (error != FILE_ERROR_OK)
112         return error;
113       break;
114   }
115   return FILE_ERROR_OK;
116 }
117
118 FileError FinishUpdate(ResourceMetadata* metadata,
119                        FileCache* cache,
120                        const std::string& local_id,
121                        scoped_ptr<google_apis::FileResource> file_resource,
122                        FileChange* changed_files) {
123   ResourceEntry entry;
124   FileError error = metadata->GetResourceEntryById(local_id, &entry);
125   if (error != FILE_ERROR_OK)
126     return error;
127
128   // When creating new entries, update check may add a new entry with the same
129   // resource ID before us. If such an entry exists, remove it.
130   std::string existing_local_id;
131   error =
132       metadata->GetIdByResourceId(file_resource->file_id(), &existing_local_id);
133
134   switch (error) {
135     case FILE_ERROR_OK:
136       if (existing_local_id != local_id) {
137         base::FilePath existing_entry_path;
138         error = metadata->GetFilePath(existing_local_id, &existing_entry_path);
139         if (error != FILE_ERROR_OK)
140           return error;
141         error = metadata->RemoveEntry(existing_local_id);
142         if (error != FILE_ERROR_OK)
143           return error;
144         changed_files->Update(existing_entry_path, entry, FileChange::DELETE);
145       }
146       break;
147     case FILE_ERROR_NOT_FOUND:
148       break;
149     default:
150       return error;
151   }
152
153   // Update metadata_edit_state and MD5.
154   switch (entry.metadata_edit_state()) {
155     case ResourceEntry::CLEAN:  // Nothing to do.
156     case ResourceEntry::DIRTY:  // Entry was edited again during the update.
157       break;
158
159     case ResourceEntry::SYNCING:
160       entry.set_metadata_edit_state(ResourceEntry::CLEAN);
161       break;
162   }
163   if (!entry.file_info().is_directory())
164     entry.mutable_file_specific_info()->set_md5(file_resource->md5_checksum());
165   entry.set_resource_id(file_resource->file_id());
166   error = metadata->RefreshEntry(entry);
167   if (error != FILE_ERROR_OK)
168     return error;
169   base::FilePath entry_path;
170   error = metadata->GetFilePath(local_id, &entry_path);
171   if (error != FILE_ERROR_OK)
172     return error;
173   changed_files->Update(entry_path, entry, FileChange::ADD_OR_UPDATE);
174
175   // Clear dirty bit unless the file has been edited during update.
176   if (entry.file_specific_info().cache_state().is_dirty() &&
177       entry.file_specific_info().cache_state().md5() ==
178       entry.file_specific_info().md5()) {
179     error = cache->ClearDirty(local_id);
180     if (error != FILE_ERROR_OK)
181       return error;
182   }
183   return FILE_ERROR_OK;
184 }
185
186 }  // namespace
187
188 EntryUpdatePerformer::EntryUpdatePerformer(
189     base::SequencedTaskRunner* blocking_task_runner,
190     file_system::OperationDelegate* delegate,
191     JobScheduler* scheduler,
192     ResourceMetadata* metadata,
193     FileCache* cache,
194     LoaderController* loader_controller)
195     : blocking_task_runner_(blocking_task_runner),
196       delegate_(delegate),
197       scheduler_(scheduler),
198       metadata_(metadata),
199       cache_(cache),
200       loader_controller_(loader_controller),
201       remove_performer_(new RemovePerformer(blocking_task_runner,
202                                             delegate,
203                                             scheduler,
204                                             metadata)),
205       entry_revert_performer_(new EntryRevertPerformer(blocking_task_runner,
206                                                        delegate,
207                                                        scheduler,
208                                                        metadata)),
209       weak_ptr_factory_(this) {
210   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
211 }
212
213 EntryUpdatePerformer::~EntryUpdatePerformer() {
214   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
215 }
216
217 void EntryUpdatePerformer::UpdateEntry(const std::string& local_id,
218                                        const ClientContext& context,
219                                        const FileOperationCallback& callback) {
220   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221   DCHECK(!callback.is_null());
222
223   scoped_ptr<LocalState> local_state(new LocalState);
224   LocalState* local_state_ptr = local_state.get();
225   base::PostTaskAndReplyWithResult(
226       blocking_task_runner_.get(),
227       FROM_HERE,
228       base::Bind(&PrepareUpdate, metadata_, cache_, local_id, local_state_ptr),
229       base::Bind(&EntryUpdatePerformer::UpdateEntryAfterPrepare,
230                  weak_ptr_factory_.GetWeakPtr(), context, callback,
231                  base::Passed(&local_state)));
232 }
233
234 void EntryUpdatePerformer::UpdateEntryAfterPrepare(
235     const ClientContext& context,
236     const FileOperationCallback& callback,
237     scoped_ptr<LocalState> local_state,
238     FileError error) {
239   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
240   DCHECK(!callback.is_null());
241
242   if (error != FILE_ERROR_OK) {
243     callback.Run(error);
244     return;
245   }
246
247   // Trashed entry should be removed.
248   if (local_state->entry.parent_local_id() == util::kDriveTrashDirLocalId) {
249     remove_performer_->Remove(local_state->entry.local_id(), context, callback);
250     return;
251   }
252
253   // Parent was locally created and needs update. Just return for now.
254   // This entry should be updated again after the parent update completes.
255   if (local_state->parent_entry.resource_id().empty() &&
256       local_state->parent_entry.metadata_edit_state() != ResourceEntry::CLEAN) {
257     callback.Run(FILE_ERROR_OK);
258     return;
259   }
260
261   base::Time last_modified = base::Time::FromInternalValue(
262       local_state->entry.file_info().last_modified());
263   base::Time last_accessed = base::Time::FromInternalValue(
264       local_state->entry.file_info().last_accessed());
265
266   // Perform content update.
267   if (local_state->should_content_update) {
268     if (local_state->entry.resource_id().empty()) {
269       // Not locking the loader intentionally here to avoid making the UI
270       // unresponsive while uploading large files.
271       // FinishUpdate() is responsible to resolve conflicts caused by this.
272       scoped_ptr<base::ScopedClosureRunner> null_loader_lock;
273
274       DriveUploader::UploadNewFileOptions options;
275       options.modified_date = last_modified;
276       options.last_viewed_by_me_date = last_accessed;
277       scheduler_->UploadNewFile(
278           local_state->parent_entry.resource_id(),
279           local_state->drive_file_path,
280           local_state->cache_file_path,
281           local_state->entry.title(),
282           local_state->entry.file_specific_info().content_mime_type(),
283           options,
284           context,
285           base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
286                      weak_ptr_factory_.GetWeakPtr(),
287                      context,
288                      callback,
289                      local_state->entry.local_id(),
290                      base::Passed(&null_loader_lock)));
291     } else {
292       DriveUploader::UploadExistingFileOptions options;
293       options.title = local_state->entry.title();
294       options.parent_resource_id = local_state->parent_entry.resource_id();
295       options.modified_date = last_modified;
296       options.last_viewed_by_me_date = last_accessed;
297       scheduler_->UploadExistingFile(
298           local_state->entry.resource_id(),
299           local_state->drive_file_path,
300           local_state->cache_file_path,
301           local_state->entry.file_specific_info().content_mime_type(),
302           options,
303           context,
304           base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
305                      weak_ptr_factory_.GetWeakPtr(),
306                      context,
307                      callback,
308                      local_state->entry.local_id(),
309                      base::Passed(scoped_ptr<base::ScopedClosureRunner>())));
310     }
311     return;
312   }
313
314   // Create directory.
315   if (local_state->entry.file_info().is_directory() &&
316       local_state->entry.resource_id().empty()) {
317     // Lock the loader to avoid race conditions.
318     scoped_ptr<base::ScopedClosureRunner> loader_lock =
319         loader_controller_->GetLock();
320
321     DriveServiceInterface::AddNewDirectoryOptions options;
322     options.modified_date = last_modified;
323     options.last_viewed_by_me_date = last_accessed;
324     scheduler_->AddNewDirectory(
325         local_state->parent_entry.resource_id(),
326         local_state->entry.title(),
327         options,
328         context,
329         base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
330                    weak_ptr_factory_.GetWeakPtr(),
331                    context,
332                    callback,
333                    local_state->entry.local_id(),
334                    base::Passed(&loader_lock)));
335     return;
336   }
337
338   // No need to perform update.
339   if (local_state->entry.metadata_edit_state() == ResourceEntry::CLEAN ||
340       local_state->entry.resource_id().empty()) {
341     callback.Run(FILE_ERROR_OK);
342     return;
343   }
344
345   // Perform metadata update.
346   scheduler_->UpdateResource(
347       local_state->entry.resource_id(), local_state->parent_entry.resource_id(),
348       local_state->entry.title(), last_modified, last_accessed,
349       context,
350       base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
351                  weak_ptr_factory_.GetWeakPtr(),
352                  context, callback, local_state->entry.local_id(),
353                  base::Passed(scoped_ptr<base::ScopedClosureRunner>())));
354 }
355
356 void EntryUpdatePerformer::UpdateEntryAfterUpdateResource(
357     const ClientContext& context,
358     const FileOperationCallback& callback,
359     const std::string& local_id,
360     scoped_ptr<base::ScopedClosureRunner> loader_lock,
361     google_apis::GDataErrorCode status,
362     scoped_ptr<google_apis::FileResource> entry) {
363   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
364   DCHECK(!callback.is_null());
365
366   if (status == google_apis::HTTP_FORBIDDEN) {
367     // Editing this entry is not allowed, revert local changes.
368     entry_revert_performer_->RevertEntry(local_id, context, callback);
369     return;
370   }
371
372   FileError error = GDataToFileError(status);
373   if (error != FILE_ERROR_OK) {
374     callback.Run(error);
375     return;
376   }
377
378   FileChange* changed_files = new FileChange;
379   base::PostTaskAndReplyWithResult(
380       blocking_task_runner_.get(),
381       FROM_HERE,
382       base::Bind(&FinishUpdate,
383                  metadata_,
384                  cache_,
385                  local_id,
386                  base::Passed(&entry),
387                  changed_files),
388       base::Bind(&EntryUpdatePerformer::UpdateEntryAfterFinish,
389                  weak_ptr_factory_.GetWeakPtr(),
390                  callback,
391                  base::Owned(changed_files)));
392 }
393
394 void EntryUpdatePerformer::UpdateEntryAfterFinish(
395     const FileOperationCallback& callback,
396     const FileChange* changed_files,
397     FileError error) {
398   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
399   DCHECK(!callback.is_null());
400
401   delegate_->OnFileChangedByOperation(*changed_files);
402   callback.Run(error);
403 }
404
405 }  // namespace internal
406 }  // namespace drive