| Index: chrome/browser/chromeos/drive/sync/entry_update_performer.cc
 | 
| diff --git a/chrome/browser/chromeos/drive/sync/entry_update_performer.cc b/chrome/browser/chromeos/drive/sync/entry_update_performer.cc
 | 
| index 5ec70d4a35ed269ce929c1de3ecd8cecd20b6f70..22357d9aafe5bccee15fee17109b6f0d93a70bb6 100644
 | 
| --- a/chrome/browser/chromeos/drive/sync/entry_update_performer.cc
 | 
| +++ b/chrome/browser/chromeos/drive/sync/entry_update_performer.cc
 | 
| @@ -5,6 +5,7 @@
 | 
|  #include "chrome/browser/chromeos/drive/sync/entry_update_performer.h"
 | 
|  
 | 
|  #include "chrome/browser/chromeos/drive/drive.pb.h"
 | 
| +#include "chrome/browser/chromeos/drive/file_cache.h"
 | 
|  #include "chrome/browser/chromeos/drive/file_system_util.h"
 | 
|  #include "chrome/browser/chromeos/drive/job_scheduler.h"
 | 
|  #include "chrome/browser/chromeos/drive/resource_metadata.h"
 | 
| @@ -16,30 +17,75 @@ using content::BrowserThread;
 | 
|  
 | 
|  namespace drive {
 | 
|  namespace internal {
 | 
| +
 | 
| +struct EntryUpdatePerformer::LocalState {
 | 
| +  LocalState() : should_content_update(false) {
 | 
| +  }
 | 
| +
 | 
| +  ResourceEntry entry;
 | 
| +  ResourceEntry parent_entry;
 | 
| +  base::FilePath drive_file_path;
 | 
| +  base::FilePath cache_file_path;
 | 
| +  bool should_content_update;
 | 
| +};
 | 
| +
 | 
|  namespace {
 | 
|  
 | 
|  // Looks up ResourceEntry for source entry and its parent.
 | 
|  FileError PrepareUpdate(ResourceMetadata* metadata,
 | 
| +                        FileCache* cache,
 | 
|                          const std::string& local_id,
 | 
| -                        ResourceEntry* entry,
 | 
| -                        ResourceEntry* parent_entry) {
 | 
| -  FileError error = metadata->GetResourceEntryById(local_id, entry);
 | 
| +                        EntryUpdatePerformer::LocalState* local_state) {
 | 
| +  FileError error = metadata->GetResourceEntryById(local_id,
 | 
| +                                                   &local_state->entry);
 | 
|    if (error != FILE_ERROR_OK)
 | 
|      return error;
 | 
|  
 | 
| -  error = metadata->GetResourceEntryById(entry->parent_local_id(),
 | 
| -                                         parent_entry);
 | 
| +  error = metadata->GetResourceEntryById(local_state->entry.parent_local_id(),
 | 
| +                                         &local_state->parent_entry);
 | 
|    if (error != FILE_ERROR_OK)
 | 
|      return error;
 | 
|  
 | 
| -  switch (entry->metadata_edit_state()) {
 | 
| +  local_state->drive_file_path = metadata->GetFilePath(local_id);
 | 
| +  if (local_state->drive_file_path.empty())
 | 
| +    return FILE_ERROR_NOT_FOUND;
 | 
| +
 | 
| +  // Check if content update is needed or not.
 | 
| +  FileCacheEntry cache_entry;
 | 
| +  if (cache->GetCacheEntry(local_id, &cache_entry) &&
 | 
| +      cache_entry.is_dirty() &&
 | 
| +      !cache->IsOpenedForWrite(local_id)) {
 | 
| +    // Update cache entry's MD5 if needed.
 | 
| +    if (cache_entry.md5().empty()) {
 | 
| +      error = cache->UpdateMd5(local_id);
 | 
| +      if (error != FILE_ERROR_OK)
 | 
| +        return error;
 | 
| +      if (!cache->GetCacheEntry(local_id, &cache_entry))
 | 
| +        return FILE_ERROR_NOT_FOUND;
 | 
| +    }
 | 
| +
 | 
| +    if (cache_entry.md5() == local_state->entry.file_specific_info().md5()) {
 | 
| +      error = cache->ClearDirty(local_id);
 | 
| +      if (error != FILE_ERROR_OK)
 | 
| +        return error;
 | 
| +    } else {
 | 
| +      error = cache->GetFile(local_id, &local_state->cache_file_path);
 | 
| +      if (error != FILE_ERROR_OK)
 | 
| +        return error;
 | 
| +
 | 
| +      local_state->should_content_update = true;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Update metadata_edit_state.
 | 
| +  switch (local_state->entry.metadata_edit_state()) {
 | 
|      case ResourceEntry::CLEAN:  // Nothing to do.
 | 
|      case ResourceEntry::SYNCING:  // Error during the last update. Go ahead.
 | 
|        break;
 | 
|  
 | 
|      case ResourceEntry::DIRTY:
 | 
| -      entry->set_metadata_edit_state(ResourceEntry::SYNCING);
 | 
| -      error = metadata->RefreshEntry(*entry);
 | 
| +      local_state->entry.set_metadata_edit_state(ResourceEntry::SYNCING);
 | 
| +      error = metadata->RefreshEntry(local_state->entry);
 | 
|        if (error != FILE_ERROR_OK)
 | 
|          return error;
 | 
|        break;
 | 
| @@ -48,12 +94,15 @@ FileError PrepareUpdate(ResourceMetadata* metadata,
 | 
|  }
 | 
|  
 | 
|  FileError FinishUpdate(ResourceMetadata* metadata,
 | 
| -                       const std::string& local_id) {
 | 
| +                       FileCache* cache,
 | 
| +                       const std::string& local_id,
 | 
| +                       scoped_ptr<google_apis::ResourceEntry> resource_entry) {
 | 
|    ResourceEntry entry;
 | 
|    FileError error = metadata->GetResourceEntryById(local_id, &entry);
 | 
|    if (error != FILE_ERROR_OK)
 | 
|      return error;
 | 
|  
 | 
| +  // Update metadata_edit_state and MD5.
 | 
|    switch (entry.metadata_edit_state()) {
 | 
|      case ResourceEntry::CLEAN:  // Nothing to do.
 | 
|      case ResourceEntry::DIRTY:  // Entry was edited again during the update.
 | 
| @@ -61,11 +110,22 @@ FileError FinishUpdate(ResourceMetadata* metadata,
 | 
|  
 | 
|      case ResourceEntry::SYNCING:
 | 
|        entry.set_metadata_edit_state(ResourceEntry::CLEAN);
 | 
| -      error = metadata->RefreshEntry(entry);
 | 
| -      if (error != FILE_ERROR_OK)
 | 
| -        return error;
 | 
|        break;
 | 
|    }
 | 
| +  if (!entry.file_info().is_directory())
 | 
| +    entry.mutable_file_specific_info()->set_md5(resource_entry->file_md5());
 | 
| +  error = metadata->RefreshEntry(entry);
 | 
| +  if (error != FILE_ERROR_OK)
 | 
| +    return error;
 | 
| +
 | 
| +  // Clear dirty bit unless the file has been edited during update.
 | 
| +  FileCacheEntry cache_entry;
 | 
| +  if (cache->GetCacheEntry(local_id, &cache_entry) &&
 | 
| +      cache_entry.md5() == entry.file_specific_info().md5()) {
 | 
| +    error = cache->ClearDirty(local_id);
 | 
| +    if (error != FILE_ERROR_OK)
 | 
| +      return error;
 | 
| +  }
 | 
|    return FILE_ERROR_OK;
 | 
|  }
 | 
|  
 | 
| @@ -75,10 +135,12 @@ EntryUpdatePerformer::EntryUpdatePerformer(
 | 
|      base::SequencedTaskRunner* blocking_task_runner,
 | 
|      file_system::OperationObserver* observer,
 | 
|      JobScheduler* scheduler,
 | 
| -    ResourceMetadata* metadata)
 | 
| +    ResourceMetadata* metadata,
 | 
| +    FileCache* cache)
 | 
|      : blocking_task_runner_(blocking_task_runner),
 | 
|        scheduler_(scheduler),
 | 
|        metadata_(metadata),
 | 
| +      cache_(cache),
 | 
|        remove_performer_(new RemovePerformer(blocking_task_runner,
 | 
|                                              observer,
 | 
|                                              scheduler,
 | 
| @@ -101,26 +163,21 @@ void EntryUpdatePerformer::UpdateEntry(const std::string& local_id,
 | 
|    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
|    DCHECK(!callback.is_null());
 | 
|  
 | 
| -  scoped_ptr<ResourceEntry> entry(new ResourceEntry);
 | 
| -  scoped_ptr<ResourceEntry> parent_entry(new ResourceEntry);
 | 
| -  ResourceEntry* entry_ptr = entry.get();
 | 
| -  ResourceEntry* parent_entry_ptr = parent_entry.get();
 | 
| +  scoped_ptr<LocalState> local_state(new LocalState);
 | 
| +  LocalState* local_state_ptr = local_state.get();
 | 
|    base::PostTaskAndReplyWithResult(
 | 
|        blocking_task_runner_.get(),
 | 
|        FROM_HERE,
 | 
| -      base::Bind(&PrepareUpdate,
 | 
| -                 metadata_, local_id, entry_ptr, parent_entry_ptr),
 | 
| +      base::Bind(&PrepareUpdate, metadata_, cache_, local_id, local_state_ptr),
 | 
|        base::Bind(&EntryUpdatePerformer::UpdateEntryAfterPrepare,
 | 
|                   weak_ptr_factory_.GetWeakPtr(), context, callback,
 | 
| -                 base::Passed(&entry),
 | 
| -                 base::Passed(&parent_entry)));
 | 
| +                 base::Passed(&local_state)));
 | 
|  }
 | 
|  
 | 
|  void EntryUpdatePerformer::UpdateEntryAfterPrepare(
 | 
|      const ClientContext& context,
 | 
|      const FileOperationCallback& callback,
 | 
| -    scoped_ptr<ResourceEntry> entry,
 | 
| -    scoped_ptr<ResourceEntry> parent_entry,
 | 
| +    scoped_ptr<LocalState> local_state,
 | 
|      FileError error) {
 | 
|    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 | 
|    DCHECK(!callback.is_null());
 | 
| @@ -131,27 +188,52 @@ void EntryUpdatePerformer::UpdateEntryAfterPrepare(
 | 
|    }
 | 
|  
 | 
|    // Trashed entry should be removed.
 | 
| -  if (entry->parent_local_id() == util::kDriveTrashDirLocalId) {
 | 
| -    remove_performer_->Remove(entry->local_id(), context, callback);
 | 
| +  if (local_state->entry.parent_local_id() == util::kDriveTrashDirLocalId) {
 | 
| +    remove_performer_->Remove(local_state->entry.local_id(), context, callback);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  base::Time last_modified = base::Time::FromInternalValue(
 | 
| +      local_state->entry.file_info().last_modified());
 | 
| +  base::Time last_accessed = base::Time::FromInternalValue(
 | 
| +      local_state->entry.file_info().last_accessed());
 | 
| +
 | 
| +  // Perform content update.
 | 
| +  if (local_state->should_content_update) {
 | 
| +    drive::DriveUploader::UploadExistingFileOptions options;
 | 
| +    options.title = local_state->entry.title();
 | 
| +    options.parent_resource_id = local_state->parent_entry.resource_id();
 | 
| +    options.modified_date = last_modified;
 | 
| +    options.last_viewed_by_me_date = last_accessed;
 | 
| +    scheduler_->UploadExistingFile(
 | 
| +        local_state->entry.resource_id(),
 | 
| +        local_state->drive_file_path,
 | 
| +        local_state->cache_file_path,
 | 
| +        local_state->entry.file_specific_info().content_mime_type(),
 | 
| +        options,
 | 
| +        context,
 | 
| +        base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
 | 
| +                   weak_ptr_factory_.GetWeakPtr(),
 | 
| +                   context,
 | 
| +                   callback,
 | 
| +                   local_state->entry.local_id()));
 | 
|      return;
 | 
|    }
 | 
|  
 | 
| -  if (entry->metadata_edit_state() == ResourceEntry::CLEAN) {
 | 
| +  // No need to perform update.
 | 
| +  if (local_state->entry.metadata_edit_state() == ResourceEntry::CLEAN) {
 | 
|      callback.Run(FILE_ERROR_OK);
 | 
|      return;
 | 
|    }
 | 
|  
 | 
| -  base::Time last_modified =
 | 
| -      base::Time::FromInternalValue(entry->file_info().last_modified());
 | 
| -  base::Time last_accessed =
 | 
| -      base::Time::FromInternalValue(entry->file_info().last_accessed());
 | 
| +  // Perform metadata update.
 | 
|    scheduler_->UpdateResource(
 | 
| -      entry->resource_id(), parent_entry->resource_id(),
 | 
| -      entry->title(), last_modified, last_accessed,
 | 
| +      local_state->entry.resource_id(), local_state->parent_entry.resource_id(),
 | 
| +      local_state->entry.title(), last_modified, last_accessed,
 | 
|        context,
 | 
|        base::Bind(&EntryUpdatePerformer::UpdateEntryAfterUpdateResource,
 | 
|                   weak_ptr_factory_.GetWeakPtr(),
 | 
| -                 context, callback, entry->local_id()));
 | 
| +                 context, callback, local_state->entry.local_id()));
 | 
|  }
 | 
|  
 | 
|  void EntryUpdatePerformer::UpdateEntryAfterUpdateResource(
 | 
| @@ -177,7 +259,8 @@ void EntryUpdatePerformer::UpdateEntryAfterUpdateResource(
 | 
|    base::PostTaskAndReplyWithResult(
 | 
|        blocking_task_runner_.get(),
 | 
|        FROM_HERE,
 | 
| -      base::Bind(&FinishUpdate, metadata_, local_id),
 | 
| +      base::Bind(&FinishUpdate,
 | 
| +                 metadata_, cache_, local_id, base::Passed(&resource_entry)),
 | 
|        callback);
 | 
|  }
 | 
|  
 | 
| 
 |