Index: chrome/browser/chromeos/arc/fileapi/arc_documents_provider_document.cc |
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_document.cc b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_document.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7c1b7aba655948d88a7f6b0071b9c2f5a75e9ca8 |
--- /dev/null |
+++ b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_document.cc |
@@ -0,0 +1,177 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_document.h" |
+ |
+#include <set> |
+ |
+#include "base/memory/ptr_util.h" |
+#include "base/strings/string_util.h" |
+#include "base/strings/stringprintf.h" |
+#include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h" |
+#include "net/base/mime_util.h" |
+ |
+namespace arc { |
+ |
+ArcDocumentsProviderDocument::ArcDocumentsProviderDocument( |
+ const std::string& document_id, |
+ bool is_directory) |
+ : document_id_(document_id), |
+ is_directory_(is_directory), |
+ children_(is_directory ? new ChildMap() : nullptr) {} |
+ |
+ArcDocumentsProviderDocument::~ArcDocumentsProviderDocument() = default; |
+ |
+// static |
+ArcDocumentsProviderDocument* ArcDocumentsProviderDocument::CreateFromDocument( |
+ const mojom::Document& document) { |
+ return new ArcDocumentsProviderDocument( |
+ document.document_id, document.mime_type == kAndroidDirectoryMimeType); |
+} |
+ |
+ArcDocumentsProviderDocument* ArcDocumentsProviderDocument::Lookup( |
+ const base::FilePath& path) { |
+ if (path.empty()) { |
Luis Héctor Chávez
2016/12/15 02:40:31
nit: elide braces for all the one-line if blocks.
Shuhei Takahashi
2016/12/15 06:50:57
Done.
|
+ return this; |
+ } |
+ ArcDocumentsProviderDocument* current = this; |
+ std::vector<base::FilePath::StringType> components; |
+ path.GetComponents(&components); |
+ for (const base::FilePath::StringType& component : components) { |
+ if (!current->is_directory()) { |
+ return nullptr; |
+ } |
+ auto iter = current->children_->find(component); |
+ if (iter == current->children_->end()) { |
+ return nullptr; |
+ } |
+ current = iter->second.get(); |
+ } |
+ return current; |
+} |
+ |
+void ArcDocumentsProviderDocument::UpdateWithChildDocuments( |
+ const std::vector<mojom::DocumentPtr>& children) { |
+ DCHECK(is_directory_); |
+ |
+ ChildMap& old_map = *children_; |
+ ChildMap new_map; |
+ |
+ std::set<mojom::Document*> processed; |
Luis Héctor Chávez
2016/12/15 02:40:31
How large do you think these maps/sets are going t
Shuhei Takahashi
2016/12/15 06:50:57
It is the number of files in a single directory, s
Luis Héctor Chávez
2016/12/15 23:54:26
sort of. depends on the load factor. On 64-bit mac
|
+ |
+ // If a document is in |old_map|, keep the mapping for them. |
+ { |
+ std::map<std::string, base::FilePath::StringType> reverse_old_map; |
+ for (const auto& pair : old_map) { |
+ reverse_old_map[pair.second->document_id()] = pair.first; |
+ } |
+ for (const mojom::DocumentPtr& child : children) { |
+ auto iter = reverse_old_map.find(child->document_id); |
+ if (iter != reverse_old_map.end()) { |
Luis Héctor Chávez
2016/12/15 02:40:31
nit:
if (iter == reverse_old_map.end())
continu
Shuhei Takahashi
2016/12/15 06:50:57
Done.
|
+ new_map[iter->second].reset(CreateFromDocument(*child)); |
+ processed.insert(&*child); |
+ } |
+ } |
+ } |
+ |
+ // Process new documents without name conflicts. |
+ std::map<base::FilePath::StringType, int> name_counters; |
Luis Héctor Chávez
2016/12/15 02:40:31
Can you also wrap this in a block to ensure |name_
Shuhei Takahashi
2016/12/15 06:50:57
Done.
|
+ for (const mojom::DocumentPtr& child : children) { |
+ if (processed.count(&*child) > 0) { |
+ continue; |
+ } |
+ name_counters[GetFileNameForDocument(child)] += 1; |
+ } |
+ for (const mojom::DocumentPtr& child : children) { |
+ if (processed.count(&*child) > 0) { |
+ continue; |
+ } |
+ base::FilePath::StringType filename = GetFileNameForDocument(child); |
+ DCHECK(name_counters[filename] >= 1); |
+ if (name_counters[filename] == 1 && new_map.count(filename) == 0) { |
+ new_map[filename].reset(CreateFromDocument(*child)); |
+ processed.insert(&*child); |
+ } |
+ } |
+ |
+ // Finally, deal with conflicts. |
+ std::map<std::string, int> suffix_counters; |
+ for (const mojom::DocumentPtr& child : children) { |
+ if (processed.count(&*child) > 0) { |
+ continue; |
+ } |
+ |
+ base::FilePath::StringType filename = GetFileNameForDocument(child); |
+ |
+ if (new_map.count(filename) > 0) { |
+ // Resolve a conflict by adding a suffix. |
+ int& suffix_counter = suffix_counters[filename]; |
+ if (suffix_counter == 0) { |
+ suffix_counter = 1; |
+ } |
+ while (true) { |
+ std::string suffix = base::StringPrintf(" %d", suffix_counter); |
+ ++suffix_counter; |
Luis Héctor Chávez
2016/12/15 02:40:31
Why not move this above L114? That way you don't n
Shuhei Takahashi
2016/12/15 06:50:57
Sounds like a good idea.
|
+ base::FilePath::StringType new_filename = |
+ base::FilePath(filename).InsertBeforeExtensionASCII(suffix).value(); |
+ if (new_map.count(new_filename) == 0) { |
+ filename = new_filename; |
+ break; |
+ } |
+ } |
+ } |
+ |
+ new_map[filename].reset(CreateFromDocument(*child)); |
+ processed.insert(&*child); |
+ } |
+ |
+ DCHECK_EQ(processed.size(), children.size()); |
+ |
+ old_map.swap(new_map); |
+} |
+ |
+// static |
+base::FilePath::StringType ArcDocumentsProviderDocument::GetFileNameForDocument( |
+ const mojom::DocumentPtr& document) { |
+ base::FilePath::StringType filename = document->display_name; |
+ |
+ // Replace path separators appearing in the file name. |
+ // Chrome OS is POSIX and kSeparators is "/". |
+ base::ReplaceChars(filename, base::FilePath::kSeparators, "_", &filename); |
+ |
+ // Do not allow "." and "..", though this is very unlikely to happen. |
+ if (filename == base::FilePath::kCurrentDirectory) { |
+ filename = "dot"; |
+ } else if (filename == base::FilePath::kParentDirectory) { |
+ filename = "dotdot"; |
+ } |
+ |
+ // Since Chrome detects MIME type from file name extensions, we need to change |
+ // the file name extension of the document if it does not match with its MIME |
+ // type. |
+ // For example, Audio Media Provider presents a music file with its title as |
+ // the file name. |
+ base::FilePath::StringType extension = |
+ base::ToLowerASCII(base::FilePath(filename).Extension()); |
+ if (!extension.empty()) { |
+ extension = extension.substr(1); // Strip the leading dot. |
+ } |
+ std::vector<base::FilePath::StringType> possible_extensions; |
+ net::GetExtensionsForMimeType(document->mime_type, &possible_extensions); |
+ if (!possible_extensions.empty() && |
+ std::find(possible_extensions.begin(), possible_extensions.end(), |
+ extension) == possible_extensions.end()) { |
+ std::string new_extension; |
+ if (!net::GetPreferredExtensionForMimeType(document->mime_type, |
+ &new_extension)) { |
+ new_extension = possible_extensions[0]; |
+ } |
+ filename = |
+ base::StringPrintf("%s.%s", filename.c_str(), new_extension.c_str()); |
+ } |
+ |
+ return filename; |
+} |
+ |
+} // namespace arc |