Chromium Code Reviews| 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 |