OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_document.h" | |
6 | |
7 #include <set> | |
8 | |
9 #include "base/memory/ptr_util.h" | |
10 #include "base/strings/string_util.h" | |
11 #include "base/strings/stringprintf.h" | |
12 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h" | |
13 #include "net/base/mime_util.h" | |
14 | |
15 namespace arc { | |
16 | |
17 namespace { | |
18 | |
19 // Computes a file name for a document. | |
20 base::FilePath::StringType GetFileNameForDocument( | |
21 const mojom::DocumentPtr& document) { | |
22 base::FilePath::StringType filename = document->display_name; | |
23 | |
24 // Replace path separators appearing in the file name. | |
25 // Chrome OS is POSIX and kSeparators is "/". | |
26 base::ReplaceChars(filename, base::FilePath::kSeparators, "_", &filename); | |
27 | |
28 // Do not allow "." and "..", though this is very unlikely to happen. | |
29 if (filename == base::FilePath::kCurrentDirectory) { | |
30 filename = "dot"; | |
hashimoto
2016/12/19 06:40:48
"dot" looks unfriendly for users who don't speak E
Shuhei Takahashi
2016/12/19 10:01:56
Thanks for the pointer, I'll follow it.
| |
31 } else if (filename == base::FilePath::kParentDirectory) { | |
32 filename = "dotdot"; | |
33 } | |
34 | |
35 // Since Chrome detects MIME type from file name extensions, we need to change | |
36 // the file name extension of the document if it does not match with its MIME | |
37 // type. | |
38 // For example, Audio Media Provider presents a music file with its title as | |
39 // the file name. | |
40 base::FilePath::StringType extension = | |
41 base::ToLowerASCII(base::FilePath(filename).Extension()); | |
42 if (!extension.empty()) | |
43 extension = extension.substr(1); // Strip the leading dot. | |
44 std::vector<base::FilePath::StringType> possible_extensions; | |
45 net::GetExtensionsForMimeType(document->mime_type, &possible_extensions); | |
46 if (!possible_extensions.empty() && | |
47 std::find(possible_extensions.begin(), possible_extensions.end(), | |
48 extension) == possible_extensions.end()) { | |
49 std::string new_extension; | |
50 if (!net::GetPreferredExtensionForMimeType(document->mime_type, | |
51 &new_extension)) { | |
52 new_extension = possible_extensions[0]; | |
53 } | |
54 filename = base::FilePath(filename).AddExtension(new_extension).value(); | |
55 } | |
56 | |
57 return filename; | |
58 } | |
59 | |
60 ArcDocumentsProviderDocument* CreateDocument(const mojom::Document& document) { | |
61 return new ArcDocumentsProviderDocument( | |
62 document.document_id, document.mime_type == kAndroidDirectoryMimeType); | |
63 } | |
64 | |
65 } // namespace | |
66 | |
67 ArcDocumentsProviderDocument::ArcDocumentsProviderDocument( | |
68 const std::string& document_id, | |
69 bool is_directory) | |
70 : document_id_(document_id), | |
71 is_directory_(is_directory), | |
72 children_(is_directory ? new ChildMap() : nullptr) { | |
73 // Constructor is called on the UI thread as opposed to other methods. | |
74 thread_checker_.DetachFromThread(); | |
75 } | |
76 | |
77 ArcDocumentsProviderDocument::~ArcDocumentsProviderDocument() { | |
78 DCHECK(thread_checker_.CalledOnValidThread()); | |
79 } | |
80 | |
81 ArcDocumentsProviderDocument* ArcDocumentsProviderDocument::Lookup( | |
82 const base::FilePath& path) { | |
83 DCHECK(thread_checker_.CalledOnValidThread()); | |
84 | |
85 if (path.empty()) | |
86 return this; | |
87 | |
88 ArcDocumentsProviderDocument* current = this; | |
89 std::vector<base::FilePath::StringType> components; | |
90 path.GetComponents(&components); | |
91 | |
92 for (const base::FilePath::StringType& component : components) { | |
93 if (!current->is_directory()) | |
94 return nullptr; | |
95 | |
96 auto iter = current->children_->find(component); | |
97 if (iter == current->children_->end()) | |
98 return nullptr; | |
99 | |
100 current = iter->second.get(); | |
101 } | |
102 return current; | |
103 } | |
104 | |
105 void ArcDocumentsProviderDocument::UpdateWithChildDocuments( | |
106 const std::vector<mojom::DocumentPtr>& children) { | |
107 DCHECK(thread_checker_.CalledOnValidThread()); | |
108 DCHECK(is_directory_); | |
109 | |
110 ChildMap& old_map = *children_; | |
111 ChildMap new_map; | |
112 | |
113 std::set<mojom::Document*> processed; | |
114 | |
115 // If a document is in |old_map|, keep the mapping for them. | |
116 { | |
117 std::map<std::string, base::FilePath::StringType> reverse_old_map; | |
118 for (const auto& pair : old_map) { | |
119 reverse_old_map[pair.second->document_id()] = pair.first; | |
120 } | |
121 for (const mojom::DocumentPtr& child : children) { | |
122 auto iter = reverse_old_map.find(child->document_id); | |
123 if (iter == reverse_old_map.end()) | |
124 continue; | |
125 new_map[iter->second].reset(CreateDocument(*child)); | |
126 processed.insert(&*child); | |
127 } | |
128 } | |
129 | |
130 // Process new documents without name conflicts. | |
131 { | |
132 std::map<base::FilePath::StringType, int> name_counters; | |
133 for (const mojom::DocumentPtr& child : children) { | |
134 if (processed.count(&*child) > 0) | |
135 continue; | |
136 name_counters[GetFileNameForDocument(child)] += 1; | |
137 } | |
138 for (const mojom::DocumentPtr& child : children) { | |
139 if (processed.count(&*child) > 0) | |
140 continue; | |
141 base::FilePath::StringType filename = GetFileNameForDocument(child); | |
142 DCHECK_GE(name_counters[filename], 1); | |
143 if (name_counters[filename] == 1 && new_map.count(filename) == 0) { | |
144 new_map[filename].reset(CreateDocument(*child)); | |
145 processed.insert(&*child); | |
146 } | |
147 } | |
148 } | |
149 | |
150 // Finally, deal with conflicts. | |
151 { | |
152 std::map<std::string, int> suffix_counters; | |
153 for (const mojom::DocumentPtr& child : children) { | |
154 if (processed.count(&*child) > 0) | |
155 continue; | |
156 | |
157 base::FilePath::StringType filename = GetFileNameForDocument(child); | |
158 | |
159 if (new_map.count(filename) > 0) { | |
160 // Resolve a conflict by adding a suffix. | |
161 int& suffix_counter = suffix_counters[filename]; | |
162 while (true) { | |
163 ++suffix_counter; | |
164 std::string suffix = base::StringPrintf(" %d", suffix_counter); | |
165 base::FilePath::StringType new_filename = | |
166 base::FilePath(filename) | |
167 .InsertBeforeExtensionASCII(suffix) | |
168 .value(); | |
169 if (new_map.count(new_filename) == 0) { | |
170 filename = new_filename; | |
171 break; | |
172 } | |
173 } | |
174 } | |
175 | |
176 new_map[filename].reset(CreateDocument(*child)); | |
177 processed.insert(&*child); | |
178 } | |
179 } | |
180 | |
181 DCHECK_EQ(processed.size(), children.size()); | |
182 | |
183 old_map.swap(new_map); | |
184 } | |
185 | |
186 } // namespace arc | |
OLD | NEW |