Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.h" | 5 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root.h" |
| 6 | 6 |
| 7 #include <algorithm> | |
| 8 #include <utility> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/files/file.h" | |
| 7 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/memory/ptr_util.h" | |
| 14 #include "base/strings/string_util.h" | |
| 15 #include "base/strings/stringprintf.h" | |
| 16 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h" | |
| 17 #include "chrome/browser/chromeos/arc/fileapi/arc_file_system_instance_util.h" | |
| 8 #include "content/public/browser/browser_thread.h" | 18 #include "content/public/browser/browser_thread.h" |
| 19 #include "net/base/mime_util.h" | |
| 9 | 20 |
| 10 using content::BrowserThread; | 21 using content::BrowserThread; |
| 22 using EntryList = storage::AsyncFileUtil::EntryList; | |
| 11 | 23 |
| 12 namespace arc { | 24 namespace arc { |
| 13 | 25 |
| 26 namespace { | |
| 27 | |
| 28 // Computes a file name for a document. | |
| 29 base::FilePath::StringType GetFileNameForDocument( | |
| 30 const mojom::DocumentPtr& document) { | |
| 31 base::FilePath::StringType filename = document->display_name; | |
| 32 | |
| 33 // Replace path separators appearing in the file name. | |
| 34 // Chrome OS is POSIX and kSeparators is "/". | |
| 35 base::ReplaceChars(filename, base::FilePath::kSeparators, "_", &filename); | |
| 36 | |
| 37 // Do not allow an empty file name and all-dots file names. | |
| 38 if (filename.empty() || | |
| 39 filename.find_first_not_of('.', 0) == std::string::npos) { | |
| 40 filename = "_"; | |
| 41 } | |
| 42 | |
| 43 // Since Chrome detects MIME type from file name extensions, we need to change | |
| 44 // the file name extension of the document if it does not match with its MIME | |
| 45 // type. | |
| 46 // For example, Audio Media Provider presents a music file with its title as | |
| 47 // the file name. | |
| 48 base::FilePath::StringType extension = | |
| 49 base::ToLowerASCII(base::FilePath(filename).Extension()); | |
| 50 if (!extension.empty()) | |
| 51 extension = extension.substr(1); // Strip the leading dot. | |
| 52 std::vector<base::FilePath::StringType> possible_extensions; | |
| 53 net::GetExtensionsForMimeType(document->mime_type, &possible_extensions); | |
| 54 if (!possible_extensions.empty() && | |
| 55 std::find(possible_extensions.begin(), possible_extensions.end(), | |
| 56 extension) == possible_extensions.end()) { | |
| 57 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.
| |
| 58 if (!net::GetPreferredExtensionForMimeType(document->mime_type, | |
| 59 &new_extension)) { | |
| 60 new_extension = possible_extensions[0]; | |
| 61 } | |
| 62 filename = base::FilePath(filename).AddExtension(new_extension).value(); | |
| 63 } | |
| 64 | |
| 65 return filename; | |
| 66 } | |
| 67 | |
| 68 } // namespace | |
| 69 | |
| 14 ArcDocumentsProviderRoot::ArcDocumentsProviderRoot( | 70 ArcDocumentsProviderRoot::ArcDocumentsProviderRoot( |
| 15 const std::string& authority, | 71 const std::string& authority, |
| 16 const std::string& root_document_id) {} | 72 const std::string& root_document_id) |
| 73 : authority_(authority), | |
| 74 root_document_id_(root_document_id), | |
| 75 weak_ptr_factory_(this) {} | |
| 17 | 76 |
| 18 ArcDocumentsProviderRoot::~ArcDocumentsProviderRoot() { | 77 ArcDocumentsProviderRoot::~ArcDocumentsProviderRoot() { |
| 19 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 78 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 20 } | 79 } |
| 21 | 80 |
| 22 void ArcDocumentsProviderRoot::GetFileInfo( | 81 void ArcDocumentsProviderRoot::GetFileInfo( |
| 23 const base::FilePath& path, | 82 const base::FilePath& path, |
| 24 const GetFileInfoCallback& callback) { | 83 const GetFileInfoCallback& callback) { |
| 25 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 84 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 26 NOTIMPLEMENTED(); // TODO(crbug.com/671511): Implement this function. | 85 ResolveToDocumentId( |
| 86 path, base::Bind(&ArcDocumentsProviderRoot::GetFileInfoWithDocumentId, | |
| 87 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 27 } | 88 } |
| 28 | 89 |
| 29 void ArcDocumentsProviderRoot::ReadDirectory( | 90 void ArcDocumentsProviderRoot::ReadDirectory( |
| 30 const base::FilePath& path, | 91 const base::FilePath& path, |
| 31 const ReadDirectoryCallback& callback) { | 92 const ReadDirectoryCallback& callback) { |
| 32 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 93 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 33 NOTIMPLEMENTED(); // TODO(crbug.com/671511): Implement this function. | 94 ResolveToDocumentId( |
| 95 path, base::Bind(&ArcDocumentsProviderRoot::ReadDirectoryWithDocumentId, | |
| 96 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 97 } | |
| 98 | |
| 99 void ArcDocumentsProviderRoot::GetFileInfoWithDocumentId( | |
| 100 const GetFileInfoCallback& callback, | |
| 101 const std::string& document_id) { | |
| 102 if (document_id.empty()) { | |
| 103 callback.Run(base::File::FILE_ERROR_NOT_FOUND, base::File::Info()); | |
| 104 return; | |
| 105 } | |
| 106 file_system_instance_util::GetDocumentOnIOThread( | |
| 107 authority_, document_id, | |
| 108 base::Bind(&ArcDocumentsProviderRoot::GetFileInfoWithDocument, | |
| 109 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 110 } | |
| 111 | |
| 112 void ArcDocumentsProviderRoot::GetFileInfoWithDocument( | |
| 113 const GetFileInfoCallback& callback, | |
| 114 mojom::DocumentPtr document) { | |
| 115 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 116 if (document.is_null()) { | |
| 117 callback.Run(base::File::FILE_ERROR_NOT_FOUND, base::File::Info()); | |
| 118 return; | |
| 119 } | |
| 120 base::File::Info info; | |
| 121 info.size = document->size; | |
| 122 info.is_directory = document->mime_type == kAndroidDirectoryMimeType; | |
| 123 info.is_symbolic_link = false; | |
| 124 info.last_modified = info.last_accessed = info.creation_time = | |
| 125 base::Time::FromJavaTime(document->last_modified); | |
| 126 callback.Run(base::File::FILE_OK, info); | |
| 127 } | |
| 128 | |
| 129 void ArcDocumentsProviderRoot::ReadDirectoryWithDocumentId( | |
| 130 const ReadDirectoryCallback& callback, | |
| 131 const std::string& document_id) { | |
| 132 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 133 if (document_id.empty()) { | |
| 134 callback.Run(base::File::FILE_ERROR_NOT_FOUND, EntryList(), | |
| 135 false /* has_more */); | |
| 136 return; | |
| 137 } | |
| 138 ReadDirectoryInternal( | |
| 139 document_id, | |
| 140 base::Bind(&ArcDocumentsProviderRoot::ReadDirectoryWithNameMapping, | |
| 141 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 142 } | |
| 143 | |
| 144 void ArcDocumentsProviderRoot::ReadDirectoryWithNameMapping( | |
| 145 const ReadDirectoryCallback& callback, | |
| 146 NameMapping mapping) { | |
| 147 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 148 std::vector<storage::DirectoryEntry> entry_list; | |
| 149 for (const auto& pair : mapping) { | |
| 150 entry_list.emplace_back(pair.first, pair.second.is_directory | |
| 151 ? storage::DirectoryEntry::DIRECTORY | |
| 152 : storage::DirectoryEntry::FILE); | |
| 153 } | |
| 154 callback.Run(base::File::FILE_OK, entry_list, false /* has_more */); | |
| 155 } | |
| 156 | |
| 157 void ArcDocumentsProviderRoot::ResolveToDocumentId( | |
| 158 const base::FilePath& path, | |
| 159 const ResolveToDocumentIdCallback& callback) { | |
| 160 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 161 std::vector<base::FilePath::StringType> components; | |
| 162 path.GetComponents(&components); | |
| 163 ResolveToDocumentIdRecursively(root_document_id_, components, callback); | |
| 164 } | |
| 165 | |
| 166 void ArcDocumentsProviderRoot::ResolveToDocumentIdRecursively( | |
| 167 const std::string& document_id, | |
| 168 const std::vector<base::FilePath::StringType>& components, | |
| 169 const ResolveToDocumentIdCallback& callback) { | |
| 170 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 171 if (components.empty()) { | |
| 172 callback.Run(document_id); | |
| 173 return; | |
| 174 } | |
| 175 ReadDirectoryInternal( | |
| 176 document_id, | |
| 177 base::Bind(&ArcDocumentsProviderRoot:: | |
| 178 ResolveToDocumentIdRecursivelyWithNameMapping, | |
| 179 weak_ptr_factory_.GetWeakPtr(), components, callback)); | |
| 180 } | |
| 181 | |
| 182 void ArcDocumentsProviderRoot::ResolveToDocumentIdRecursivelyWithNameMapping( | |
| 183 const std::vector<base::FilePath::StringType>& components, | |
| 184 const ResolveToDocumentIdCallback& callback, | |
| 185 NameMapping mapping) { | |
| 186 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 187 DCHECK(!components.empty()); | |
| 188 auto iter = mapping.find(components[0]); | |
| 189 if (iter == mapping.end()) { | |
| 190 callback.Run(std::string()); | |
| 191 return; | |
| 192 } | |
| 193 ResolveToDocumentIdRecursively(iter->second.document_id, | |
| 194 std::vector<base::FilePath::StringType>( | |
| 195 components.begin() + 1, components.end()), | |
| 196 callback); | |
| 197 } | |
| 198 | |
| 199 void ArcDocumentsProviderRoot::ReadDirectoryInternal( | |
| 200 const std::string& document_id, | |
| 201 const ReadDirectoryInternalCallback& callback) { | |
| 202 file_system_instance_util::GetChildDocumentsOnIOThread( | |
| 203 authority_, document_id, | |
| 204 base::Bind( | |
| 205 &ArcDocumentsProviderRoot::ReadDirectoryInternalWithChildDocuments, | |
| 206 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 207 } | |
| 208 | |
| 209 void ArcDocumentsProviderRoot::ReadDirectoryInternalWithChildDocuments( | |
| 210 const ReadDirectoryInternalCallback& callback, | |
| 211 base::Optional<std::vector<mojom::DocumentPtr>> maybe_children) { | |
| 212 if (!maybe_children) { | |
| 213 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
| |
| 214 return; | |
| 215 } | |
| 216 std::vector<mojom::DocumentPtr>& children = maybe_children.value(); | |
| 217 | |
| 218 // Sort entries to keep the mapping stable as far as possible. | |
| 219 std::sort(children.begin(), children.end(), | |
| 220 [](const mojom::DocumentPtr& a, const mojom::DocumentPtr& b) { | |
| 221 return a->document_id < b->document_id; | |
| 222 }); | |
| 223 | |
| 224 NameMapping mapping; | |
| 225 std::map<base::FilePath::StringType, int> suffix_counters; | |
| 226 | |
| 227 for (const mojom::DocumentPtr& document : children) { | |
| 228 base::FilePath::StringType filename = GetFileNameForDocument(document); | |
| 229 | |
| 230 if (mapping.count(filename) > 0) { | |
| 231 // Resolve a conflict by adding a suffix. | |
| 232 int& suffix_counter = suffix_counters[filename]; | |
| 233 while (true) { | |
| 234 ++suffix_counter; | |
| 235 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.
| |
| 236 base::FilePath::StringType new_filename = | |
| 237 base::FilePath(filename).InsertBeforeExtensionASCII(suffix).value(); | |
| 238 if (mapping.count(new_filename) == 0) { | |
| 239 filename = new_filename; | |
| 240 break; | |
| 241 } | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 DCHECK_EQ(0u, mapping.count(filename)); | |
| 246 | |
| 247 mapping[filename] = | |
| 248 ThinDocument{document->document_id, | |
| 249 document->mime_type == kAndroidDirectoryMimeType}; | |
| 250 } | |
| 251 | |
| 252 callback.Run(std::move(mapping)); | |
| 34 } | 253 } |
| 35 | 254 |
| 36 } // namespace arc | 255 } // namespace arc |
| OLD | NEW |