Chromium Code Reviews| Index: chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.cc |
| diff --git a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.cc b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.cc |
| index aa1b86890526123ae9f5168754b6ce218dc540b8..89159806a01476b873783259ed228ffe53344e6b 100644 |
| --- a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.cc |
| +++ b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.cc |
| @@ -4,16 +4,75 @@ |
| #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.h" |
| +#include <algorithm> |
| +#include <utility> |
| + |
| +#include "base/bind.h" |
| +#include "base/files/file.h" |
| #include "base/logging.h" |
| +#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 "chrome/browser/chromeos/arc/fileapi/arc_file_system_instance_util.h" |
| #include "content/public/browser/browser_thread.h" |
| +#include "net/base/mime_util.h" |
| using content::BrowserThread; |
| +using EntryList = storage::AsyncFileUtil::EntryList; |
| namespace arc { |
| +namespace { |
| + |
| +// Computes a file name for a document. |
| +base::FilePath::StringType 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 an empty file name and all-dots file names. |
| + if (filename.empty() || |
| + filename.find_first_not_of('.', 0) == std::string::npos) { |
| + filename = "_"; |
| + } |
| + |
| + // 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; |
|
hashimoto
2016/12/20 04:55:07
This should be base::FilePath::StringType.
Shuhei Takahashi
2016/12/20 06:08:24
Done.
|
| + if (!net::GetPreferredExtensionForMimeType(document->mime_type, |
| + &new_extension)) { |
| + new_extension = possible_extensions[0]; |
| + } |
| + filename = base::FilePath(filename).AddExtension(new_extension).value(); |
| + } |
| + |
| + return filename; |
| +} |
| + |
| +} // namespace |
| + |
| ArcDocumentsProviderRoot::ArcDocumentsProviderRoot( |
| const std::string& authority, |
| - const std::string& root_document_id) {} |
| + const std::string& root_document_id) |
| + : authority_(authority), |
| + root_document_id_(root_document_id), |
| + weak_ptr_factory_(this) {} |
| ArcDocumentsProviderRoot::~ArcDocumentsProviderRoot() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| @@ -23,14 +82,174 @@ void ArcDocumentsProviderRoot::GetFileInfo( |
| const base::FilePath& path, |
| const GetFileInfoCallback& callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - NOTIMPLEMENTED(); // TODO(crbug.com/671511): Implement this function. |
| + ResolveToDocumentId( |
| + path, base::Bind(&ArcDocumentsProviderRoot::GetFileInfoWithDocumentId, |
| + weak_ptr_factory_.GetWeakPtr(), callback)); |
| } |
| void ArcDocumentsProviderRoot::ReadDirectory( |
| const base::FilePath& path, |
| const ReadDirectoryCallback& callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - NOTIMPLEMENTED(); // TODO(crbug.com/671511): Implement this function. |
| + ResolveToDocumentId( |
| + path, base::Bind(&ArcDocumentsProviderRoot::ReadDirectoryWithDocumentId, |
| + weak_ptr_factory_.GetWeakPtr(), callback)); |
| +} |
| + |
| +void ArcDocumentsProviderRoot::GetFileInfoWithDocumentId( |
| + const GetFileInfoCallback& callback, |
| + const std::string& document_id) { |
| + if (document_id.empty()) { |
| + callback.Run(base::File::FILE_ERROR_NOT_FOUND, base::File::Info()); |
| + return; |
| + } |
| + file_system_instance_util::GetDocumentOnIOThread( |
| + authority_, document_id, |
| + base::Bind(&ArcDocumentsProviderRoot::GetFileInfoWithDocument, |
| + weak_ptr_factory_.GetWeakPtr(), callback)); |
| +} |
| + |
| +void ArcDocumentsProviderRoot::GetFileInfoWithDocument( |
| + const GetFileInfoCallback& callback, |
| + mojom::DocumentPtr document) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + if (document.is_null()) { |
| + callback.Run(base::File::FILE_ERROR_NOT_FOUND, base::File::Info()); |
| + return; |
| + } |
| + base::File::Info info; |
| + info.size = document->size; |
| + info.is_directory = document->mime_type == kAndroidDirectoryMimeType; |
| + info.is_symbolic_link = false; |
| + info.last_modified = info.last_accessed = info.creation_time = |
| + base::Time::FromJavaTime(document->last_modified); |
| + callback.Run(base::File::FILE_OK, info); |
| +} |
| + |
| +void ArcDocumentsProviderRoot::ReadDirectoryWithDocumentId( |
| + const ReadDirectoryCallback& callback, |
| + const std::string& document_id) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + if (document_id.empty()) { |
| + callback.Run(base::File::FILE_ERROR_NOT_FOUND, EntryList(), |
| + false /* has_more */); |
| + return; |
| + } |
| + ReadDirectoryInternal( |
| + document_id, |
| + base::Bind(&ArcDocumentsProviderRoot::ReadDirectoryWithNameMapping, |
| + weak_ptr_factory_.GetWeakPtr(), callback)); |
| +} |
| + |
| +void ArcDocumentsProviderRoot::ReadDirectoryWithNameMapping( |
| + const ReadDirectoryCallback& callback, |
| + NameMapping mapping) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + std::vector<storage::DirectoryEntry> entry_list; |
| + for (const auto& pair : mapping) { |
| + entry_list.emplace_back(pair.first, pair.second.is_directory |
| + ? storage::DirectoryEntry::DIRECTORY |
| + : storage::DirectoryEntry::FILE); |
| + } |
| + callback.Run(base::File::FILE_OK, entry_list, false /* has_more */); |
| +} |
| + |
| +void ArcDocumentsProviderRoot::ResolveToDocumentId( |
| + const base::FilePath& path, |
| + const ResolveToDocumentIdCallback& callback) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + std::vector<base::FilePath::StringType> components; |
| + path.GetComponents(&components); |
| + ResolveToDocumentIdRecursively(root_document_id_, components, callback); |
| +} |
| + |
| +void ArcDocumentsProviderRoot::ResolveToDocumentIdRecursively( |
| + const std::string& document_id, |
| + const std::vector<base::FilePath::StringType>& components, |
| + const ResolveToDocumentIdCallback& callback) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + if (components.empty()) { |
| + callback.Run(document_id); |
| + return; |
| + } |
| + ReadDirectoryInternal( |
| + document_id, |
| + base::Bind(&ArcDocumentsProviderRoot:: |
| + ResolveToDocumentIdRecursivelyWithNameMapping, |
| + weak_ptr_factory_.GetWeakPtr(), components, callback)); |
| +} |
| + |
| +void ArcDocumentsProviderRoot::ResolveToDocumentIdRecursivelyWithNameMapping( |
| + const std::vector<base::FilePath::StringType>& components, |
| + const ResolveToDocumentIdCallback& callback, |
| + NameMapping mapping) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + DCHECK(!components.empty()); |
| + auto iter = mapping.find(components[0]); |
| + if (iter == mapping.end()) { |
| + callback.Run(std::string()); |
| + return; |
| + } |
| + ResolveToDocumentIdRecursively(iter->second.document_id, |
| + std::vector<base::FilePath::StringType>( |
| + components.begin() + 1, components.end()), |
| + callback); |
| +} |
| + |
| +void ArcDocumentsProviderRoot::ReadDirectoryInternal( |
| + const std::string& document_id, |
| + const ReadDirectoryInternalCallback& callback) { |
| + file_system_instance_util::GetChildDocumentsOnIOThread( |
| + authority_, document_id, |
| + base::Bind( |
| + &ArcDocumentsProviderRoot::ReadDirectoryInternalWithChildDocuments, |
| + weak_ptr_factory_.GetWeakPtr(), callback)); |
| +} |
| + |
| +void ArcDocumentsProviderRoot::ReadDirectoryInternalWithChildDocuments( |
| + const ReadDirectoryInternalCallback& callback, |
| + base::Optional<std::vector<mojom::DocumentPtr>> maybe_children) { |
| + if (!maybe_children) { |
| + callback.Run(NameMapping()); |
|
hashimoto
2016/12/20 04:55:07
ReadDirectoryInternal cannot return errors when th
Shuhei Takahashi
2016/12/20 06:08:24
Makes sense, let's return errors from ReadDirector
|
| + return; |
| + } |
| + std::vector<mojom::DocumentPtr>& children = maybe_children.value(); |
| + |
| + // Sort entries to keep the mapping stable as far as possible. |
| + std::sort(children.begin(), children.end(), |
| + [](const mojom::DocumentPtr& a, const mojom::DocumentPtr& b) { |
| + return a->document_id < b->document_id; |
| + }); |
| + |
| + NameMapping mapping; |
| + std::map<base::FilePath::StringType, int> suffix_counters; |
| + |
| + for (const mojom::DocumentPtr& document : children) { |
| + base::FilePath::StringType filename = GetFileNameForDocument(document); |
| + |
| + if (mapping.count(filename) > 0) { |
| + // Resolve a conflict by adding a suffix. |
| + int& suffix_counter = suffix_counters[filename]; |
| + while (true) { |
| + ++suffix_counter; |
| + std::string suffix = base::StringPrintf(" %d", suffix_counter); |
|
hashimoto
2016/12/20 04:55:07
Drive code uses "(n)" instead of "n" as suffix.
It
Shuhei Takahashi
2016/12/20 06:08:24
Changed to (n). Yes, it would be good if we can co
hashimoto
2016/12/20 06:24:18
Somewhere under components (probably a new directo
Shuhei Takahashi
2016/12/20 06:32:49
Thanks, filed crbug.com/675868.
|
| + base::FilePath::StringType new_filename = |
| + base::FilePath(filename).InsertBeforeExtensionASCII(suffix).value(); |
| + if (mapping.count(new_filename) == 0) { |
| + filename = new_filename; |
| + break; |
| + } |
| + } |
| + } |
| + |
| + DCHECK_EQ(0u, mapping.count(filename)); |
| + |
| + mapping[filename] = |
| + ThinDocument{document->document_id, |
| + document->mime_type == kAndroidDirectoryMimeType}; |
| + } |
| + |
| + callback.Run(std::move(mapping)); |
| } |
| } // namespace arc |