Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / change_list_processor.cc
index ab4fef6..40c2ea1 100644 (file)
@@ -7,11 +7,12 @@
 #include "base/metrics/histogram.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/chromeos/drive/drive.pb.h"
+#include "chrome/browser/chromeos/drive/file_change.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
 #include "chrome/browser/chromeos/drive/resource_metadata.h"
-#include "chrome/browser/google_apis/drive_api_parser.h"
-#include "chrome/browser/google_apis/gdata_wapi_parser.h"
+#include "chrome/browser/drive/drive_api_util.h"
+#include "google_apis/drive/drive_api_parser.h"
 
 namespace drive {
 namespace internal {
@@ -53,27 +54,58 @@ class ChangeListToEntryMapUMAStats {
   int num_shared_with_me_entries_;
 };
 
+// Returns true if it's OK to overwrite the local entry with the remote one.
+bool ShouldApplyChange(const ResourceEntry& local_entry,
+                       const ResourceEntry& remote_entry) {
+  if (local_entry.metadata_edit_state() == ResourceEntry::CLEAN)
+    return true;
+  return base::Time::FromInternalValue(remote_entry.modification_date()) >
+      base::Time::FromInternalValue(local_entry.modification_date());
+}
+
 }  // namespace
 
 std::string DirectoryFetchInfo::ToString() const {
-  return ("resource_id: " + resource_id_ +
+  return ("local_id: " + local_id_ +
+          ", resource_id: " + resource_id_ +
           ", changestamp: " + base::Int64ToString(changestamp_));
 }
 
 ChangeList::ChangeList() {}
 
-ChangeList::ChangeList(const google_apis::ResourceList& resource_list)
-    : largest_changestamp_(resource_list.largest_changestamp()) {
-  resource_list.GetNextFeedURL(&next_url_);
+ChangeList::ChangeList(const google_apis::ChangeList& change_list)
+    : next_url_(change_list.next_link()),
+      largest_changestamp_(change_list.largest_change_id()) {
+  const ScopedVector<google_apis::ChangeResource>& items = change_list.items();
+  entries_.resize(items.size());
+  parent_resource_ids_.resize(items.size());
+  size_t entries_index = 0;
+  for (size_t i = 0; i < items.size(); ++i) {
+    if (ConvertChangeResourceToResourceEntry(
+            *items[i],
+            &entries_[entries_index],
+            &parent_resource_ids_[entries_index])) {
+      ++entries_index;
+    }
+  }
+  entries_.resize(entries_index);
+  parent_resource_ids_.resize(entries_index);
+}
 
-  entries_.resize(resource_list.entries().size());
-  parent_resource_ids_.resize(resource_list.entries().size());
+ChangeList::ChangeList(const google_apis::FileList& file_list)
+    : next_url_(file_list.next_link()),
+      largest_changestamp_(0) {
+  const ScopedVector<google_apis::FileResource>& items = file_list.items();
+  entries_.resize(items.size());
+  parent_resource_ids_.resize(items.size());
   size_t entries_index = 0;
-  for (size_t i = 0; i < resource_list.entries().size(); ++i) {
-    if (ConvertToResourceEntry(*resource_list.entries()[i],
-                               &entries_[entries_index],
-                               &parent_resource_ids_[entries_index]))
+  for (size_t i = 0; i < items.size(); ++i) {
+    if (ConvertFileResourceToResourceEntry(
+            *items[i],
+            &entries_[entries_index],
+            &parent_resource_ids_[entries_index])) {
       ++entries_index;
+    }
   }
   entries_.resize(entries_index);
   parent_resource_ids_.resize(entries_index);
@@ -82,7 +114,7 @@ ChangeList::ChangeList(const google_apis::ResourceList& resource_list)
 ChangeList::~ChangeList() {}
 
 ChangeListProcessor::ChangeListProcessor(ResourceMetadata* resource_metadata)
-  : resource_metadata_(resource_metadata) {
+    : resource_metadata_(resource_metadata), changed_files_(new FileChange) {
 }
 
 ChangeListProcessor::~ChangeListProcessor() {
@@ -142,9 +174,7 @@ FileError ChangeListProcessor::Apply(
     }
   }
 
-  FileError error = ApplyEntryMap(is_delta_update,
-                                  largest_changestamp,
-                                  about_resource.Pass());
+  FileError error = ApplyEntryMap(largest_changestamp, about_resource.Pass());
   if (error != FILE_ERROR_OK) {
     DLOG(ERROR) << "ApplyEntryMap failed: " << FileErrorToString(error);
     return error;
@@ -165,7 +195,6 @@ FileError ChangeListProcessor::Apply(
 }
 
 FileError ChangeListProcessor::ApplyEntryMap(
-    bool is_delta_update,
     int64 changestamp,
     scoped_ptr<google_apis::AboutResource> about_resource) {
   DCHECK(about_resource);
@@ -174,24 +203,14 @@ FileError ChangeListProcessor::ApplyEntryMap(
   ResourceEntry root;
   FileError error = resource_metadata_->GetResourceEntryByPath(
       util::GetDriveMyDriveRootPath(), &root);
-  switch (error) {
-    case FILE_ERROR_OK:  // Refresh the existing My Drive root entry.
-      root.mutable_directory_specific_info()->set_changestamp(changestamp);
-      error = resource_metadata_->RefreshEntry(root);
-      break;
-    case FILE_ERROR_NOT_FOUND: {  // Add the My Drive root directory.
-      changed_dirs_.insert(util::GetDriveGrandRootPath());
-      changed_dirs_.insert(util::GetDriveMyDriveRootPath());
-
-      root = util::CreateMyDriveRootEntry(about_resource->root_folder_id());
-      root.mutable_directory_specific_info()->set_changestamp(changestamp);
-      std::string local_id;
-      error = resource_metadata_->AddEntry(root, &local_id);
-      break;
-    }
-    default:
-      break;
+  if (error != FILE_ERROR_OK) {
+    LOG(ERROR) << "Failed to get root entry: " << FileErrorToString(error);
+    return error;
   }
+
+  root.mutable_directory_specific_info()->set_changestamp(changestamp);
+  root.set_resource_id(about_resource->root_folder_id());
+  error = resource_metadata_->RefreshEntry(root);
   if (error != FILE_ERROR_OK) {
     LOG(ERROR) << "Failed to update root entry: " << FileErrorToString(error);
     return error;
@@ -209,15 +228,13 @@ FileError ChangeListProcessor::ApplyEntryMap(
   }
 
   // Apply all entries except deleted ones to the metadata.
-  std::vector<ResourceEntry> deleted_entries;
-  deleted_entries.reserve(entry_map_.size());
+  std::vector<std::string> deleted_resource_ids;
   while (!entry_map_.empty()) {
     ResourceEntryMap::iterator it = entry_map_.begin();
 
     // Process deleted entries later to avoid deleting moved entries under it.
     if (it->second.deleted()) {
-      deleted_entries.push_back(ResourceEntry());
-      deleted_entries.back().Swap(&it->second);
+      deleted_resource_ids.push_back(it->first);
       entry_map_.erase(it);
       continue;
     }
@@ -237,9 +254,12 @@ FileError ChangeListProcessor::ApplyEntryMap(
          it != entry_map_.end();) {
       entries.push_back(it);
 
+      DCHECK(parent_resource_id_map_.count(it->first)) << it->first;
       const std::string& parent_resource_id =
           parent_resource_id_map_[it->first];
-      DCHECK(!parent_resource_id.empty()) << it->first;
+
+      if (parent_resource_id.empty())  // This entry has no parent.
+        break;
 
       ResourceEntryMap::iterator it_parent =
           entry_map_.find(parent_resource_id);
@@ -250,8 +270,16 @@ FileError ChangeListProcessor::ApplyEntryMap(
         FileError error = resource_metadata_->GetIdByResourceId(
             parent_resource_id, &parent_local_id);
         if (error != FILE_ERROR_OK) {
-          LOG(ERROR) << "Failed to get local ID: " << parent_resource_id
-                     << ", error = " << FileErrorToString(error);
+          // See crbug.com/326043. In some complicated situations, parent folder
+          // for shared entries may be accessible (and hence its resource id is
+          // included), but not in the change/file list.
+          // In such a case, clear the parent and move it to drive/other.
+          if (error == FILE_ERROR_NOT_FOUND) {
+            parent_resource_id_map_[it->first] = "";
+          } else {
+            LOG(ERROR) << "Failed to get local ID: " << parent_resource_id
+                       << ", error = " << FileErrorToString(error);
+          }
           break;
         }
         ResourceEntry parent_entry;
@@ -277,152 +305,169 @@ FileError ChangeListProcessor::ApplyEntryMap(
       // root entry, but we should better be defensive (see crbug.com/297259).
       ResourceEntryMap::iterator it = entries[i];
       if (it->first != root.resource_id()) {
-        // TODO(hashimoto): Handle ApplyEntry errors correctly.
         FileError error = ApplyEntry(it->second);
-        DLOG_IF(WARNING, error != FILE_ERROR_OK)
-            << "ApplyEntry failed: " << FileErrorToString(error)
-            << ", title = " << it->second.title();
+        if (error != FILE_ERROR_OK) {
+          LOG(ERROR) << "ApplyEntry failed: " << FileErrorToString(error)
+                     << ", title = " << it->second.title();
+          return error;
+        }
       }
       entry_map_.erase(it);
     }
   }
 
   // Apply deleted entries.
-  for (size_t i = 0; i < deleted_entries.size(); ++i) {
-    // TODO(hashimoto): Handle ApplyEntry errors correctly.
-    FileError error = ApplyEntry(deleted_entries[i]);
-    DLOG_IF(WARNING, error != FILE_ERROR_OK)
-        << "ApplyEntry failed: " << FileErrorToString(error)
-        << ", title = " << deleted_entries[i].title();
+  for (size_t i = 0; i < deleted_resource_ids.size(); ++i) {
+    std::string local_id;
+    FileError error = resource_metadata_->GetIdByResourceId(
+        deleted_resource_ids[i], &local_id);
+    switch (error) {
+      case FILE_ERROR_OK:
+        error = resource_metadata_->RemoveEntry(local_id);
+        break;
+      case FILE_ERROR_NOT_FOUND:
+        error = FILE_ERROR_OK;
+        break;
+      default:
+        break;
+    }
+    if (error != FILE_ERROR_OK) {
+      LOG(ERROR) << "Failed to delete: " << FileErrorToString(error)
+                 << ", resource_id = " << deleted_resource_ids[i];
+      return error;
+    }
   }
 
   return FILE_ERROR_OK;
 }
 
 FileError ChangeListProcessor::ApplyEntry(const ResourceEntry& entry) {
+  DCHECK(!entry.deleted());
+  DCHECK(parent_resource_id_map_.count(entry.resource_id()));
+  const std::string& parent_resource_id =
+      parent_resource_id_map_[entry.resource_id()];
+
+  ResourceEntry new_entry(entry);
+  FileError error = SetParentLocalIdOfEntry(resource_metadata_, &new_entry,
+                                            parent_resource_id);
+  if (error != FILE_ERROR_OK)
+    return error;
+
   // Lookup the entry.
   std::string local_id;
-  FileError error = resource_metadata_->GetIdByResourceId(entry.resource_id(),
-                                                          &local_id);
+  error = resource_metadata_->GetIdByResourceId(entry.resource_id(), &local_id);
+
   ResourceEntry existing_entry;
   if (error == FILE_ERROR_OK)
     error = resource_metadata_->GetResourceEntryById(local_id, &existing_entry);
 
-  const FileError get_existing_entry_result = error;
-  if (entry.deleted()) {
-    // Deleted file/directory.
-    switch (get_existing_entry_result) {
-      case FILE_ERROR_OK:
-        error = resource_metadata_->RemoveEntry(local_id);
-        break;
-      case FILE_ERROR_NOT_FOUND:  // Already deleted.
-        error = FILE_ERROR_OK;
-        break;
-      default:
-        error = get_existing_entry_result;
-    }
-  } else {
-    const std::string& parent_resource_id =
-        parent_resource_id_map_[entry.resource_id()];
-    DCHECK(!parent_resource_id.empty()) << entry.resource_id();
-
-    std::string parent_local_id;
-    error = resource_metadata_->GetIdByResourceId(
-        parent_resource_id, &parent_local_id);
-    if (error == FILE_ERROR_OK) {
-      ResourceEntry new_entry(entry);
-      new_entry.set_parent_local_id(parent_local_id);
-
-      switch (get_existing_entry_result) {
-        case FILE_ERROR_OK:  // Entry exists and needs to be refreshed.
-          new_entry.set_local_id(local_id);
+  switch (error) {
+    case FILE_ERROR_OK:
+      if (ShouldApplyChange(existing_entry, new_entry)) {
+        // Entry exists and needs to be refreshed.
+        new_entry.set_local_id(local_id);
+        error = resource_metadata_->RefreshEntry(new_entry);
+      } else {
+        if (entry.file_info().is_directory()) {
+          // No need to refresh, but update the changestamp.
+          new_entry = existing_entry;
+          new_entry.mutable_directory_specific_info()->set_changestamp(
+              new_entry.directory_specific_info().changestamp());
           error = resource_metadata_->RefreshEntry(new_entry);
-          break;
-        case FILE_ERROR_NOT_FOUND: {  // Adding a new entry.
-          std::string local_id;
-          error = resource_metadata_->AddEntry(new_entry, &local_id);
-          break;
         }
-        default:
-          error = get_existing_entry_result;
+        DVLOG(1) << "Change was discarded for: " << entry.resource_id();
       }
-
-      if (error == FILE_ERROR_OK)
-        UpdateChangedDirs(entry);
+      break;
+    case FILE_ERROR_NOT_FOUND: {  // Adding a new entry.
+      std::string local_id;
+      error = resource_metadata_->AddEntry(new_entry, &local_id);
+      break;
     }
+    default:
+      return error;
   }
+  if (error != FILE_ERROR_OK)
+    return error;
 
-  return error;
+  UpdateChangedDirs(entry);
+  return FILE_ERROR_OK;
 }
 
 // static
 FileError ChangeListProcessor::RefreshDirectory(
     ResourceMetadata* resource_metadata,
     const DirectoryFetchInfo& directory_fetch_info,
-    ScopedVector<ChangeList> change_lists,
-    base::FilePath* out_file_path) {
+    scoped_ptr<ChangeList> change_list,
+    std::vector<ResourceEntry>* out_refreshed_entries) {
   DCHECK(!directory_fetch_info.empty());
 
-  std::string directory_local_id;
-  FileError error = resource_metadata->GetIdByResourceId(
-      directory_fetch_info.resource_id(), &directory_local_id);
-  if (error != FILE_ERROR_OK)
-    return error;
-
   ResourceEntry directory;
-  error = resource_metadata->GetResourceEntryById(directory_local_id,
-                                                  &directory);
+  FileError error = resource_metadata->GetResourceEntryById(
+      directory_fetch_info.local_id(), &directory);
   if (error != FILE_ERROR_OK)
     return error;
 
   if (!directory.file_info().is_directory())
     return FILE_ERROR_NOT_A_DIRECTORY;
 
-  for (size_t i = 0; i < change_lists.size(); ++i) {
-    ChangeList* change_list = change_lists[i];
-    std::vector<ResourceEntry>* entries = change_list->mutable_entries();
-    for (size_t i = 0; i < entries->size(); ++i) {
-      ResourceEntry* entry = &(*entries)[i];
-      const std::string& parent_resource_id =
-          change_list->parent_resource_ids()[i];
+  std::vector<ResourceEntry>* entries = change_list->mutable_entries();
+  for (size_t i = 0; i < entries->size(); ++i) {
+    ResourceEntry* entry = &(*entries)[i];
+    const std::string& parent_resource_id =
+        change_list->parent_resource_ids()[i];
+
+    // Skip if the parent resource ID does not match. This is needed to
+    // handle entries with multiple parents. For such entries, the first
+    // parent is picked and other parents are ignored, hence some entries may
+    // have a parent resource ID which does not match the target directory's.
+    if (parent_resource_id != directory_fetch_info.resource_id()) {
+      DVLOG(1) << "Wrong-parent entry rejected: " << entry->resource_id();
+      continue;
+    }
 
-      // Skip if the parent resource ID does not match. This is needed to
-      // handle entries with multiple parents. For such entries, the first
-      // parent is picked and other parents are ignored, hence some entries may
-      // have a parent resource ID which does not match the target directory's.
-      if (parent_resource_id != directory_fetch_info.resource_id()) {
-        DVLOG(1) << "Wrong-parent entry rejected: " << entry->resource_id();
-        continue;
-      }
+    entry->set_parent_local_id(directory_fetch_info.local_id());
 
-      entry->set_parent_local_id(directory_local_id);
+    std::string local_id;
+    error = resource_metadata->GetIdByResourceId(entry->resource_id(),
+                                                 &local_id);
+    if (error == FILE_ERROR_OK) {
+      entry->set_local_id(local_id);
+      error = resource_metadata->RefreshEntry(*entry);
+    }
 
-      std::string local_id;
-      error = resource_metadata->GetIdByResourceId(entry->resource_id(),
-                                                   &local_id);
-      if (error == FILE_ERROR_OK) {
-        entry->set_local_id(local_id);
-        error = resource_metadata->RefreshEntry(*entry);
-      }
+    if (error == FILE_ERROR_NOT_FOUND) {  // If refreshing fails, try adding.
+      entry->clear_local_id();
+      error = resource_metadata->AddEntry(*entry, &local_id);
+    }
 
-      if (error == FILE_ERROR_NOT_FOUND) {  // If refreshing fails, try adding.
-        std::string local_id;
-        entry->clear_local_id();
-        error = resource_metadata->AddEntry(*entry, &local_id);
-      }
+    if (error != FILE_ERROR_OK)
+      return error;
 
-      if (error != FILE_ERROR_OK)
-        return error;
-    }
+    ResourceEntry result_entry;
+    error = resource_metadata->GetResourceEntryById(local_id, &result_entry);
+    if (error != FILE_ERROR_OK)
+      return error;
+    out_refreshed_entries->push_back(result_entry);
   }
+  return FILE_ERROR_OK;
+}
 
-  directory.mutable_directory_specific_info()->set_changestamp(
-      directory_fetch_info.changestamp());
-  error = resource_metadata->RefreshEntry(directory);
-  if (error != FILE_ERROR_OK)
-    return error;
-
-  *out_file_path = resource_metadata->GetFilePath(directory_local_id);
+// static
+FileError ChangeListProcessor::SetParentLocalIdOfEntry(
+    ResourceMetadata* resource_metadata,
+    ResourceEntry* entry,
+    const std::string& parent_resource_id) {
+  std::string parent_local_id;
+  if (parent_resource_id.empty()) {
+    // Entries without parents should go under "other" directory.
+    parent_local_id = util::kDriveOtherDirLocalId;
+  } else {
+    FileError error = resource_metadata->GetIdByResourceId(
+        parent_resource_id, &parent_local_id);
+    if (error != FILE_ERROR_OK)
+      return error;
+  }
+  entry->set_parent_local_id(parent_local_id);
   return FILE_ERROR_OK;
 }
 
@@ -433,24 +478,12 @@ void ChangeListProcessor::UpdateChangedDirs(const ResourceEntry& entry) {
   base::FilePath file_path;
   if (resource_metadata_->GetIdByResourceId(
           entry.resource_id(), &local_id) == FILE_ERROR_OK)
-    file_path = resource_metadata_->GetFilePath(local_id);
+    resource_metadata_->GetFilePath(local_id, &file_path);
 
   if (!file_path.empty()) {
-    // Notify parent.
-    changed_dirs_.insert(file_path.DirName());
-
-    if (entry.file_info().is_directory()) {
-      // Notify self if entry is a directory.
-      changed_dirs_.insert(file_path);
-
-      // Notify all descendants if it is a directory deletion.
-      if (entry.deleted()) {
-        std::set<base::FilePath> sub_directories;
-        resource_metadata_->GetSubDirectoriesRecursively(local_id,
-                                                         &sub_directories);
-        changed_dirs_.insert(sub_directories.begin(), sub_directories.end());
-      }
-    }
+    FileChange::ChangeType type =
+        entry.deleted() ? FileChange::DELETE : FileChange::ADD_OR_UPDATE;
+    changed_files_->Update(file_path, entry, type);
   }
 }