OLD | NEW |
| (Empty) |
1 // Copyright 2013 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 "apps/saved_files_service.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include <algorithm> | |
10 #include <map> | |
11 #include <unordered_map> | |
12 #include <utility> | |
13 | |
14 #include "apps/saved_files_service_factory.h" | |
15 #include "base/memory/ptr_util.h" | |
16 #include "base/value_conversions.h" | |
17 #include "content/public/browser/browser_context.h" | |
18 #include "content/public/browser/notification_service.h" | |
19 #include "extensions/browser/extension_host.h" | |
20 #include "extensions/browser/extension_prefs.h" | |
21 #include "extensions/browser/extension_system.h" | |
22 #include "extensions/browser/notification_types.h" | |
23 #include "extensions/common/permissions/api_permission.h" | |
24 #include "extensions/common/permissions/permission_set.h" | |
25 #include "extensions/common/permissions/permissions_data.h" | |
26 | |
27 namespace apps { | |
28 | |
29 using extensions::APIPermission; | |
30 using extensions::Extension; | |
31 using extensions::ExtensionHost; | |
32 using extensions::ExtensionPrefs; | |
33 | |
34 namespace { | |
35 | |
36 // Preference keys | |
37 | |
38 // The file entries that the app has permission to access. | |
39 const char kFileEntries[] = "file_entries"; | |
40 | |
41 // The path to a file entry that the app had permission to access. | |
42 const char kFileEntryPath[] = "path"; | |
43 | |
44 // Whether or not the the entry refers to a directory. | |
45 const char kFileEntryIsDirectory[] = "is_directory"; | |
46 | |
47 // The sequence number in the LRU of the file entry. | |
48 const char kFileEntrySequenceNumber[] = "sequence_number"; | |
49 | |
50 const size_t kMaxSavedFileEntries = 500; | |
51 const int kMaxSequenceNumber = INT32_MAX; | |
52 | |
53 // These might be different to the constant values in tests. | |
54 size_t g_max_saved_file_entries = kMaxSavedFileEntries; | |
55 int g_max_sequence_number = kMaxSequenceNumber; | |
56 | |
57 // Persists a SavedFileEntry in ExtensionPrefs. | |
58 void AddSavedFileEntry(ExtensionPrefs* prefs, | |
59 const std::string& extension_id, | |
60 const SavedFileEntry& file_entry) { | |
61 ExtensionPrefs::ScopedDictionaryUpdate update( | |
62 prefs, extension_id, kFileEntries); | |
63 auto file_entries = update.Create(); | |
64 DCHECK(!file_entries->GetDictionaryWithoutPathExpansion(file_entry.id, NULL)); | |
65 | |
66 std::unique_ptr<base::DictionaryValue> file_entry_dict = | |
67 base::MakeUnique<base::DictionaryValue>(); | |
68 file_entry_dict->Set(kFileEntryPath, CreateFilePathValue(file_entry.path)); | |
69 file_entry_dict->SetBoolean(kFileEntryIsDirectory, file_entry.is_directory); | |
70 file_entry_dict->SetInteger(kFileEntrySequenceNumber, | |
71 file_entry.sequence_number); | |
72 file_entries->SetWithoutPathExpansion(file_entry.id, | |
73 std::move(file_entry_dict)); | |
74 } | |
75 | |
76 // Updates the sequence_number of a SavedFileEntry persisted in ExtensionPrefs. | |
77 void UpdateSavedFileEntry(ExtensionPrefs* prefs, | |
78 const std::string& extension_id, | |
79 const SavedFileEntry& file_entry) { | |
80 ExtensionPrefs::ScopedDictionaryUpdate update( | |
81 prefs, extension_id, kFileEntries); | |
82 auto file_entries = update.Get(); | |
83 DCHECK(file_entries); | |
84 std::unique_ptr<prefs::DictionaryValueUpdate> file_entry_dict; | |
85 file_entries->GetDictionaryWithoutPathExpansion(file_entry.id, | |
86 &file_entry_dict); | |
87 DCHECK(file_entry_dict); | |
88 file_entry_dict->SetInteger(kFileEntrySequenceNumber, | |
89 file_entry.sequence_number); | |
90 } | |
91 | |
92 // Removes a SavedFileEntry from ExtensionPrefs. | |
93 void RemoveSavedFileEntry(ExtensionPrefs* prefs, | |
94 const std::string& extension_id, | |
95 const std::string& file_entry_id) { | |
96 ExtensionPrefs::ScopedDictionaryUpdate update( | |
97 prefs, extension_id, kFileEntries); | |
98 auto file_entries = update.Create(); | |
99 file_entries->RemoveWithoutPathExpansion(file_entry_id, NULL); | |
100 } | |
101 | |
102 // Clears all SavedFileEntry for the app from ExtensionPrefs. | |
103 void ClearSavedFileEntries(ExtensionPrefs* prefs, | |
104 const std::string& extension_id) { | |
105 prefs->UpdateExtensionPref(extension_id, kFileEntries, nullptr); | |
106 } | |
107 | |
108 // Returns all SavedFileEntries for the app. | |
109 std::vector<SavedFileEntry> GetSavedFileEntries( | |
110 ExtensionPrefs* prefs, | |
111 const std::string& extension_id) { | |
112 std::vector<SavedFileEntry> result; | |
113 const base::DictionaryValue* file_entries = NULL; | |
114 if (!prefs->ReadPrefAsDictionary(extension_id, kFileEntries, &file_entries)) | |
115 return result; | |
116 | |
117 for (base::DictionaryValue::Iterator it(*file_entries); !it.IsAtEnd(); | |
118 it.Advance()) { | |
119 const base::DictionaryValue* file_entry = NULL; | |
120 if (!it.value().GetAsDictionary(&file_entry)) | |
121 continue; | |
122 const base::Value* path_value; | |
123 if (!file_entry->Get(kFileEntryPath, &path_value)) | |
124 continue; | |
125 base::FilePath file_path; | |
126 if (!GetValueAsFilePath(*path_value, &file_path)) | |
127 continue; | |
128 bool is_directory = false; | |
129 file_entry->GetBoolean(kFileEntryIsDirectory, &is_directory); | |
130 int sequence_number = 0; | |
131 if (!file_entry->GetInteger(kFileEntrySequenceNumber, &sequence_number)) | |
132 continue; | |
133 if (!sequence_number) | |
134 continue; | |
135 result.push_back( | |
136 SavedFileEntry(it.key(), file_path, is_directory, sequence_number)); | |
137 } | |
138 return result; | |
139 } | |
140 | |
141 } // namespace | |
142 | |
143 SavedFileEntry::SavedFileEntry() : is_directory(false), sequence_number(0) {} | |
144 | |
145 SavedFileEntry::SavedFileEntry(const std::string& id, | |
146 const base::FilePath& path, | |
147 bool is_directory, | |
148 int sequence_number) | |
149 : id(id), | |
150 path(path), | |
151 is_directory(is_directory), | |
152 sequence_number(sequence_number) {} | |
153 | |
154 class SavedFilesService::SavedFiles { | |
155 public: | |
156 SavedFiles(content::BrowserContext* context, const std::string& extension_id); | |
157 ~SavedFiles(); | |
158 | |
159 void RegisterFileEntry(const std::string& id, | |
160 const base::FilePath& file_path, | |
161 bool is_directory); | |
162 void EnqueueFileEntry(const std::string& id); | |
163 bool IsRegistered(const std::string& id) const; | |
164 const SavedFileEntry* GetFileEntry(const std::string& id) const; | |
165 std::vector<SavedFileEntry> GetAllFileEntries() const; | |
166 | |
167 private: | |
168 // Compacts sequence numbers if the largest sequence number is | |
169 // g_max_sequence_number. Outside of testing, it is set to kint32max, so this | |
170 // will almost never do any real work. | |
171 void MaybeCompactSequenceNumbers(); | |
172 | |
173 void LoadSavedFileEntriesFromPreferences(); | |
174 | |
175 content::BrowserContext* context_; | |
176 const std::string extension_id_; | |
177 | |
178 // Contains all file entries that have been registered, keyed by ID. | |
179 std::unordered_map<std::string, std::unique_ptr<SavedFileEntry>> | |
180 registered_file_entries_; | |
181 | |
182 // The queue of file entries that have been retained, keyed by | |
183 // sequence_number. Values are a subset of values in registered_file_entries_. | |
184 // This should be kept in sync with file entries stored in extension prefs. | |
185 std::map<int, SavedFileEntry*> saved_file_lru_; | |
186 | |
187 DISALLOW_COPY_AND_ASSIGN(SavedFiles); | |
188 }; | |
189 | |
190 // static | |
191 SavedFilesService* SavedFilesService::Get(content::BrowserContext* context) { | |
192 return SavedFilesServiceFactory::GetForBrowserContext(context); | |
193 } | |
194 | |
195 SavedFilesService::SavedFilesService(content::BrowserContext* context) | |
196 : context_(context) { | |
197 registrar_.Add(this, | |
198 extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED, | |
199 content::NotificationService::AllSources()); | |
200 } | |
201 | |
202 SavedFilesService::~SavedFilesService() {} | |
203 | |
204 void SavedFilesService::Observe(int type, | |
205 const content::NotificationSource& source, | |
206 const content::NotificationDetails& details) { | |
207 DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_HOST_DESTROYED, type); | |
208 ExtensionHost* host = content::Details<ExtensionHost>(details).ptr(); | |
209 const Extension* extension = host->extension(); | |
210 if (extension) { | |
211 ClearQueueIfNoRetainPermission(extension); | |
212 Clear(extension->id()); | |
213 } | |
214 } | |
215 | |
216 void SavedFilesService::RegisterFileEntry(const std::string& extension_id, | |
217 const std::string& id, | |
218 const base::FilePath& file_path, | |
219 bool is_directory) { | |
220 GetOrInsert(extension_id)->RegisterFileEntry(id, file_path, is_directory); | |
221 } | |
222 | |
223 void SavedFilesService::EnqueueFileEntry(const std::string& extension_id, | |
224 const std::string& id) { | |
225 GetOrInsert(extension_id)->EnqueueFileEntry(id); | |
226 } | |
227 | |
228 std::vector<SavedFileEntry> SavedFilesService::GetAllFileEntries( | |
229 const std::string& extension_id) { | |
230 SavedFiles* saved_files = Get(extension_id); | |
231 if (saved_files) | |
232 return saved_files->GetAllFileEntries(); | |
233 return GetSavedFileEntries(ExtensionPrefs::Get(context_), extension_id); | |
234 } | |
235 | |
236 bool SavedFilesService::IsRegistered(const std::string& extension_id, | |
237 const std::string& id) { | |
238 return GetOrInsert(extension_id)->IsRegistered(id); | |
239 } | |
240 | |
241 const SavedFileEntry* SavedFilesService::GetFileEntry( | |
242 const std::string& extension_id, | |
243 const std::string& id) { | |
244 return GetOrInsert(extension_id)->GetFileEntry(id); | |
245 } | |
246 | |
247 void SavedFilesService::ClearQueueIfNoRetainPermission( | |
248 const Extension* extension) { | |
249 if (!extension->permissions_data()->active_permissions().HasAPIPermission( | |
250 APIPermission::kFileSystemRetainEntries)) { | |
251 ClearQueue(extension); | |
252 } | |
253 } | |
254 | |
255 void SavedFilesService::ClearQueue(const extensions::Extension* extension) { | |
256 ClearSavedFileEntries(ExtensionPrefs::Get(context_), extension->id()); | |
257 Clear(extension->id()); | |
258 } | |
259 | |
260 void SavedFilesService::OnApplicationTerminating() { | |
261 // Stop listening to NOTIFICATION_EXTENSION_HOST_DESTROYED in particular | |
262 // as all extension hosts will be destroyed as a result of shutdown. | |
263 registrar_.RemoveAll(); | |
264 } | |
265 | |
266 SavedFilesService::SavedFiles* SavedFilesService::Get( | |
267 const std::string& extension_id) const { | |
268 auto it = extension_id_to_saved_files_.find(extension_id); | |
269 if (it != extension_id_to_saved_files_.end()) | |
270 return it->second.get(); | |
271 | |
272 return NULL; | |
273 } | |
274 | |
275 SavedFilesService::SavedFiles* SavedFilesService::GetOrInsert( | |
276 const std::string& extension_id) { | |
277 SavedFiles* saved_files = Get(extension_id); | |
278 if (saved_files) | |
279 return saved_files; | |
280 | |
281 std::unique_ptr<SavedFiles> scoped_saved_files( | |
282 new SavedFiles(context_, extension_id)); | |
283 saved_files = scoped_saved_files.get(); | |
284 extension_id_to_saved_files_.insert( | |
285 std::make_pair(extension_id, std::move(scoped_saved_files))); | |
286 return saved_files; | |
287 } | |
288 | |
289 void SavedFilesService::Clear(const std::string& extension_id) { | |
290 extension_id_to_saved_files_.erase(extension_id); | |
291 } | |
292 | |
293 SavedFilesService::SavedFiles::SavedFiles(content::BrowserContext* context, | |
294 const std::string& extension_id) | |
295 : context_(context), extension_id_(extension_id) { | |
296 LoadSavedFileEntriesFromPreferences(); | |
297 } | |
298 | |
299 SavedFilesService::SavedFiles::~SavedFiles() {} | |
300 | |
301 void SavedFilesService::SavedFiles::RegisterFileEntry( | |
302 const std::string& id, | |
303 const base::FilePath& file_path, | |
304 bool is_directory) { | |
305 auto it = registered_file_entries_.find(id); | |
306 if (it != registered_file_entries_.end()) | |
307 return; | |
308 | |
309 registered_file_entries_[id] = | |
310 base::MakeUnique<SavedFileEntry>(id, file_path, is_directory, 0); | |
311 } | |
312 | |
313 void SavedFilesService::SavedFiles::EnqueueFileEntry(const std::string& id) { | |
314 auto it = registered_file_entries_.find(id); | |
315 DCHECK(it != registered_file_entries_.end()); | |
316 | |
317 SavedFileEntry* file_entry = it->second.get(); | |
318 int old_sequence_number = file_entry->sequence_number; | |
319 if (!saved_file_lru_.empty()) { | |
320 // Get the sequence number after the last file entry in the LRU. | |
321 std::map<int, SavedFileEntry*>::reverse_iterator it = | |
322 saved_file_lru_.rbegin(); | |
323 if (it->second == file_entry) | |
324 return; | |
325 | |
326 file_entry->sequence_number = it->first + 1; | |
327 } else { | |
328 // The first sequence number is 1, as 0 means the entry is not in the LRU. | |
329 file_entry->sequence_number = 1; | |
330 } | |
331 saved_file_lru_.insert( | |
332 std::make_pair(file_entry->sequence_number, file_entry)); | |
333 ExtensionPrefs* prefs = ExtensionPrefs::Get(context_); | |
334 if (old_sequence_number) { | |
335 saved_file_lru_.erase(old_sequence_number); | |
336 UpdateSavedFileEntry(prefs, extension_id_, *file_entry); | |
337 } else { | |
338 AddSavedFileEntry(prefs, extension_id_, *file_entry); | |
339 if (saved_file_lru_.size() > g_max_saved_file_entries) { | |
340 std::map<int, SavedFileEntry*>::iterator it = saved_file_lru_.begin(); | |
341 it->second->sequence_number = 0; | |
342 RemoveSavedFileEntry(prefs, extension_id_, it->second->id); | |
343 saved_file_lru_.erase(it); | |
344 } | |
345 } | |
346 MaybeCompactSequenceNumbers(); | |
347 } | |
348 | |
349 bool SavedFilesService::SavedFiles::IsRegistered(const std::string& id) const { | |
350 auto it = registered_file_entries_.find(id); | |
351 return it != registered_file_entries_.end(); | |
352 } | |
353 | |
354 const SavedFileEntry* SavedFilesService::SavedFiles::GetFileEntry( | |
355 const std::string& id) const { | |
356 auto it = registered_file_entries_.find(id); | |
357 if (it == registered_file_entries_.end()) | |
358 return NULL; | |
359 | |
360 return it->second.get(); | |
361 } | |
362 | |
363 std::vector<SavedFileEntry> SavedFilesService::SavedFiles::GetAllFileEntries() | |
364 const { | |
365 std::vector<SavedFileEntry> result; | |
366 for (auto it = registered_file_entries_.begin(); | |
367 it != registered_file_entries_.end(); ++it) { | |
368 result.push_back(*it->second.get()); | |
369 } | |
370 return result; | |
371 } | |
372 | |
373 void SavedFilesService::SavedFiles::MaybeCompactSequenceNumbers() { | |
374 DCHECK_GE(g_max_sequence_number, 0); | |
375 DCHECK_GE(static_cast<size_t>(g_max_sequence_number), | |
376 g_max_saved_file_entries); | |
377 std::map<int, SavedFileEntry*>::reverse_iterator it = | |
378 saved_file_lru_.rbegin(); | |
379 if (it == saved_file_lru_.rend()) | |
380 return; | |
381 | |
382 // Only compact sequence numbers if the last entry's sequence number is the | |
383 // maximum value. This should almost never be the case. | |
384 if (it->first < g_max_sequence_number) | |
385 return; | |
386 | |
387 int sequence_number = 0; | |
388 ExtensionPrefs* prefs = ExtensionPrefs::Get(context_); | |
389 for (std::map<int, SavedFileEntry*>::iterator it = saved_file_lru_.begin(); | |
390 it != saved_file_lru_.end(); | |
391 ++it) { | |
392 sequence_number++; | |
393 if (it->second->sequence_number == sequence_number) | |
394 continue; | |
395 | |
396 SavedFileEntry* file_entry = it->second; | |
397 file_entry->sequence_number = sequence_number; | |
398 UpdateSavedFileEntry(prefs, extension_id_, *file_entry); | |
399 saved_file_lru_.erase(it++); | |
400 // Provide the following element as an insert hint. While optimized | |
401 // insertion time with the following element as a hint is only supported by | |
402 // the spec in C++11, the implementations do support this. | |
403 it = saved_file_lru_.insert( | |
404 it, std::make_pair(file_entry->sequence_number, file_entry)); | |
405 } | |
406 } | |
407 | |
408 void SavedFilesService::SavedFiles::LoadSavedFileEntriesFromPreferences() { | |
409 ExtensionPrefs* prefs = ExtensionPrefs::Get(context_); | |
410 std::vector<SavedFileEntry> saved_entries = | |
411 GetSavedFileEntries(prefs, extension_id_); | |
412 for (std::vector<SavedFileEntry>::iterator it = saved_entries.begin(); | |
413 it != saved_entries.end(); | |
414 ++it) { | |
415 std::unique_ptr<SavedFileEntry> file_entry(new SavedFileEntry(*it)); | |
416 const std::string& id = file_entry->id; | |
417 saved_file_lru_.insert( | |
418 std::make_pair(file_entry->sequence_number, file_entry.get())); | |
419 registered_file_entries_[id] = std::move(file_entry); | |
420 } | |
421 } | |
422 | |
423 // static | |
424 void SavedFilesService::SetMaxSequenceNumberForTest(int max_value) { | |
425 g_max_sequence_number = max_value; | |
426 } | |
427 | |
428 // static | |
429 void SavedFilesService::ClearMaxSequenceNumberForTest() { | |
430 g_max_sequence_number = kMaxSequenceNumber; | |
431 } | |
432 | |
433 // static | |
434 void SavedFilesService::SetLruSizeForTest(int size) { | |
435 g_max_saved_file_entries = size; | |
436 } | |
437 | |
438 // static | |
439 void SavedFilesService::ClearLruSizeForTest() { | |
440 g_max_saved_file_entries = kMaxSavedFileEntries; | |
441 } | |
442 | |
443 } // namespace apps | |
OLD | NEW |